eefocus_4087784 发表于 2026-4-18 17:37:25

零知派——STM32驱动INA219电流功率监测计实现高精度电源管理



一款基于零知派标准板的高精度电流/电压/功率监测解决方案


✔零知派(零知开源)是一个专为电子初学者/电子兴趣爱好者设计的开源软硬件平台,在硬件上提供超高性价比STM32系列开发板、物联网控制板。取消了Bootloader程序烧录,让开发重心从 “配置环境” 转移到 “创意实现”,极大降低了技术门槛。零知开源编程软件,内置上千个覆盖多场景的示例代码,支持项目源码一键下载,项目文章在线浏览。零知派(零知开源)平台通过软硬件协同创新,让你的创意快速转化为实物,来动手试试吧!✔访问零知实验室,获取更多实战项目和教程资源吧!
www.lingzhilab.com

(1)项目概述
      本项目基于STM32F103RBT6微控制器和零知派INA219电流功率监测计,实现了一个高精度的电源监控系统。该系统能够实时监测电路中的总线电压、电流消耗和功率输出,并通过ST7789 TFT显示屏进行可视化展示。通过软件I2C(SoftWire)驱动INA219传感器,实现了稳定的数据采集和实时波形显示
(2)项目难点及解决方案
      问题描述:INA219 库默认使用硬件 I2C 通信,与零知标准板的 STM32F103RBT6 存在兼容性问题
解决方案:将INA219库中所有TwoWire类型替换为SoftWire类型;调整构造函数,使其接受SoftWire指针参数


一、硬件系统设计1.1 硬件清单
序号元件名称型号规格数量
1主控板零知派标准板(STM32F103RBT6)      1
2电流功率监测计             零知派INA2191
3      显示屏ST7789(240×320)1
5杜邦线公对公、公对母若干   
6USB 数据线Mini USB1
7LED 模块5mm 发光二极管1

1.2 接线方案表
元件引脚连接到 零知派标准板的引脚备注
INA219    SCLA5软件 I2C 时钟线
SDAA4软件 I2C 数据线
VCC3.3V传感器电源
GND       GND接地
Vin+被测电源正极/3.3V

Vin-负载正极通过 0.1Ω 电阻连接到 Vin+
ST7789直插直插零知派标准板TFT扩展引脚   硬件 SPI通信
    INA219电流功率监测计的Vin+接3.3V供电电源、Vin-接负载(本项目连接到LED模块IN引脚)


1.3 具体接线图   INA219与零知派标准板通过软件I2C进行通信;各器件接线、LED模块按照代码接线所示:



1.4 连接实物图


二、软件架构设计2.1 系统初始化void setup(void) {
Serial.begin(115200);          // 初始化串口通信
tft.init(240, 320);            // 初始化TFT显示屏
tft.invertDisplay(false);      // 设置显示方向
tft.setRotation(1);            // 旋转显示屏
tft.fillScreen(BACKGROUND);    // 清屏
showSplashScreen();            // 显示启动画面
delay(1500);

sw.begin();                  // 初始化软件I2C
sw.setClock(100000);         // 设置I2C时钟频率为100kHz

if (!INA.begin()) {            // 初始化INA219传感器
    Serial.println("Failed to find INA219 chip");
    while (1) { delay(10); }   // 初始化失败则死循环
}

// 校准传感器:最大电流0.5A,分流电阻0.1Ω
if (!INA.setMaxCurrentShunt(0.5, 0.1)) {
    Serial.println("Calibration failed!");
    while(1);                  // 校准失败则死循环
}

// 初始化历史数据缓冲区
for (int i = 0; i < HISTORY_SIZE; i++) {
    voltageHistory = 0;
    currentHistory = 0;
    powerHistory = 0;
}

drawStaticUI();                // 绘制静态UI界面
Serial.println("System initialized");
Serial.print("INA219_LIB_VERSION: ");
Serial.println(INA219_LIB_VERSION);
}初始化软件 I2C 并设置通信速率,进行INA219传感器校准;初始化历史数据缓冲区,用于存储绘图数据


