零知开源——基于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主控制器
电流传感器INA2191电流/功率测量
显示屏ST7789 TFT (240x320)1数据显示
连接线杜邦线若干硬件连接

1.2 接线方案

零知增强板(STM32F407VET6)INA219 (I2C)ST7789(SPI)引脚功能说明
3.3V/VCC3.3V电源
5VVCC/5V电源
GNDGNDGND接地
SCLSCL/21/I2C时钟
SDASDA/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.25V3.20V-0.05V
电流2.66mA2.73mA+0.07mA
功率8.645mW8.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。

电流公式:I_{current}=\frac{V_{shunt}}{R_{shunt}}

4.2 关键参数计算

(1)校准值计算 :Cal = \frac{0.04096}{Current_{LSB} \times R_{SHUNT}}       

  • 其中 Current_{LSB}为最小分辨率

(2)电流分辨率选择:Current{_{LSB}} = \tfrac{Expected\left ( I_{Max} \right ) }{32767}

  • 对于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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值