eefocus_4087784 发表于 2026-4-11 14:55:12

零知派——STM32驱动INA226 高精度功率监测仪(可视化数据记录仪+一键导出Excel)

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

项目概述
      本项目基于零知标准板(主控芯片为 STM32F103RBT6)和 INA226 高精度电流/功率监测芯片,搭建了一套完整的LED负载功率监测系统。实时采集LED负载的总线电压、电流和功率。在 ST7789 240x320 TFT彩屏上,以三路独立波形(电压、电流、功率)直观展示数据变化趋势,按照 PLX-DAQ 格式实时接收串口输出数据
项目难点及解决方案
      问题描述:实时地在TFT屏幕上绘制波形和更新数值,同时不影响数据的串口导出
解决方案:先清空波形区域,再重绘网格和轴线,最后绘制新数据点

一、系统接线部分1.1 硬件清单
硬件名称 数量 说明
零知标准板 (STM32F103RBT6) 1 主控板
INA226 功率监测模块 1 内置0.1Ω分流电阻,建议选择成品模块
ST7789 TFT彩屏 (240x320) 1 SPI接口屏幕,用于数据显示
LED (功率负载) 1 如3W大功率LED
电阻 (如220Ω) 1 用于限制LED电流,确保不超0.5A
杜邦线若干 - 用于连接各模块
5V/3.3V电源 1 为系统供电

1.2 接线方案表      按照代码定义进行连接,ST7789直插零知标准板TFT扩展口,无需单独接线:


INA226模块 零知标准板引脚零知标准板引脚 LED负载 电源
VCC 3.3V 5V (或3.3V) LED 正极电源 正极 (如5V)
GND GND GND LED 负极 IN+ 引脚
SCL A5 无需连接 分流电阻 -
SDA A4 无需连接 IN- 引脚 电源 负极 (GND)
VBUS 电源 正极 (LED+端) D10 - -
IN+ LED 负极 D2 - -
IN- 电源 负极 (GND) D4 - -

1.3 具体接线图

      请注意:确保电流严格按照 电源正极 → LED → IN+ → 分流电阻 → IN- → 电源负极 的路径流动

1.4 接线实物图      VBUS必须连接到被测电源的正极(LED正极),INA226正确采样总线电压。如果VBUS悬空,功率寄存器始终为0



二、安装与使用部分2.1 开源平台-输入"INA226 高精度功率监测仪"并搜索-代码下载自动打开

2.2 连接-验证-上传

2.3 调试-串口监视器


三、代码讲解部分      本项目代码核心在于四个部分:INA226的初始化与校准、波形数据的采集与缓存、UI界面的刷新与绘制,以及数据导出格式
3.1 INA226 初始化与校准
void setup() {
Serial.begin(115200);
while (!Serial);

// ── TFT 初始化
tft.init(TFT_WIDTH, TFT_HEIGHT);
tft.invertDisplay(true);
tft.setRotation(1);
tft.fillScreen(C_BG);
showSplashScreen();
delay(1500);

// ── 软件 I2C + INA226 初始化
sw.begin();
sw.setClock(100000);

if (!ina226.begin()) {
    tft.fillScreen(C_BG);
    tft.setTextColor(0xF800); tft.setTextSize(2);
    tft.setCursor(20, 100); tft.print("INA226 NOT FOUND");
    tft.setTextColor(C_DIM); tft.setTextSize(1);
    tft.setCursor(20, 128); tft.print("SDA=A4SCL=A5");
    Serial.println("未检测到INA226,请检查接线!");
    while (1) delay(10);
}

int err = ina226.setMaxCurrentShunt(0.5, 0.1, true);
if (err != 0) {
    Serial.print("校准失败,错误码: 0x");
    Serial.println(err, HEX);
    while (1) delay(10);
}

// ── 清空历史缓存
for (int i = 0; i < HISTORY_SIZE; i++) {
    voltageHistory = 0;
    currentHistory = 0;
    powerHistory   = 0;
}

// ── 绘制静态界面
drawStaticUI();

// ── PLX-DAQ 初始化(必须在 Serial.begin 之后)
plxDaqInit();

Serial.println("系统初始化完成");
Serial.print("INA226_LIB_VERSION: ");
Serial.println(INA226_LIB_VERSION);

lastSampleTime = millis();
}1)校准函数:ina226.setMaxCurrentShunt(0.5, 0.1, true)
maxCurrent = 0.5:预期的最大电流值,这个值决定了电流寄存器的最小分辨率(Current_LSB)
根据Current_LSB = Maximum_Expected_Current / 2^15 公式:Current_LSB = 0.5 / 32768 ≈ 15.26e-6 A/bit = 15.26 μA/bit
这意味着电流寄存器的每1个LSB代表15.26μA
      shunt = 0.1:外部分流电阻的阻值(Ω),INA226需要这个值来将测得的差分电压换算成电流;normalize = true:归一化标志位