2.2 数据采集与处理void loop(void) {
// 读取传感器数据
float busVoltage = INA.getBusVoltage();      // 读取总线电压(V)
float current = INA.getCurrent_mA() - 0.8;   // 读取电流(mA)并进行零点校准
float power = INA.getPower_mW();             // 读取功率(mW)

// 存储历史数据
voltageHistory = busVoltage;
currentHistory = current;
powerHistory = power;

// 更新UI显示
updateUI(busVoltage, current, power);

// 更新历史数据索引(循环缓冲区)
historyIndex = (historyIndex + 1) % HISTORY_SIZE;

// 串口输出数据
Serial.println("\n\tBUS\t\tSHUNT\t\tCURRENT\t\tPOWER\t\tOVF\t\tCNVR");
Serial.print("\t");
Serial.print(busVoltage, 2);
Serial.print("\t\t");
Serial.print(INA.getShuntVoltage_mV(), 2);
Serial.print("\t\t");
Serial.print(current, 2);
Serial.print("\t\t");
Serial.print(power, 2);
Serial.print("\t\t");
Serial.print(INA.getMathOverflowFlag());// 数学溢出标志
Serial.print("\t\t");
Serial.print(INA.getConversionFlag());    // 转换完成标志
Serial.println();

delay(1000);// 1秒刷新一次
}每秒从 INA219 读取总线电压、电流和功率;使用循环缓冲区将新数据存入历史缓冲区,实现数据的滚动存储


2.3 UI显示功能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 > maxVal) maxVal = voltageHistory;
    if (currentHistory > maxVal) maxVal = currentHistory;
    if (powerHistory > maxVal) maxVal = powerHistory;
}
maxVal *= 1.15;// 增加15%的余量

// 绘制历史曲线
for (int i = 1; i < HISTORY_SIZE; i++) {
    // 计算历史索引(循环缓冲区)
    int prevIndex = (historyIndex + i - 1) % HISTORY_SIZE;
    int currIndex = (historyIndex + i) % HISTORY_SIZE;
   
    // 计算X坐标
    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 / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);
    int y2_voltage = GRAPH_Y + GRAPH_HEIGHT - constrain(voltageHistory / 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 / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);
    int y2_current = GRAPH_Y + GRAPH_HEIGHT - constrain(currentHistory / 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 / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);
    int y2_power = GRAPH_Y + GRAPH_HEIGHT - constrain(powerHistory / 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");                      // 最小值
}采用自动缩放机制,根据历史数据的最大值动态调整 Y 轴范围;使用不同颜色绘制三条曲线,分别表示电压、电流和功率


项目代码
/**************************************************************************************
* 文件: /STM32F103_INA219_RealTime_PowerMonitor/STM32F103_INA219_RealTime_PowerMonitor.ino
* 作者:零知派(深圳市在芯间科技有限公司)
* -^^- 零知派,让电子制作变得更简单! -^^-
* 时间: 2026-4-16 15:30:45
* 说明: 基于零知派标准板(STM32F103RBT6主控)和零知派INA219电流功率监测计,通过软件I2C(SoftWire)实现传感器稳定通信。
*       实时采集总线电压、负载电流及功率数据,在ST7789 TFT屏可视化展示实时数值与历史趋势曲线,同步串口输出数据供调试分析。
***************************************************************************************/

#include "INA219.h"
#include <Adafruit_GFX.h>      // 图形显示基础库(适配ST7789)
#include <Adafruit_ST7789.h>   // ST7789 TFT屏硬件驱动库
#include <SoftWire.h>          // 软件I2C库

// -------------------------- 硬件引脚定义模块 --------------------------
/**
* ST7789 TFT屏硬件SPI引脚配置
* 硬件SPI固定引脚:SCK=13(时钟)、SDA(MOSI)=11(数据),以下为可配置引脚
*/
#define TFT_CS    10// TFT片选引脚(低电平有效)
#define TFT_DC    2   // TFT数据/命令选择引脚(高=数据,低=命令)
#define TFT_RST   4   // TFT复位引脚(低电平复位,可不接则设为-1)

// -------------------------- 全局参数配置模块 --------------------------
/**
* INA219传感器配置
* @param 0x44: INA219默认I2C地址0x40(可通过A0/A1引脚修改为0x41/0x44/0x45)
* @param &sw: 绑定软件I2C实例
*/
SoftWire sw(SCL, SDA, SOFT_STANDARD);   // 软件I2C引脚
INA219 INA(0x44, &sw);

/**
* ST7789 TFT屏配置
* 分辨率:240x320(宽x高),旋转后实际显示方向由setRotation(1)决定
*/
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
#define TFT_WIDTH240    // ST7789屏物理宽度(像素)
#define TFT_HEIGHT 320    // ST7789屏物理高度(像素)

