TA的每日心情 | 奋斗 2025-7-11 08:33 |
|---|
签到天数: 2 天 连续签到: 1 天 [LV.1]初来乍到
秀才
- 积分
- 321
|
✔零知派(零知开源)是一个专为电子初学者/电子兴趣爱好者设计的开源软硬件平台,在硬件上提供超高性价比STM32系列开发板、物联网控制板。取消了Bootloader程序烧录,让开发重心从 “配置环境” 转移到 “创意实现”,极大降低了技术门槛。零知派编程软件,内置上千个覆盖多场景的示例代码,支持项目源码一键下载,项目文章在线浏览。零知派(零知开源)平台通过软硬件协同创新,让你的创意快速转化为实物,来动手试试吧!
项目概述
本项目基于零知标准板(主控芯片为 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=A4 SCL=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[i] = 0;
- currentHistory[i] = 0;
- powerHistory[i] = 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[i] > maxVal) maxVal = voltageHistory[i];
- if (currentHistory[i] > maxVal) maxVal = currentHistory[i];
- if (powerHistory[i] > maxVal) maxVal = powerHistory[i];
- }
- 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[prevIdx]),
- x2, CALC_Y(voltageHistory[currIdx]), VOLTAGE_COLOR);
- tft.drawLine(x1, CALC_Y(currentHistory[prevIdx]),
- x2, CALC_Y(currentHistory[currIdx]), CURRENT_COLOR);
- tft.drawLine(x1, CALC_Y(powerHistory[prevIdx]),
- x2, CALC_Y(powerHistory[currIdx]), 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[historyIndex] = busVoltage;
- currentHistory[historyIndex] = current;
- powerHistory[historyIndex] = 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("[DBG] 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_WIDTH 240
- #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[HISTORY_SIZE];
- float currentHistory[HISTORY_SIZE];
- float powerHistory[HISTORY_SIZE];
- 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_HEIGHT 174
- #define PANEL_X 210
- #define PANEL_Y 0
- #define PANEL_WIDTH 110
- // -------------------------- 颜色定义 (RGB565) --------------------------
- #define C_BG 0x0000
- #define C_TITLEBAR 0x0883
- #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 BACKGROUND C_BG
- #define TEXT_COLOR C_TEXT
- #define AXIS_COLOR C_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会开始实时接收并显示数据
③系统运行
观察屏幕,右侧面板将实时更新电压、电流、功率的数值;左侧波形区域将开始绘制三条不同颜色的波形曲线,并随时间滚动
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
|
|