《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];
}
}

我要赚赏金