/**
* 波形绘制历史数据配置
* HISTORY_SIZE:波形缓存长度(决定时间轴跨度),建议根据刷新频率调整
*/
const int HISTORY_SIZE = 100;    // 最大历史数据点数(100个采样点,对应100秒)
float voltageHistory;// 电压历史数据缓存
float currentHistory;// 电流历史数据缓存
float powerHistory;    // 功率历史数据缓存
int historyIndex = 0;               // 历史数据当前写入索引(循环覆盖)

/**
* 显示界面颜色配置(RGB565格式,5位红+6位绿+5位蓝)
*/
#define BACKGROUND    ST77XX_BLACK    // 屏幕背景色
#define VOLTAGE_COLOR ST77XX_YELLOW   // 电压波形/文本颜色
#define CURRENT_COLOR ST77XX_GREEN    // 电流波形/文本颜色
#define POWER_COLOR   ST77XX_CYAN   // 功率波形/文本颜色
#define TEXT_COLOR    ST77XX_WHITE    // 通用文本颜色
#define AXIS_COLOR    ST77XX_WHITE    // 坐标轴颜色
#define PANEL_COLOR   0x18A0          // 数值面板背景色(RGB565:深灰蓝)

/**
* 显示区域坐标配置(像素单位)
* 波形区:左上方主要区域;数值面板:右侧固定面板
*/
#define GRAPH_WIDTH   200       // 波形显示区域宽度
#define GRAPH_HEIGHT120       // 波形显示区域高度
#define GRAPH_X       10      // 波形区左上角X坐标
#define GRAPH_Y       60      // 波形区左上角Y坐标
#define PANEL_WIDTH   80      // 实时数值面板宽度
#define PANEL_X       240       // 数值面板左上角X坐标(补充:原代码240超出屏宽,建议改为150)
#define PANEL_Y       0         // 数值面板左上角Y坐标

// -------------------------- 初始化函数 --------------------------
/**
* @brief 系统初始化入口函数
* @details 完成串口、TFT屏、INA219传感器、数据缓存、静态UI的初始化
*/
void setup(void) {
// 1. 串口初始化(调试用,波特率115200)
Serial.begin(115200);

// 2. TFT屏初始化
tft.init(TFT_WIDTH, TFT_HEIGHT);       // 初始化屏显参数
tft.invertDisplay(false);            // 关闭显示反转(true为反色显示)
tft.setRotation(1);                  // 旋转屏幕(0-3,1为90度顺时针)
tft.fillScreen(BACKGROUND);            // 清屏(背景色)
showSplashScreen();                  // 显示启动画面
delay(1500);                           // 启动画面停留1.5秒

// 3. INA219传感器初始化
sw.begin();                            // 启动软件I2C
sw.setClock(100000);                   // 设置I2C时钟频率(100kHz,INA219最大支持400kHz)
if (!INA.begin()) {                  // 检测INA219是否连接
    Serial.println("Failed to find INA219 chip");
    while (1) { delay(10); }             // 硬件错误,死循环报错
}
// 校准INA219(最大预期电流0.5A,分流电阻0.1Ω)
if (!INA.setMaxCurrentShunt(0.5, 0.1)) {
    Serial.println("Calibration failed!");
    while(1);                            // 校准失败,死循环报错
}

// 4. 历史数据缓存初始化(清零)
for (int i = 0; i < HISTORY_SIZE; i++) {
    voltageHistory = 0;
    currentHistory = 0;
    powerHistory = 0;
}

// 5. 绘制静态UI框架(仅初始化时绘制一次)
drawStaticUI();

// 初始化完成提示
Serial.println("System initialized");
Serial.print("INA219_LIB_VERSION: ");
Serial.println(INA219_LIB_VERSION);
}