2)校准寄存器的计算公式:
      库函数根据归一化后的Current_LSB和R_SHUNT计算出校准值,并写入0x05寄存器。之后,INA226内部硬件会自动执行Current = (V_SHUNT × CAL) / 2048的计算,并将结果存入电流寄存器

3.2 UI刷新与波形绘制void updateUI(float voltage, float current, float power) {
// 1. 更新右侧数值面板
updatePanelValues(voltage, current, power);

// 2. 清除波形区域,只保留左侧Y轴一列
tft.fillRect(GRAPH_X + 1, GRAPH_Y, GRAPH_WIDTH - 1, GRAPH_HEIGHT, C_BG);

// 3. 重绘网格和轴线
for (int row = 1; row < 4; row++) {
    int yh = GRAPH_Y + row * GRAPH_HEIGHT / 4;
    tft.drawFastHLine(GRAPH_X + 1, yh, GRAPH_WIDTH - 1, C_GRID_H);
}
// ... (重绘纵向虚线)
tft.drawFastVLine(GRAPH_X, GRAPH_Y, GRAPH_HEIGHT, C_AXIS);
tft.drawFastHLine(GRAPH_X, GRAPH_Y + GRAPH_HEIGHT, GRAPH_WIDTH, C_AXIS);

// 4. 找到当前缓存区中所有数据的最大值,并乘以1.15作为Y轴动态范围
float maxVal = 0.1f;
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.15f;

// 5. 绘制Y轴量程标注
tft.fillRect(GRAPH_X + 1, GRAPH_Y, 28, 7, C_BG);
tft.setCursor(GRAPH_X + 1, GRAPH_Y + 1); tft.print(maxVal, 1);
// ... (绘制中点、零点)

// 6. 绘制三路折线图
#define CALC_Y(val) \
      (GRAPH_Y + GRAPH_HEIGHT - 1 - \
       (int)constrain((val) / maxVal * (float)(GRAPH_HEIGHT - 2), 0.0f, (float)(GRAPH_HEIGHT - 2)))
for (int i = 1; i < HISTORY_SIZE; i++) {
    int prevIdx = (historyIndex + i - 1) % HISTORY_SIZE;
    int currIdx = (historyIndex + i)   % HISTORY_SIZE;

    int x1 = GRAPH_X + (i - 1) * 2;
    int x2 = GRAPH_X + i * 2;
    if (x2 >= GRAPH_X + GRAPH_WIDTH) break;

    tft.drawLine(x1, CALC_Y(voltageHistory),
               x2, CALC_Y(voltageHistory),VOLTAGE_COLOR);
    tft.drawLine(x1, CALC_Y(currentHistory),
               x2, CALC_Y(currentHistory),CURRENT_COLOR);
    tft.drawLine(x1, CALC_Y(powerHistory),
               x2, CALC_Y(powerHistory),    POWER_COLOR);
}
// ... (绘制光标竖线)
}      动态Y轴范围:实时扫描整个历史缓冲区,找出电压、电流、功率中的最大值,并在此基础上增加15%的余量作为当前Y轴的上限
      坐标映射:宏定义CALC_Y(val)是实现数据到屏幕像素转换的核心,根据当前最大值maxVal,将真实数据val线性映射到波形区的高度范围(GRAPH_HEIGHT)内。constrain函数确保了映射结果不会超出波形区边界
      波形绘制:通过循环,将历史缓冲区中相邻的两个数据点用drawLine连接起来。x坐标的间隔为2像素,y坐标通过CALC_Y计算得出

3.3 PLX-DAQ数据导出      使用PLX-DAQ格式,无需实现复杂的文件系统或SD卡写入,只需通过串口发送文本,就可以利用PC端强大的Excel软件完成数据的记录、保存和可视化分析

// ========================== 主循环 ==========================
void loop() {
unsigned long now = millis();

// 按采样间隔执行
if (now - lastSampleTime >= SAMPLE_INTERVAL) {
    lastSampleTime = now;

    float busVoltage = ina226.getBusVoltage();
    float current    = ina226.getCurrent_mA();
    float power      = ina226.getPower_mW();

    // current -= 0.8;// 零点漂移补偿(如需要)

    // 存入波形缓存
    voltageHistory = busVoltage;
    currentHistory = current;
    powerHistory   = power;

    // 刷新 TFT 界面
    updateUI(busVoltage, current, power);

    // 发送 PLX-DAQ 数据到 Excel
    plxDaqSend(busVoltage, current, power);

    historyIndex = (historyIndex + 1) % HISTORY_SIZE;
}
}

// ========================== PLX-DAQ 函数 ==========================

