简介
在上一篇文章中,我们点亮了OLED屏幕, 那么本章节我们主要是驱动INA219. 对于INA的驱动方式也比较简单, 因为总共就只有六个寄存器。 由于这次的模块使用的是DFrobot的 Gravity: I2C Digital Wattmeter SKU: SEN0291。 所以可以参考他们官方的接线方式。
由于我们的电机负载是12V的, 所以可以将开关电源的输入接到 IN + , 然后将开关电源的 - 接到GND。 并且将负载(电机的+)接到IN- 。 然后将电机的- 接到GND(由于电机是一个直流电机, 所以正接反接都没有区别)都可以正常工作。
INA219驱动过程
在我驱动这个INA219的时候可谓是历经坎坷, 最终是确定了这个模块存在问题。我最初的时候是根据TI的手册自己编写的驱动。 但是一直读取不到VBS的电压 (即IN-, 到GND的电压。)于是我遍尝试根据论坛其他坛友的文章尝试使用他们的驱动库来进行INA219的驱动。 我尝试了很多个,也都是电流上下浮动很大, 而电压shunt voltage 和 bug voltage始终为0V上下波动。 在最开始的时候我并没有想到是模块可能存在问题, 我尝试改变模块的Address 来重新驱动。 但是还是不工作。
下面是我的驱动文件
#ifndef _INA219_HAL_H_ #define _INA219_HAL_H_ #include "stm32f4xx_hal.h" #include <stdbool.h> #include <stdint.h> #define INA219_I2C_ADDR 0x40 // 7-bit address (example) #define INA219_ADDRESS (INA219_I2C_ADDR << 1) // HAL 使用 8-bit 地址 // INA219 寄存器地址 #define INA219_REG_CONFIG 0x00 #define INA219_REG_SHUNT 0x01 #define INA219_REG_BUSVOLTAGE 0x02 #define INA219_REG_POWER 0x03 #define INA219_REG_CURRENT 0x04 #define INA219_REG_CALIBRATION 0x05 // 电气参数(请根据你硬件确认) #define INA_R 0.01f // Shunt 电阻,单位:欧姆 #define INA_I_MAX 3.2f // 最大电流(A) #define IAN_I_LSB 0.0001f // 每位电流(A) #define INA_Power_LSB (20 * IAN_I_LSB) // 每位功率(W) #define INA_CAL 40960 // Calibration值,来自公式计算 // HAL I2C句柄(在 main.c 定义的) extern I2C_HandleTypeDef hi2c1; // 驱动函数 void INA_Init(void); uint16_t INA_GET_Voltage_MV(void); int16_t INA_GET_Current_MA(void); uint16_t INA_GET_Power_MW(void); float INA_GET_Shunt_MV(void); float INA_GET_LoadVoltage_MV(void); #endif
源文件
#include "ina219_soft_i2c.h" #include <stdio.h> // 写寄存器 static void INA_REG_Write(uint8_t reg, uint16_t value) { uint8_t data[2]; data[0] = (value >> 8) & 0xFF; data[1] = value & 0xFF; if (HAL_I2C_Mem_Write(&hi2c1, INA219_ADDRESS, reg, I2C_MEMADD_SIZE_8BIT, data, 2, HAL_MAX_DELAY) != HAL_OK) { printf("INA219 Write Failed: Reg 0x%02X\r\n", reg); } } // 读寄存器 static uint16_t INA_REG_Read(uint8_t reg) { uint8_t data[2] = {0}; if (HAL_I2C_Mem_Read(&hi2c1, INA219_ADDRESS, reg, I2C_MEMADD_SIZE_8BIT, data, 2, HAL_MAX_DELAY) != HAL_OK) { printf("INA219 Read Failed: Reg 0x%02X\r\n", reg); return 0xFFFF; } return (data[0] << 8) | data[1]; } float INA_GET_Shunt_MV(void) { int16_t raw = (int16_t)INA_REG_Read(INA219_REG_SHUNT); if (raw == (int16_t)0xFFFF) return 0; return raw * 0.01f; // 每位是10μV = 0.01mV } // 获取负载两端电压,单位 mV float INA_GET_LoadVoltage_MV(void) { float bus_mv = INA_GET_Voltage_MV(); // 总线电压(mV) float shunt_mv = INA_GET_Shunt_MV(); // 分流电压(mV) return bus_mv - shunt_mv; // 负载两端的实际电压 } // 初始化 void INA_Init(void) { uint16_t config = 0x019F; // BRNG=1 (32V), PG=11 (±320mV), BADC/SADC=1001 (12-bit), MODE=111 INA_REG_Write(INA219_REG_CONFIG, config); INA_REG_Write(INA219_REG_CALIBRATION, INA_CAL); printf("INA219 Init done.\r\n"); } #define INA219_CNVR_MASK 0x0002 uint16_t INA_GET_Voltage_MV(void) { uint16_t raw = INA_REG_Read(INA219_REG_BUSVOLTAGE); if (raw == 0xFFFF) return 0; raw = (raw >> 3) & 0x1FFF; // 移除3个状态位 return raw * 4; // 每位4mV } // 获取电流(单位:mA) int16_t INA_GET_Current_MA(void) { // 读取 current 前应重新写校准值 INA_REG_Write(INA219_REG_CALIBRATION, INA_CAL); int16_t raw = (int16_t)INA_REG_Read(INA219_REG_CURRENT); if (raw == (int16_t)0xFFFF) return 0; return (int16_t)(raw * IAN_I_LSB * 1000.0f); // 转换为 mA } // 获取功率(单位:mW) uint16_t INA_GET_Power_MW(void) { uint16_t raw = INA_REG_Read(INA219_REG_POWER); if (raw == 0xFFFF) return 0; return (uint16_t)(raw * INA_Power_LSB * 1000.0f); // 转换为 mW }
在主程序中的打印和调用。
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C1_Init(); MX_USART2_UART_Init(); ssd1306_Init(&hi2c1); ssd1306_SetCursor(0, 0); ssd1306_WriteString("RY is stupid!", Font_7x10, White); ssd1306_SetCursor(0, 20); ssd1306_WriteString("You even can not!", Font_7x10, White); ssd1306_SetCursor(0, 40); ssd1306_WriteString("Turn on LED", Font_7x10, White); ssd1306_UpdateScreen(&hi2c1); for (uint8_t addr = 1; addr < 127; addr++) { if (HAL_I2C_IsDeviceReady(&hi2c1, addr << 1, 3, 10) == HAL_OK) { printf("I2C device found at 0x%02X\r\n", addr); } } INA_Init(); while (1) { float bus_mv = INA_GET_Voltage_MV(); // 总线电压(mV) float shunt_mv = INA_GET_Shunt_MV(); // 分流电压(mV) int16_t current = INA_GET_Current_MA(); // 电流(mA) uint16_t power = INA_GET_Power_MW(); // 功率(mW) float load_mv = INA_GET_LoadVoltage_MV(); // 负载电压(mV) // 打印结果 printf("Bus Voltage : %.2f V\r\n", bus_mv / 1000.0f); printf("Shunt Voltage : %.2f mV\r\n", shunt_mv); printf("Load Voltage : %.2f V\r\n", load_mv / 1000.0f); printf("Current : %d mA\r\n", current); printf("Power : %d mW\r\n", power); printf("-------------------------------\r\n"); HAL_Delay(1000); } }
实物图接线
读取到的数据(空载)
带负载
可以从上图中看到电流的数据还算正常和我用下图万用表测量的基本保持一致。
问题排查过程
由于更换过了很多的驱动库, 都无法正常的读取到电压的数据, 于是我怀疑是不是模块出现了问题, 我便使用了一个ESP32-S3按照DF-robot的代码来进行INA219模块的测试。
代码如下所示:
/*! file getVoltageCurrentPower.ino SEN0291 Gravity: I2C Digital Wattmeter The module is connected in series between the power supply and the load to read the voltage, current and power The module has four I2C, these addresses are: INA219_I2C_ADDRESS1 0x40 A0 = 0 A1 = 0 INA219_I2C_ADDRESS2 0x41 A0 = 1 A1 = 0 INA219_I2C_ADDRESS3 0x44 A0 = 0 A1 = 1 INA219_I2C_ADDRESS4 0x45 A0 = 1 A1 = 1 Copyright [DFRobot](https://www.dfrobot.com), 2016 Copyright GNU Lesser General Public License version V0.1 date 2019-2-27 */ #include <Wire.h> #include "DFRobot_INA219.h" DFRobot_INA219_IIC ina219(&Wire, INA219_I2C_ADDRESS1); // Revise the following two paramters according to actula reading of the INA219 and the multimeter // for linearly calibration float ina219Reading_mA = 1000; float extMeterReading_mA = 1000; void setup(void) { Serial.begin(115200); while(!Serial); Serial.println(); Wire.begin(5,4); while(ina219.begin() != true) { Serial.println("INA219 begin faild"); delay(2000); } ina219.linearCalibrate(ina219Reading_mA, extMeterReading_mA); Serial.println(); } void loop(void) { Serial.print("BusVoltage: "); Serial.print(ina219.getBusVoltage_V(), 2); Serial.println("V"); Serial.print("ShuntVoltage: "); Serial.print(ina219.getShuntVoltage_mV(), 3); Serial.println("mV"); Serial.print("Current: "); Serial.print(ina219.getCurrent_mA(), 1); Serial.println("mA"); Serial.print("Power: "); Serial.print(ina219.getPower_mW(), 1); Serial.println("mW"); Serial.println(""); delay(1000); }
效果如下:
至此是排除了代码问题。
然后尝试一下使用ESP32的电源空载INA219(不适用外置电源), 查看输出。
可以看到模块的数据输出是不正常的。当连接ESP32的5V到INA219的 IN + 的时候, 输出如下所示。
如下是STM32的工程文件,希望完成任务的坛友有空了帮我测试一下。代码是否能正常在你们的模块上工作。 万分感激。