// -------------------------- 主循环函数 --------------------------
/**
* @brief 系统主循环
* @details 每秒采集一次数据,更新缓存、UI和串口输出,循环执行
*/
void loop(void) {
// 1. 读取INA219数据(核心传感器数据采集)
float busVoltage = INA.getBusVoltage();      // 读取总线电压(V)
float current = INA.getCurrent_mA() - 0.8;   // 读取电流(mA),减去0.8mA校准零点偏移
float power = INA.getPower_mW();               // 读取功率(mW)

// 2. 存储数据到历史缓存(循环覆盖)
voltageHistory = busVoltage;
currentHistory = current;
powerHistory = power;

// 3. 更新屏幕显示(实时数值+波形)
updateUI(busVoltage, current, power);

// 4. 更新缓存索引(循环复用缓存)
historyIndex = (historyIndex + 1) % HISTORY_SIZE;

// 5. 串口输出调试信息(格式化打印)
Serial.println("\n\tBUS(V)\t\tSHUNT(mV)\tCURRENT(mA)\tPOWER(mW)\tOVF\t\tCNVR");
Serial.print("\t");
Serial.print(busVoltage, 2);                  // 总线电压(保留2位小数)
Serial.print("\t\t");
Serial.print(INA.getShuntVoltage_mV(), 2);    // 分流电阻电压(mV)
Serial.print("\t\t");
Serial.print(current, 2);                     // 电流(mA)
Serial.print("\t\t");
Serial.print(power, 2);                     // 功率(mW)
Serial.print("\t\t");
Serial.print(INA.getMathOverflowFlag());      // 数学溢出标志(1=溢出,需重新校准)
Serial.print("\t\t");
Serial.print(INA.getConversionFlag());      // 转换完成标志(1=数据有效)
Serial.println();

// 6. 采样间隔(1秒,可根据需求调整,最小受INA219转换时间限制)
delay(1000);
}

// -------------------------- 屏幕显示功能模块 --------------------------
/**
* @brief 启动画面显示
* @details 系统初始化时显示品牌/功能提示,提升用户体验
*/
void showSplashScreen() {
tft.fillScreen(BACKGROUND);          // 清屏
tft.setTextColor(ST77XX_YELLOW);   // 设置文本颜色为**色
tft.setTextSize(3);                  // 文本大小(1=8x8像素,3=24x24像素)
tft.setCursor(70, 80);               // 设置文本光标位置(X,Y)
tft.print("POWER");                  // 打印"POWER"
tft.setCursor(85, 120);            // 调整光标位置
tft.print("MONITOR");                // 打印"MONITOR"

tft.setTextColor(ST77XX_CYAN);       // 切换文本颜色为青色
tft.setTextSize(1);                  // 缩小文本大小
tft.setCursor(90, 180);            // 调整光标位置
tft.print("Initializing...");      // 打印初始化提示
}

/**
* @brief 绘制静态UI框架
* @details 绘制仅需初始化一次的界面元素(标题、边框、坐标轴、面板、图例)
*/
void drawStaticUI() {
tft.fillScreen(BACKGROUND);          // 清屏

// 1. 绘制标题
tft.setTextColor(TEXT_COLOR);
tft.setTextSize(2);
tft.setCursor(50, 5);
tft.print("POWER MONITOR");

// 2. 绘制波形区边框
tft.drawRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT, AXIS_COLOR);
drawAxes();// 绘制坐标轴

// 3. 绘制实时数值面板(右侧)
tft.fillRect(PANEL_X, PANEL_Y, PANEL_WIDTH, 240, PANEL_COLOR);// 面板背景
tft.drawRect(PANEL_X, PANEL_Y, PANEL_WIDTH, 240, TEXT_COLOR);   // 面板边框
tft.setTextColor(TEXT_COLOR);
tft.setTextSize(1);
tft.setCursor(PANEL_X + 10, PANEL_Y + 10);
tft.print("REALTIME");
tft.setCursor(PANEL_X + 15, PANEL_Y + 20);
tft.print("VALUES");
tft.drawFastHLine(PANEL_X, PANEL_Y + 35, PANEL_WIDTH, TEXT_COLOR);// 分隔线

// 4. 绘制波形图例
drawLegend();

// 5. 绘制坐标轴标签
tft.setTextSize(1);
tft.setCursor(GRAPH_X - 5, GRAPH_Y - 15);
tft.print("Value");          // Y轴标签
tft.setCursor(GRAPH_X + GRAPH_WIDTH - 30, GRAPH_Y + GRAPH_HEIGHT + 5);
tft.print("Time (s)");       // X轴标签

// 6. 绘制数值面板静态文本(V/I/P标签)
drawPanelStaticText();
}