/**
* PLX-DAQ 初始化
* 向 Excel 发送列标题定义指令,建立数据表头和图表列绑定
* 必须在 setup() 末尾调用一次
*
* PLX-DAQ 指令说明:
*   CLEARDATA          - 清空 Excel 已有数据,从 A1 重新开始
*   LABEL,xxx,xxx,...- 定义各列的表头名称
*   RESETTIMER         - 将内置计时器归零
*   ROW,DATA,x,x,...   - 发送一行数据(对应 LABEL 顺序)
*   CELL,SET,行,列,值- 直接写入指定单元格(可选)
*/
void plxDaqInit() {
delay(500);// 等待 Excel 插件就绪

// 清空旧数据,从头开始记录
Serial.println("CLEARDATA");

// 定义列标题
// 格式:LABEL,列A标题,列B标题,...
Serial.println("LABEL,Sample,Time_ms,Voltage_V,Current_mA,Power_mW");

// 重置 PLX-DAQ 内置计时器
Serial.println("RESETTIMER");

delay(100);
}

/**
* PLX-DAQ 数据发送
* 每次采样后调用,向 Excel 写入一行数据
*
* Excel 列对应关系:
*   A列 = Sample    采样序号(从1开始递增)
*   B列 = Time_ms   运行时间戳(秒,保留1位小数)
*   C列 = Voltage_V 总线电压(V,保留3位小数)
*   D列 = Current_mA 电流(mA,保留2位小数)
*   E列 = Power_mW功率(mW,保留2位小数)
*
* PLX-DAQ ROW 指令格式:
*   ROW,DATA,值1,值2,值3,...
*   值的顺序必须与 LABEL 定义完全一致
*/
void plxDaqSend(float voltage, float current, float power) {
sampleCount++;

// 构建 PLX-DAQ 数据行
// ROW,DATA 是固定前缀,后接各列数据,逗号分隔
Serial.print("ROW,DATA,");
Serial.print(sampleCount);          // A列:采样序号
Serial.print(",");
Serial.print(millis() / 1000.0, 1); // B列:时间戳(秒,1位小数)
Serial.print(",");
Serial.print(voltage, 3);         // C列:电压 V(3位小数)
Serial.print(",");
Serial.print(current, 2);         // D列:电流 mA(2位小数)
Serial.print(",");
Serial.println(power, 2);         // E列:功率 mW(2位小数,println自动换行)

// ── 可选:同步输出普通调试信息(不影响PLX-DAQ解析,需注释掉避免干扰)
// 注意:PLX-DAQ 只解析以 ROW/LABEL/CLEARDATA 等关键字开头的行
//       其余行会被忽略,普通 Serial.print 调试信息不会干扰 Excel 采集
//       但建议调试完成后注释掉以保持输出整洁
Serial.print(" V="); Serial.print(voltage,3);
Serial.print(" I="); Serial.print(current,2);
Serial.print("mA P="); Serial.print(power,2); Serial.println("mW");
}
PLX-DAQ协议:打开串口时,它会识别特定的命令和数据格式
      DATA 命令:告诉PLX-DAQ,接下来的一行是数据行,数据项之间用逗号分隔
      TIMESTAMP 命令(可选):可以在数据前添加时间戳、LABEL 命令(可选):可以在数据开始前定义列标题


3.4 数据结构与波形缓存// -------------------------- 引脚定义 --------------------------
#define TFT_CS    10
#define TFT_DC    2
#define TFT_RST   4

// -------------------------- 屏幕参数 --------------------------
#define TFT_WIDTH240
#define TFT_HEIGHT 320
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

// -------------------------- INA226 --------------------------
SoftWire sw(SCL, SDA, SOFT_STANDARD);
INA226 ina226(0x40, &sw);

// -------------------------- 波形缓存配置 --------------------------
const int HISTORY_SIZE = 96;
float voltageHistory;
float currentHistory;
float powerHistory;
int   historyIndex = 0;

// -------------------------- PLX-DAQ 配置 --------------------------
unsigned long sampleCount = 0;      // 采样计数器(Excel A列)
unsigned long lastSampleTime = 0;   // 上次采样时间戳
#define SAMPLE_INTERVAL 1000          // 采样间隔 ms(与 delay 保持一致)

// -------------------------- 显示区域坐标 --------------------------
#define GRAPH_X       8
#define GRAPH_Y       26
#define GRAPH_WIDTH   196
#define GRAPH_HEIGHT174
#define PANEL_X       210
#define PANEL_Y       0
#define PANEL_WIDTH   110

