零知开源——基于STM32F407VET6和INA219的功率监测器设计与实现
✔零知IDE 是一个真正属于国人自己的开源软件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!
✔访问零知实验室,获取更多实战项目和教程资源吧!
www.lingzhilab.com
(1)项目概述
本教程详细介绍了如何使用零知增强板(STM32F407VET6主控)和INA219电流/功率传感器构建一个高精度LED灯功率监测系统。项目包含实时波形显示、数据滤波处理、串口数据输出等功能,并通过ST7789显示屏直观展示测量结果。文章将从硬件选型、软件实现到精度优化进行全面讲解。
(2)项目亮点
>通过配置INA219为16V/400mA高精度模式,实现±0.1mA电流精度
>采用多通道波形显示技术,支持自动缩放
>滑动平均滤波算法有效降低噪声干扰
>优化刷新策略,降低系统功耗
>精心设计的UI布局,信息展示直观清晰
(3)项目难点及解决方案
问题描述:小电流测量精度不足,在测量mA级电流时,传感器读数存在偏差(约0.15mA)且波动较大
解决方案:
>配置INA219为高精度模式(16V量程,400mA量程)
>启用128次采样平均,显著降低噪声
>在软件中增加零点校准功能(在无负载时自动计算偏移量)
>添加滑动平均滤波算法
一、硬件系统设计
1.1 硬件清单
组件 | 型号 | 数量 | 备注 |
---|---|---|---|
主控板 | 零知增强板(STM32F407VET6) | 1 | 主控制器 |
电流传感器 | INA219 | 1 | 电流/功率测量 |
显示屏 | ST7789 TFT (240x320) | 1 | 数据显示 |
连接线 | 杜邦线 | 若干 | 硬件连接 |
1.2 接线方案
零知增强板(STM32F407VET6) | INA219 (I2C) | ST7789(SPI) | 引脚功能说明 |
---|---|---|---|
3.3V | / | VCC | 3.3V电源 |
5V | VCC | / | 5V电源 |
GND | GND | GND | 接地 |
SCL | SCL/21 | / | I2C时钟 |
SDA | SDA/20 | / | I2C数据 |
53 | / | CS | 片选 |
2 | / | DC | 数据/命令选择 |
51 | / | SDA | 主出从入 |
52 | / | SCL | 时钟 |
4 | / | RES | 复位 |
1.3 连接硬件图
1.4 实物连接图
二、软件架构设计
2.1 库文件导入
#include <Wire.h> //与INA219进行I2C协议通信
#include <Adafruit_INA219.h> //电流、电压传感器库文件
#include <Adafruit_GFX.h> //核心图形库文件
#include <Adafruit_ST7789.h> //显示屏驱动文件
#include <SPI.h> //显示屏SPI通信协议库文件
2.2 初始化设置
void setup(void) {Serial.begin(9600);// 显示屏初始化tft.init(240, 320);tft.invertDisplay(true);tft.setRotation(1); // 横屏模式// 显示开机画面showSplashScreen();delay(1500);// 传感器初始化if (!sensor219.begin()) {Serial.println("Failed to find INA219 chip");while (1);}// 滤波数组初始化for (int i = 0; i < numReadings; i++) {currentReadings[i] = 0;}// 历史数据初始化for (int i = 0; i < HISTORY_SIZE; i++) {voltageHistory[i] = 0;currentHistory[i] = 0;powerHistory[i] = 0;}// 绘制静态UIdrawStaticUI();
}
2.3 主循环处理
void loop(void) {float busVoltage = sensor219.getBusVoltage_V();float current = sensor219.getCurrent_mA() - 0.05;float power = busVoltage * (current/1000);// 电流滤波处理currentTotal -= currentReadings[readIndex];currentReadings[readIndex] = current;currentTotal += currentReadings[readIndex];readIndex = (readIndex + 1) % numReadings;currentAvg = currentTotal / numReadings;// 更新历史数据voltageHistory[historyIndex] = busVoltage;currentHistory[historyIndex] = currentAvg;powerHistory[historyIndex] = power * 1000; // 转换为mW// 更新显示屏updateUI(busVoltage, currentAvg, power * 1000);// 移动历史索引historyIndex = (historyIndex + 1) % HISTORY_SIZE;// 串口打印数据Serial.print("Bus Voltage: "); Serial.print(busVoltage, 3); Serial.println(" V"); Serial.print("Current: "); Serial.print(currentAvg, 3); Serial.println(" mA");Serial.print("Power: "); Serial.print(power * 1000,3); Serial.println(" mW");Serial.println("------------------------");delay(1000); // 1000ms刷新率
}
>获取电压、电流值并计算得到功率值
>通过10点滑动滤波处理电流数据
>数据传输到ST7789显示屏波形展示/串口打印输出
>每秒刷新一次
2.4 波形绘制实现
void updateUI(float voltage, float current, float power) {// 更新右侧面板数值updatePanelValues(voltage, current, power);// 清除图表区域 (保留坐标轴和边框)tft.fillRect(GRAPH_X + 1, GRAPH_Y + 1, GRAPH_WIDTH - 1, GRAPH_HEIGHT - 1, BACKGROUND);// 重绘坐标轴(因为部分可能被覆盖)drawAxes();// 查找最大值用于缩放float maxVal = 0.1;for (int i = 0; i < HISTORY_SIZE; i++) {if (voltageHistory[i] > maxVal) maxVal = voltageHistory[i];if (currentHistory[i] > maxVal) maxVal = currentHistory[i];if (powerHistory[i] > maxVal) maxVal = powerHistory[i];}// 添加15%的余量maxVal *= 1.15;// 绘制新的历史曲线 - 使用稀疏点绘制for (int i = 1; i < HISTORY_SIZE; i++) {int prevIndex = (historyIndex + i - 1) % HISTORY_SIZE;int currIndex = (historyIndex + i) % HISTORY_SIZE;// 计算X位置 - 每2像素一个点int x1 = GRAPH_X + (i-1) * 2;int x2 = GRAPH_X + i * 2;// 如果超出图表范围则停止绘制if (x2 > GRAPH_X + GRAPH_WIDTH) break;// 电压曲线int y1_voltage = GRAPH_Y + GRAPH_HEIGHT - constrain(voltageHistory[prevIndex] / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);int y2_voltage = GRAPH_Y + GRAPH_HEIGHT - constrain(voltageHistory[currIndex] / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);tft.drawLine(x1, y1_voltage, x2, y2_voltage, VOLTAGE_COLOR);// 电流曲线int y1_current = GRAPH_Y + GRAPH_HEIGHT - constrain(currentHistory[prevIndex] / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);int y2_current = GRAPH_Y + GRAPH_HEIGHT - constrain(currentHistory[currIndex] / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);tft.drawLine(x1, y1_current, x2, y2_current, CURRENT_COLOR);// 功率曲线int y1_power = GRAPH_Y + GRAPH_HEIGHT - constrain(powerHistory[prevIndex] / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);int y2_power = GRAPH_Y + GRAPH_HEIGHT - constrain(powerHistory[currIndex] / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);tft.drawLine(x1, y1_power, x2, y2_power, POWER_COLOR);}// 绘制Y轴刻度tft.setTextColor(0xAD75); // 浅灰色tft.setTextSize(1);// 最大值tft.setCursor(GRAPH_X + 2,GRAPH_Y + 5);tft.print(maxVal, 1);// 中间值tft.setCursor(GRAPH_X + 2,GRAPH_Y + GRAPH_HEIGHT/2 + 5);tft.print(maxVal/2, 1);// 最小值tft.setCursor(GRAPH_X - 5, GRAPH_Y + GRAPH_HEIGHT + 5);tft.print("0");
}void updatePanelValues(float voltage, float current, float power) {// 设置文本属性tft.setTextColor(TEXT_COLOR);tft.setTextSize(1);// 电压值 (只刷新数值部分)tft.fillRect(PANEL_X + 25, PANEL_Y + 65, 30, 12, PANEL_COLOR);tft.setCursor(PANEL_X + 25, PANEL_Y + 70);tft.print(voltage, 2);// 电流值tft.fillRect(PANEL_X + 25, PANEL_Y + 125, 30, 12, PANEL_COLOR);tft.setCursor(PANEL_X + 25, PANEL_Y + 130);tft.print(current, 2);// 功率值tft.fillRect(PANEL_X + 25, PANEL_Y + 185, 30, 12, PANEL_COLOR);tft.setCursor(PANEL_X + 25, PANEL_Y + 190);tft.print(power, 2);
}
清除图表区域并将新的数据更新到右侧面板,绘制蓝色电压曲线、紫色电流曲线和红色功率曲线实时显示三通道波形,同时动态计算Y轴刻度
2.5 INA219库文件解析
(1)高精度模式配置
void Adafruit_INA219::init() {// 使用16V/400mA高精度模式setCalibration_16V_400mA();// 增强配置:启用128次采样平均Adafruit_BusIO_Register config_reg =Adafruit_BusIO_Register(i2c_dev, INA219_REG_CONFIG, 2, MSBFIRST);uint16_t config = INA219_CONFIG_BVOLTAGERANGE_16V |INA219_CONFIG_GAIN_1_40MV | INA219_CONFIG_BADCRES_12BIT |INA219_CONFIG_SADCRES_12BIT_128S_69MS | // 128次采样平均INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS;config_reg.write(config, 2);
}
(2)16V/400mA校准模式详解
void Adafruit_INA219::setCalibration_16V_400mA() {// 配置参数:// 总线电压范围:16V// 增益:1(量程±40mV)// 分流电阻:0.1Ωina219_calValue = 8192; // 校准寄存器值// 电流计算参数ina219_currentDivider_mA = 20; // 电流LSB = 50μA// 功率计算参数ina219_powerMultiplier_mW = 1.0f; // 功率LSB = 1mW// 写入校准寄存器Adafruit_BusIO_Register calibration_reg =Adafruit_BusIO_Register(i2c_dev, INA219_REG_CALIBRATION, 2, MSBFIRST);calibration_reg.write(ina219_calValue, 2);// 配置寄存器设置uint16_t config = INA219_CONFIG_BVOLTAGERANGE_16V |INA219_CONFIG_GAIN_1_40MV | INA219_CONFIG_BADCRES_12BIT |INA219_CONFIG_SADCRES_12BIT_1S_532US |INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS;Adafruit_BusIO_Register config_reg =Adafruit_BusIO_Register(i2c_dev, INA219_REG_CONFIG, 2, MSBFIRST);config_reg.write(config, 2);
}
三、功能展示
3.1 显示屏界面
界面包含三个主要区域:
波形显示区:左侧区域显示电压(蓝)、电流(紫)、功率(红)的实时波形
数据面板:右侧显示实时测量值
图例区:底部显示各波形对应的参数
3.2 万用表对比测试
>将万用表分别打到量程为20V的电压档以及20mA的电流档
参数 | 万用表测量值 | 系统测量值 | 误差 |
---|---|---|---|
电压 | 3.25V | 3.20V | -0.05V |
电流 | 2.66mA | 2.73mA | +0.07mA |
功率 | 8.645mW | 8.984mW | +0.339mW |
注:万用表测量的功率值为两次万用表测量的电压和电流值乘积、系统测量的功率值为传感器获取到10次功率平均值。电压值和电流值误差在控制范围内
3.3 视频演示
(1)电压值获取
INA219获取电压值数据对比
传感器读取到的电压值3.22V,万用表获取到的电压值为3.27V
(2)电流值获取
INA219获取电流值数据对比
传感器读取到的电流值为2.74mA,万用表获取到的电流值为2.66mA
3.4 串口数据输出
串口监视器输出电压、电流和功率数值和波形曲线
四、INA219检测技术解析
4.1 基础原理
INA219检测电流的原理是计算电流经过采样电阻两端的压差,进行计算得到,整个过程由芯片自动计算,因此只需要确定好采样电阻、设置好寄存器即可获取到电流值。
根据芯片手册,经过分流电阻N(采样电阻)后,能够采集到的最低有效电压LSB为10uV。
电流公式:
4.2 关键参数计算
(1)校准值计算 :
- 其中
为最小分辨率
(2)电流分辨率选择:
- 对于400mA量程:0.4A / 32767 ≈ 12.2μA
(3)实际配置:本项目采用0.1mA分辨率(平衡精度和量程)
4.3 精度影响因素
分流电阻精度:建议使用0.1%精度的电阻
ADC量化误差:12位ADC分辨率限制
温度漂移:约±0.005%/℃
噪声干扰:电源纹波和电磁干扰
可以采取的优化测量精度策略:使用高精度模式(降低量程)、增加采样平均次数、软件滤波处理、温度补偿
五、常见问题解答
Q1:为什么电流测量值总是有偏差?
A:可能原因及解决方案,
可能原因:
零点偏移未校准
分流电阻值不精确
温度影响
解决方案:
在无负载时进行零点校准
使用精密分流电阻(0.1Ω±0.1%)
添加软件补偿值
Q2:如何提高刷新率?
A:可采取的优化策略,
减少历史数据点数(如从100减至50)
降低采样平均次数(从128降至64)
减少滤波窗口大小
Q3:显示屏出现花屏或者空白怎么办?
A:排查步骤,
检查接线是否牢固(特别是RES、DC、CS引脚)
确认电源稳定(3.3V电压足够)
降低SPI时钟频率
检查屏幕初始化代码(旋转方向、分辨率)
六、结论
本项目成功实现了基于STM32F407VET6和INA219的高精度功率监测系统。通过精心设计的硬件配置和软件优化,系统达到了:
电流测量精度:±0.1mA
电压测量精度:±0.05V
功率计算精度:±1mW
项目资源:
1.传感器库文件:INA219驱动库
2.传感器数据手册:INA219数据手册
3.STM32F407参考手册:STM32F4参考手册
本文所有代码均已在实际硬件平台测试通过,读者可根据需求自行调整参数!点击了解更多零知开发教程:
https://www.lingzhilab.com/freesources.html