《Let'sdo第1期-DIY功率检测与控制系统》
目标:基础任务:INA219功率信息检测(不带负载)
Gravity I2C数字功率计
Gravity:I2C数字功率计是一款可测量26V 8A以内各类电子模块、用电设备的电压、电流和功率,最大相对误差不超过±0.2%的高分辨、高精度、大量程测量模块(使用前需进行一次简单的手动校准)。可用于太阳能系统、电池库仑计、电机、主控板或电子模块的功耗测量、电池续航评估与实时电源参数在线监控。 模块采用TI INA219零温漂电流/功率监控芯片和2W大功率低温漂10mΩ合金采样电阻,电压和电流分辨率分别可达4mV与1mA,在满量程测量条件下,电压与电流的最大测量相对误差不超过±0.2%,并提供4个可通过拨码开关配置的I2C地址。模块可对双向高侧电流(流经电源或电池正极的电流)进行准确测量,这在太阳能或库仑计应用,电池既需要充电,也需要放电的场合尤为有用,用户可通过电流的正负读数了解电池的充放电状态,也可以了解电池的冲放电的实时电压、电流与功率。在电机应用场景,可通过实时监控电机电流是否由于堵转或负载过大导致电流过大,从而及时采取保护措施。此外,也可以使用该模块测量各类电子模块或整个项目的实时功耗,从而评估电池的续航时间。
特性
高精度、高分辨率、大量程、低温漂双向电流高侧测量兼容3.3V/5V控制器精致小巧,方便项目嵌入
技术规格
供电电压(VCC):3.3V ~ 5.5V
电压量程(IN+或IN-相对GND):0 ~ 26V
电压分辨率:4 mV
电压相对误差:<±0.2%
(典型值)电流量程:0 ~ ±8A
(可测双向电流)电流分辨率:1 mA
电流相对误差:<±0.2%(典型值,需手动校准)
功率量程:0 ~ 206 W功率分辨率:20 mW(硬件)/4mW(软件)
静态电流:0.7 mA
通信接口:Gravity I2C (逻辑电平0-3.3V)
I2C地址:4个,0x40,0x41,0x44,0x45(默认)
尺寸:30.0mm×22.0mm重量:4g
接口说明
1 | VCC | 电源正极(3.3~5.5V) |
2 | GND | 电源负极 |
3 | SCL | I2C时钟线 |
4 | SDA | I2C数据线 |
5 | ADDR | I2C地址选择拨码开关 |
6 | 3P TERMINAL | 电压与电流测量接线柱3P |
连线图
1、I2C3配置
__HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**I2C3 GPIO Configuration PC9 ------> I2C3_SDA PA8 ------> I2C3_SCL */ GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C3; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C3; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* Peripheral clock enable */ __HAL_RCC_I2C3_CLK_ENABLE();
static void MX_I2C3_Init(void) { /* USER CODE BEGIN I2C3_Init 0 */ /* USER CODE END I2C3_Init 0 */ /* USER CODE BEGIN I2C3_Init 1 */ /* USER CODE END I2C3_Init 1 */ hi2c3.Instance = I2C3; hi2c3.Init.ClockSpeed = 100000; hi2c3.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c3.Init.OwnAddress1 = 0; hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c3.Init.OwnAddress2 = 0; hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c3) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C3_Init 2 */ /* USER CODE END I2C3_Init 2 */ }
2、INA219驱动
2个拨码开关头调到0,设置地址为:0x40
#define INA219_ADDRESS (0x40 << 1) // 默认地址,左移1位因为STM32 HAL库需要 // INA219寄存器地址 #define INA219_REG_CONFIG 0x00 #define INA219_REG_SHUNTVOLT 0x01 #define INA219_REG_BUSVOLT 0x02 #define INA219_REG_POWER 0x03 #define INA219_REG_CURRENT 0x04 #define INA219_REG_CALIB 0x05
void INA219_Config(I2C_HandleTypeDef *hi2c) { uint8_t config[3]; hi2c_ina219=hi2c; // 配置值 (32V, 1A量程,12位分辨率) uint16_t configValue = 0x3c1f; // config[0] = INA219_REG_CONFIG; config[1] = (configValue >> 8); // 高字节 config[2] = configValue & 0xFF; // 低字节 HAL_I2C_Master_Transmit(hi2c_ina219, INA219_ADDRESS, config, 3, HAL_MAX_DELAY); // 校准值计算 (根据具体应用调整) uint16_t calValue = 4096; // 示例值,需要根据实际需求计算 config[0] = INA219_REG_CALIB; config[1] = (calValue >> 8); config[2] = calValue & 0xFF; HAL_I2C_Master_Transmit(hi2c_ina219, INA219_ADDRESS, config, 3, HAL_MAX_DELAY); }
3、读取电压值
float INA219_ReadBusVoltage(void) { uint8_t reg = INA219_REG_BUSVOLT; uint8_t data[2]; uint16_t voltageRaw; float voltage; // 写入要读取的寄存器地址 HAL_I2C_Master_Transmit(hi2c_ina219, INA219_ADDRESS, ®, 1, HAL_MAX_DELAY); // 读取2字节数据 HAL_I2C_Master_Receive(hi2c_ina219, INA219_ADDRESS, data, 2, HAL_MAX_DELAY); voltageRaw = (data[0] << 8) | data[1]; voltageRaw = (voltageRaw >> 3) * 4; // 转换为mV voltage = voltageRaw * 0.001f; // 转换为V return voltage; }
4、读物电流值
float INA219_ReadCurrent(void) { uint8_t reg = INA219_REG_CURRENT; uint8_t data[2]; int16_t currentRaw; float current; HAL_I2C_Master_Transmit(hi2c_ina219, INA219_ADDRESS, ®, 1, HAL_MAX_DELAY); HAL_I2C_Master_Receive(hi2c_ina219, INA219_ADDRESS, data, 2, HAL_MAX_DELAY); currentRaw = (data[0] << 8) | data[1]; current = currentRaw * 0.001f; // 1mA/LSB (取决于校准值) return current; }
5、运行效果
OLED_Clear(); OLED_ShowChineseString16(0,0,(uint8_t*)"DIY功率监测与控制",1); OLED_Refresh(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { float busVoltage = INA219_ReadBusVoltage(); float shuntVoltage = INA219_ReadShuntVoltage(); float current = INA219_ReadCurrent(); float power = busVoltage * current; printf("Bus Voltage: %.2f V\n", busVoltage); printf("Shunt Voltage: %.2f mV\n", shuntVoltage * 1000); printf("Current: %.2f mA\n", current * 1000); printf("Power: %.2f mW\n", power * 1000); sprintf(buff,"电流: %6.3fmA ",current*1000); OLED_ShowChineseString16(0,16,(uint8_t*)buff,1); sprintf(buff,"电压: %6.3fV ",busVoltage); OLED_ShowChineseString16(0,32,(uint8_t*)buff,1); sprintf(buff,"功率: %6.3fmW ",power*1000); OLED_ShowChineseString16(0,48,(uint8_t*)buff,1); //第一排title循环显示效果 OLED_ShiftTwoRowsLeft(0, 3); HAL_Delay(250); OLED_ShiftTwoRowsLeft(0, 3); HAL_Delay(250); OLED_ShiftTwoRowsLeft(0, 3); HAL_Delay(250); OLED_ShiftTwoRowsLeft(0, 3); HAL_Delay(250); OLED_Refresh(); HAL_GPIO_TogglePin(LD2_GPIO_Port,LD2_Pin); }
第一排title循环显示效果:
/** * @brief 指定2行(16像素高)内容循环左移 * @param start_page: 起始页(0-6),每页8行,2页=16行 * @param shift_pixels: 左移的像素数(1-7) */ void OLED_ShiftTwoRowsLeft(uint8_t start_page, uint8_t shift_pixels) { if(start_page > 6) return; // 最大从第6页开始(6+1=7<8) uint8_t i,n; uint8_t temp[144][2]; for (i=0;i<shift_pixels;i++) { temp[i][0]=OLED_GRAM[i][start_page]; temp[i][1]=OLED_GRAM[i][start_page+1]; } for(i=shift_pixels;i<144;i++) //实现左移 { for(n=start_page;n<start_page+2;n++) { OLED_GRAM[i-shift_pixels][n]=OLED_GRAM[i][n]; } } for(i=0;i<shift_pixels;i++) { OLED_GRAM[144-(shift_pixels-i)][start_page]=temp[i][0]; OLED_GRAM[144-(shift_pixels-i)][start_page+1]=temp[i][1]; } }