/**
* @brief 绘制波形区坐标轴
* @details 绘制X/Y轴、轴尖标记和水平网格线,提升波形可读性
*/
void drawAxes() {
// 绘制Y轴(垂直轴)
tft.drawFastVLine(GRAPH_X, GRAPH_Y, GRAPH_HEIGHT, AXIS_COLOR);
tft.drawLine(GRAPH_X, GRAPH_Y, GRAPH_X - 3, GRAPH_Y + 5, AXIS_COLOR);// Y轴上尖
tft.drawLine(GRAPH_X, GRAPH_Y, GRAPH_X + 3, GRAPH_Y + 5, AXIS_COLOR);

// 绘制X轴(水平轴)
tft.drawFastHLine(GRAPH_X, GRAPH_Y + GRAPH_HEIGHT, GRAPH_WIDTH, AXIS_COLOR);
tft.drawLine(GRAPH_X + GRAPH_WIDTH, GRAPH_Y + GRAPH_HEIGHT,
               GRAPH_X + GRAPH_WIDTH - 5, GRAPH_Y + GRAPH_HEIGHT - 3, AXIS_COLOR);// X轴右尖
tft.drawLine(GRAPH_X + GRAPH_WIDTH, GRAPH_Y + GRAPH_HEIGHT,
               GRAPH_X + GRAPH_WIDTH - 5, GRAPH_Y + GRAPH_HEIGHT + 3, AXIS_COLOR);

// 绘制水平网格线(4等分)
for (int i = 1; i < 4; i++) {
    int yPos = GRAPH_Y + i * GRAPH_HEIGHT / 4;// 网格线Y坐标
    for (int x = GRAPH_X; x < GRAPH_X + GRAPH_WIDTH; x += 4) {// 虚线绘制
      tft.drawPixel(x, yPos, 0x5AEB);// 浅灰色像素(RGB565)
    }
}
}

/**
* @brief 绘制数值面板静态文本
* @details 绘制V/I/P标签和单位,仅初始化时绘制
*/
void drawPanelStaticText() {
tft.setTextColor(TEXT_COLOR);
tft.setTextSize(2);

// 电压标签
tft.setCursor(PANEL_X + 10, PANEL_Y + 50);
tft.print("V:");
// 电流标签
tft.setCursor(PANEL_X + 10, PANEL_Y + 110);
tft.print("I:");
// 功率标签
tft.setCursor(PANEL_X + 10, PANEL_Y + 170);
tft.print("P:");

// 单位标注
tft.setTextSize(1);
tft.setCursor(PANEL_X + 55, PANEL_Y + 70);
tft.print("V");    // 电压单位
tft.setCursor(PANEL_X + 55, PANEL_Y + 130);
tft.print("mA");   // 电流单位
tft.setCursor(PANEL_X + 55, PANEL_Y + 190);
tft.print("mW");   // 功率单位
}

/**
* @brief 绘制波形图例
* @details 绘制电压/电流/功率的颜色标识,方便用户识别波形
*/
void drawLegend() {
int legendY = GRAPH_Y + GRAPH_HEIGHT + 25;// 图例Y坐标

// 电压图例
tft.fillRect(20, legendY, 15, 3, VOLTAGE_COLOR);// 颜色块
tft.setTextColor(VOLTAGE_COLOR);
tft.setTextSize(1);
tft.setCursor(40, legendY - 3);
tft.print("VOLTAGE");

// 电流图例
tft.fillRect(100, legendY, 15, 3, CURRENT_COLOR);
tft.setTextColor(CURRENT_COLOR);
tft.setCursor(120, legendY - 3);
tft.print("CURRENT");

// 功率图例
tft.fillRect(180, legendY, 15, 3, POWER_COLOR);
tft.setTextColor(POWER_COLOR);
tft.setCursor(200, legendY - 3);
tft.print("POWER");
}

