✔零知IDE 是一个真正属于国人自己的开源软件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!
✔访问零知实验室,获取更多实战项目和教程资源吧!
(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;
}
// 绘制静态UI
drawStaticUI();
}
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参考手册
本文所有代码均已在实际硬件平台测试通过,读者可根据需求自行调整参数!点击了解更多零知开发教程:

被折叠的 条评论
为什么被折叠?