// -------------------------- 颜色定义 (RGB565) --------------------------
#define C_BG      0x0000
#define C_TITLEBAR0x0883
#define C_PANEL   0x0883
#define C_DIVIDER   0x2965
#define C_GRID_H    0x10A2
#define C_GRID_V    0x0841
#define C_AXIS      0x3186
#define C_TEXT      0xFFFF
#define C_DIM       0x8410
#define C_LABEL   0xAD55
#define VOLTAGE_COLOR 0xFD20
#define CURRENT_COLOR 0x07EF
#define POWER_COLOR   0xF81F
#define BACKGROUNDC_BG
#define TEXT_COLORC_TEXT
#define AXIS_COLORC_AXIS
#define PANEL_COLOR C_PANEL

// -------------------------- 函数声明 --------------------------
void showSplashScreen();
void drawStaticUI();
void drawAxes();
void drawPanelStaticText();
void drawLegend();
void updateUI(float voltage, float current, float power);
void updatePanelValues(float voltage, float current, float power);
void plxDaqInit();
void plxDaqSend(float voltage, float current, float power);
      voltageHistory、currentHistory、powerHistory三个独立的缓冲区,分别存储三种物理量的历史值


系统流程图


四、项目结果演示4.1 操作流程①编译烧录
      按照接线方案表连接好所有硬件,并仔细检查电流检测回路和VBUS连接是否正确。打开零知派软件,将项目代码烧录到零知标准板


②打开串口监视器/Excel
查看实时数据
      打开零知派的串口监视器(波特率115200),可以看到每秒输出的电压、电流、功率数据


导出数据到Excel
      安装PLX-DAQ v2加载项到Excel、在Excel中打开PLX-DAQ工具、选择正确的COM口和波特率(115200)、点击“Connect”按钮。此时,Excel会开始实时接收并显示数据


③系统运行
      TFT屏幕将显示启动画面,然后进入主界面
观察屏幕,右侧面板将实时更新电压、电流、功率的数值;左侧波形区域将开始绘制三条不同颜色的波形曲线,并随时间滚动


4.2 视频演示
视频演示了基于零知标准板(STM32F103)和INA226芯片的功率监测系统。系统通过TFT彩屏实时显示电压、电流、功率的数值及动态波形。同时,通过串口以PLX-DAQ格式输出数据,可直接导入Excel进行实时图表绘制和数据记录。视频展示了系统从启动到数据导出的完整过程

五、INA226 技术讲解      INA226内部的核心是一个16位ΔΣ型ADC,可以测量两个电压:分流电压(IN+ 与 IN- 之差)和总线电压(VBUS引脚对地电压)。测量结果经过数字滤波和平均后,存入相应的电压寄存器

如果校准寄存器被正确编程,硬件乘法器会自动计算出电流和功率值,并存入电流寄存器和功率寄存器
5.1 校准寄存器的计算      INA226本身并不知道你使用了多大的分流电阻,也不知道希望电流数据以何种分辨率呈现。校准就是告诉芯片这两个参数,从而使它能正确计算电流和功率


计算公式
Max_Expected_Current=0.5A、R_SHUNT=0.1Ω
      理论Current_LSB=0.5/32768≈15.26μA/bit,归一化后,库选择Current_LSB=20μA/bit(大于15.26的最小整数倍)
电压、电流、功率的读取流程
      getBusVoltage()、getCurrent_mA()、getPower_mW() 函数内部完成了寄存器读取和数值转换


5.2 I2C通信协议      INA226作为I2C从设备,地址可通过A0/A1引脚配置,本代码使用默认地址0x40


写操作时序
对寄存器的写入操作由主机发送首个字节开始,该字节为从机地址,且读写位为低电平


读操作时序
从设备读取数据时,通过写操作存入寄存器指针的最后一个值,将决定读操作期间读取哪个寄存器


寄存器映射


低侧检测电流路径
电源正极 → LED负载 → INA226 IN+ → [分流电阻] → INA226 IN- → 电源负极(GND)
                                              |——    电压测量差分对   ——|

六、常见问题解答(FAQ)Q1: 为什么屏幕上的电流或功率一直显示0?      A: 请检查:1) 电流检测回路是否正确(IN+接负载侧,IN-接地侧);2) VBUS是否连接到负载电源正极;3) 校准参数中的分流电阻值是否与实际一致
Q2: Excel无法接收到数据?
       A: 确保PLX-DAQ选择的COM口正确,波特率与代码一致(115200),且没有其他程序占用串口
Q3: 能否测量负电流(如电池放电/充电双向)?
      A: 可以。INA226的电流寄存器是有符号数,当电流反向(从IN-流向IN+)时,读数会变为负数。您可以在代码中直接获取负值并显示


项目资源整合
INA226数据手册:    ina226.pdf
INA226库文件:       RobTillaart/INA226

页: [1]
查看完整版本: 零知派——STM32驱动INA226 高精度功率监测仪(可视化数据记录仪+一键导出Excel)