/**
* @brief 更新整个UI界面
* @details 包含实时数值更新和波形绘制,是界面动态更新的核心函数
* @param voltage 总线电压(V)
* @param current 电流(mA)
* @param power 功率(mW)
*/
void updateUI(float voltage, float current, float power) {
// 1. 更新右侧数值面板
updatePanelValues(voltage, current, power);

// 2. 清空波形区(保留边框和坐标轴)
tft.fillRect(GRAPH_X + 1, GRAPH_Y + 1, GRAPH_WIDTH - 1, GRAPH_HEIGHT - 1, BACKGROUND);
drawAxes();// 重新绘制坐标轴(避免被清空)

// 3. 计算波形最大值(用于归一化显示)
float maxVal = 0.1;// 初始值(避免除以0)
for (int i = 0; i < HISTORY_SIZE; i++) {
    if (voltageHistory > maxVal) maxVal = voltageHistory;
    if (currentHistory > maxVal) maxVal = currentHistory;
    if (powerHistory > maxVal) maxVal = powerHistory;
}
maxVal *= 1.15;// 留15%余量,避免波形超出显示区

// 4. 绘制波形(循环绘制历史数据点)
for (int i = 1; i < HISTORY_SIZE; i++) {
    // 计算缓存索引(循环读取)
    int prevIndex = (historyIndex + i - 1) % HISTORY_SIZE;
    int currIndex = (historyIndex + i) % HISTORY_SIZE;

    // 计算X坐标(时间轴)
    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 / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);
    int y2_voltage = GRAPH_Y + GRAPH_HEIGHT - constrain(voltageHistory / 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 / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);
    int y2_current = GRAPH_Y + GRAPH_HEIGHT - constrain(currentHistory / 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 / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);
    int y2_power = GRAPH_Y + GRAPH_HEIGHT - constrain(powerHistory / maxVal * GRAPH_HEIGHT, 0, GRAPH_HEIGHT);
    tft.drawLine(x1, y1_power, x2, y2_power, POWER_COLOR);
}

// 5. 绘制Y轴数值标签(最大值、中间值、0)
tft.setTextColor(0xAD75);// 浅紫色
tft.setTextSize(1);
tft.setCursor(GRAPH_X + 2, GRAPH_Y + 5);
tft.print(maxVal, 1);            // 最大值(保留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");                  // 最小值
}

/**
* @brief 更新实时数值面板
* @details 清空原有数值区域并打印新值,避免文本重叠
* @param voltage 总线电压(V)
* @param current 电流(mA)
* @param power 功率(mW)
*/
void updatePanelValues(float voltage, float current, float power) {
tft.setTextColor(TEXT_COLOR);
tft.setTextSize(1);

// 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);// 保留2位小数

// 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);

// 3. 更新功率数值
tft.fillRect(PANEL_X + 25, PANEL_Y + 185, 30, 12, PANEL_COLOR);
tft.setCursor(PANEL_X + 25, PANEL_Y + 190);
tft.print(power, 2);
}

/******************************************************************************
* 深圳市在芯间科技有限公司
* **旗舰店:零知派
* 网 址:https://shop533070398.taobao.com
* 版权说明:
*1.本代码的版权归【深圳市在芯间科技有限公司】所有,仅限个人非商业性学习使用。
*2.严禁将本代码或其衍生版本用于任何商业用途(包括但不限于产品开发、付费服务、企业内部使用等)。
*3.任何商业用途均需事先获得【深圳市在芯间科技有限公司】的书面授权,未经授权的商业使用行为将被视为侵权。
******************************************************************************/系统流程图


2.4 INA219库文件解析1)传感器软件I2C 初始化
初始化传感器对象,指定 I2C 地址和软件 I2C 总线

INA219::INA219(const uint8_t address, SoftWire *wire)// SoftWire使用I2C
{
_address   = address;
_wire      = wire;
//no calibrated values by default.
_current_LSB = 0;
_maxCurrent= 0;
_shunt       = 0;
_error       = 0;
}


bool INA219::begin()
{
if (! isConnected()) return false;
return true;
}


bool INA219::isConnected()
{
if ((_address < 0x40) || (_address > 0x4F)) return false;
_wire->beginTransmission(_address);
return ( _wire->endTransmission() == 0);
}2)寄存器读写函数
INA219 通过 I2C 读写寄存器实现数据交互

uint16_t INA219::_readRegister(uint8_t reg)
{
_wire->beginTransmission(_address);
_wire->write(reg);
int n = _wire->endTransmission();
if (n != 0)
{
    _error = -1;
    return 0;
}

uint16_t value = 0;
if (2 == _wire->requestFrom(_address, (uint8_t)2))
{
    value = _wire->read();
    value <<= 8;
    value |= _wire->read();
}
else
{
    _error = -2;
    return 0;
}
return value;
}


uint16_t INA219::_writeRegister(uint8_t reg, uint16_t value)
{
_wire->beginTransmission(_address);
_wire->write(reg);
_wire->write(value >> 8);
_wire->write(value & 0xFF);
int n = _wire->endTransmission();
if (n != 0)
{
    _error = -1;
}
return n;
}

三、项目演示3.1 监测过程与比较系统实时显示电压、电流和功率信息,并通过波形图表展示历史数据变化趋势


界面包含三个主要区域
    左侧波形区:实时显示电压、电流、功率的三通道波形;右侧数据区:实时数值显示,包含单位标识;底部图例:颜色标识区分不同参数


Tips
零知派标准板上的A1和A0都与Vs+引脚连接引出排针接口,通过跳线帽短接直接切换0x41、0x44和0x45 I2C总线地址,本项目将A1通过跳线帽短接,I2C地址为0x44



3.2 万用表对比测试将万用表分别打到量程为20V的电压档以及20mA的电流档


进行电压档测试

进行电流档测试


参数万用表测量值系统测量值误差
电压4.90V4.97V+0.07V
电流                1.917mA               1.893mA         -0.024mA      
功率9.393mW9.408mW-0.015mW
注:电压值和电流值误差控制在整个温度范围内的0.5%精度


3)通过串口输出数据,便于进一步分析




3.3 视频演示
https://live.csdn.net/v/522314?spm=1001.2014.3001.5501
系统启动过程、实时数据监测、波形显示效果,以及在不同负载条件下系统的响应特性。可以看到当电路负载变化时,电流和功率读数实时更新,波形图表平滑滚动,系统响应迅速且稳定。


四、INA219 电流功率监测计技术讲解INA219是一款基于I2C接口的高端电流分流和功率监控器,能够监测分流器压降和电源电压。其内部包含16位ADC、可编程增益放大器(PGA) 和精密基准电压源,能够实现高精度测量

4.1 INA219工作原理

​根据芯**册,经过分流电阻N(采样电阻)后,能够采集到的最低有效电压LSB为10uV。
利用欧姆定律计算电流公式:
电流测量:根据Rshunt(0.1Ω)分流电阻两端的电压降计算;电压测量:直接测量总线电压(支持0-26V范围);功率计算:内部乘法器实时计算功率


4.2 I2C通信协议
本项目采用软件模拟 I2C(SoftWire)与 INA219 通信
1)串行总线地址
​INA219 有两个地址引脚A0 和 A1都设置为GND,该从机地址为0x40
2)软件I2C时序

总线上的所有从机在 SCL 的上升沿移入从机地址字节,其中最后一位指示要进行的是读操作还是写操作。在第九个时钟脉冲期间,被寻址的从机通过生成确认信号并将 SDA 拉至低电平来响应主机


4.3 寄存器配置1)配置寄存器(0x00)

BRNG位(13):总线电压范围选择(0=16V, 1=32V);PG位(11-12):PGA增益设置(00=±40mV, 01=±80mV, 10=±160mV, 11=±320mV);BADC位(7-10):总线ADC分辨率和平均模式;SADC位(3-6):分流ADC分辨率和平均模式;



eg:MODE位(0-2):操作模式选择

阴影部分的值为默认值,采取连续分流和总线测量模式


2)校准寄存器(0x05)



校准值计算公式:

其中电流LSB = 最大预期电流 / 32768
Current_LSB=10010^-6=100uA=0.0001A
计算基准值:Cal=0.04096/(Current_LSB/R)=0.04096/(0.0001A0.1R)=4096=0x1000
校准寄存器与缩放

如果发现测量到的电流值有误,用电流表测到的实际值为0.290A,INA219测量结果为0.342A
采用Cal的校准公式(缩放校准后的)Cal=4096*0.290/0.3421 = 3472 = 0x0D90


五、常见问题解答(Q&A)Q1:测量值不准确怎么办?A:可以尝试:重新校准传感器,确保setMaxCurrentShunt()函数的参数与实际使用的分流电阻匹配;进行零点校准,在无负载情况下测量电流值,并在代码中进行补偿
Q2:软件 I2C 通信不稳定怎么办?
A:可以:调整i2c_delay参数,确保通信时序正确;降低通信速率(如从 400kHz 降至 100kHz)
Q3:软件I2C通信失败如何排查?
A:检查引脚配置是否正确(SCL接零知标准板A5、SDA接零知标准板A4);采用I2C扫描函数确认INA219地址设置(默认0x40)

项目资源整合
INA219 库(支持 SoftWire):      RobTillaart/INA219
INA219 数据手册:                      INA219 DataSheet​

页: [1]
查看完整版本: 零知派——STM32驱动INA219电流功率监测计实现高精度电源管理