简介面向嵌入式软硬件工程师及电赛、课程设计学习者的 INA226 功率测量模块实战资料围绕 STM32F103 与 INA226 的软硬件协同展开用于解决电流、电压与功率的实时监测问题。压缩包约 59.3MB上游未提供文件清单与类型明细从资源标签看内容涉及 STM32F103 驱动软件、AD 采集与硬件电路设计、PCB 布局等多个部分。资源已有 3392 人学习下载具备一定参考热度。读者可从中获取 I2C 接口初始化与寄存器配置思路、量程与采样率设定方法、分流电阻取值与噪声抑制要点以及 I2C 走线、去耦电容布置等 PCB 实践经验并结合 Seasky_INA226 示例理解从数据采集到功率换算的完整链路适合需要快速搭建电源监控方案的中级开发者对照学习。1. 需求切入一块 12V/5A 的监控板为什么绕不开 INA226 功率测量模块做 12V/5A 直流负载监控直觉方案是串一只 0.01Ω 采样电阻运放放大后送进 STM32F103 的 ADC成本低、路子熟。真跑起来才发现问题全在细节里几十毫安的待机电流被 ADC 噪声淹掉运放失调随温度漂采样电阻两端随便引两根线就把走线电阻串进了测量回路最后标定表得做好几页。INA226 功率测量模块换了个思路把分流电压 ADC、总线电压 ADC、功率乘法器和校准寄存器全塞进一颗芯片通过 I2C 直接给出电流和功率的数值。这套方案适合要做电源监控、电池充放电检测、电机功耗采集的课程设计和毕业设计也适合需要一份能直接投板的硬件方案参考。下面按需求、选型、硬件、软件、PCB、验收的顺序拆开讲。2. 总体方案与器件选型INA226 与 STM32F103 的分工边界方案定得稳不稳先看职责怎么切。这套系统的边界很清楚INA226 负责所有模拟量的事——分流电压采样、总线电压采样、功率计算STM32F103 负责时序、配置、读数和上层逻辑。中间只隔一条 I2C 总线没有模拟信号跨过 MCU 的引脚这是整个设计能稳的前提。2.1 为什么把模拟前端交给 INA226而不是 STM32F103 的 ADCSTM32F103 的 12 位 ADC 在 3.3V 参考下 LSB 约 0.8mV配合增益 50 倍的运放等效到采样电阻上的分辨率大约 16µV理论上够用。但实际误差源太多运放失调电压、电阻对管匹配、参考电压温漂、ADC 的积分非线性每一项单独看都不大叠在一起就是几个百分点的偏差还得靠软件两点校准去补。INA226 内部是 16 位 ΔΣADC分流电压 LSB 固定 2.5µV总线电压 LSB 1.25mV增益误差和失调在手册电气特性表里都有明确上限。更关键的是它内置了功率寄存器芯片内部用分流电压乘总线电压算出功率省掉 MCU 端的浮点乘法和时间对齐问题。对课程设计来说这是一颗参数写进寄存器就能出数的芯片调试路径短出错面小。代价是灵活性。INA226 的量程被采样电阻和校准寄存器锁死改量程要改电阻或者改 Current_LSB它也不做隔离共模电压上限就是手册标注的那条线。选型时要确认这两点能接受否则该换隔离方案。提示INA226 的分流电压满量程是 ±81.92mV这个数字是后面所有量程计算的起点先记住它。2.2 信号链与模块划分从分流电阻到功率寄存器整套模块可以按信号流拆成四段每段职责独立调试时能分段验证模块输入输出关键器件/接口功率采样段母线电流分流电压合金采样电阻、开尔文走线电压采样段母线电压分压后电压VBUS 引脚内置分压直接接母线数字转换段分流/总线电压16 位数字量INA226 内部 ADC 与功率乘法器通信与控制段I2C 读写电流/电压/功率STM32F103 GPIO 模拟 I2C分流电压和总线电压进 INA226 后走两条独立通道可编程转换时间从 140µs 到 8.244ms 共 8 档平均次数从 1 到 1024 共 8 档。这两个参数决定了刷新率和噪声的取舍后面软件章节会具体算。2.3 量程、分辨率与采样率的预算表在动手画原理图前先把数字算清楚这张表直接决定采样电阻取值和配置寄存器项目取值计算依据采样电阻10mΩ5A 时压降 50mV占 ±81.92mV 满量程的 61%电流满量程±8.192A81.92mV ÷ 10mΩ电流分辨率250µA2.5µV ÷ 10mΩ电阻功耗0.25W 5AI²R 25 × 0.01总线电压满量程40.96V32767 × 1.25mV单次转换时间1.1msVBUSCT/VSHCT 100平均次数16AVG 010更新周期约 35ms(1.1 1.1) × 16母线 12V、电流 5A 的场景下50mV 压降相对 12V 只有 0.4%对负载几乎没有影响。如果被测母线是 3.3V 甚至更低压降就要重新权衡采样电阻得往下调代价是分辨率同步下降。这个取舍在方案评审时最容易被追问提前把表格摆出来比口头解释有效。3. 硬件与 PCB 设计采样电阻、共模范围与开尔文布线的硬约束硬件这一块是整个方案最容易翻车的地方因为 INA226 的精度上限由外围决定。芯片本身再准采样电阻选错、走线取错点、共模超范围读数照样没法用。AD 硬件设计在这个方案里不是接个 ADC而是围绕 81.92mV 这个微小信号把整条模拟通路的误差压住。3.1 采样电阻取值与功率余量采样电阻同时受三个约束夹击压降不能影响负载、压降不能超过 81.92mV、功耗和温漂要可控。先算上限。5A 满量程对应 R ≤ 81.92mV / 5A 16.4mΩ。取 10mΩ 留出 60% 余量母线电流有波动也不会削顶。再算功耗5A 下 I²R 0.25W选 2512 封装 1W 的合金电阻降额到 25%温升小阻值漂移也小。如果选普通厚膜电阻温度系数可能到几百 ppm/℃5A 连续跑半小时读数漂几十毫安很常见。不同阻值的取舍可以直接对比采样电阻5A 时压降满量程电流电流分辨率5A 时功耗5mΩ25mV±16.4A500µA0.125W10mΩ50mV±8.19A250µA0.25W20mΩ100mV超量程±4.10A125µA0.5W20mΩ 在 5A 下已经超过 81.92mV除非把满量程电流降到 4A 以内否则不能用。10mΩ 是这套需求下的平衡点。注意合金电阻的阻值精度通常标 1%这一项就吃掉了整个系统的误差预算的大头别把希望全压在 INA226 的增益误差上。3.2 共模电压、总线电压引脚与供电域INA226 的共模输入范围是 0 到 36V和供电电压无关这一点和很多电流检测放大器不同VS 用 3.3V 供电IN/IN- 照样能测 24V 母线上的分流电压。这带来一个直接好处——VBUS 引脚可以直接怼到母线上测电压不需要外部分压。但 36V 是硬上限。测 48V 系统时VBUS 必须分压而 IN/IN- 不能简单分压因为分压网络的不匹配会直接变成共模误差。这种情况要么换耐压更高的器件要么加隔离没有第三条路。做方案时把母线电压写在需求首页选型阶段就能排掉这个坑。供电域的选择也影响布线。VS 供 3.3V与 STM32F103 同域I2C 上拉直接拉到 3.3V省掉电平转换芯片VS 供 5V 时SDA/SCL 是开漏结构上拉到 3.3V 也能工作但要核对 VIH 门限。为减少变量这块板子统一用 3.3V 供电。A0、A1 两个地址引脚决定 7 位从机地址都接地时是 0x40。同一条总线上挂多片 INA226 时用这两个引脚错开地址最多 16 片做多路电源监控的课设可以直接用这个特性扩展。3.3 输入滤波、去耦与引脚保护IN 和 IN- 之间可以加一颗 100nF 差模电容配合每端 10Ω 串联电阻做 RC 滤波截止频率约 80kHz抑制开关电源的高频尖峰。串联电阻会引入误差因为 INA226 的输入偏置电流会在这两个电阻上产生压降但偏置电流在纳安级10Ω 上的压降可以忽略。滤波网络的位置有讲究电阻必须接在开尔文取样点之后不能接在功率走线中间否则滤波器的电阻和功率走线的电阻混在一起反而把误差放大了。去耦是另一个常被省掉的部分。VS 引脚旁边放 0.1µF紧贴引脚地端就近打过孔下地平面再并一颗 1µF 做低频储能。INA226 内部 ADC 在转换瞬间有电流尖峰去耦不到位读数会呈现规律性的跳动且跳动幅度随平均次数变化——这个现象在排查章节还会提到。ALERT 引脚可以接到 MCU 的外部中断配置成过流或过压报警。课程设计里加上这一路答辩时能讲的东西会多一层不只是读数据而是有保护动作。3.4 PCB 布线开尔文连接与功率回路分区PCB 上有三条规则必须守住。第一条采样电阻的开尔文连接。电流从电阻两端的大焊盘流入流出电压取样必须从焊盘内侧、电流路径的内边缘引出两条细走线直接进 IN/IN-两条线等长、平行、不与其他网络交叉。如果图省事从焊盘外侧或者走线中途取点取样点之间就夹了一段铜箔电阻几百微欧量级在 5A 下就是毫伏级误差小电流段直接失去意义。第二条功率回路和信号回路分区。功率走线按 5A 算宽度1oz 铜、温升 10℃ 的条件下 2mm 宽够用实际取 3mm 并开窗加锡留足余量。功率回路面积尽量小减少对信号区的磁场耦合。信号区INA226、去耦、I2C集中在一侧与功率区之间用地平面隔开。第三条I2C 走线避开功率回路。SDA/SCL 不与功率走线长距离平行必须交叉时走直角。上拉电阻 4.7kΩ 放在 MCU 端或者总线中段别放在 INA226 旁边——放远了线路电容大上升沿变缓400kHz 下容易出错。网络布线约束原因IN/IN-从焊盘内侧引出等长平行宽度 0.2mm避开功率走线电阻功率回路宽度 ≥3mm开窗加锡面积最小载流与磁耦合I2C远离功率区不平行走线上拉 4.7kΩ抗干扰、保证上升沿VS 去耦0.1µF 贴引脚过孔就近下地抑制转换尖峰4. 软件实现STM32F103 的寄存器配置、校准与滤波软件部分看着只是读几个寄存器真正决定成败的是三件事I2C 总线稳不稳、校准寄存器算得对不对、异常时能不能自己恢复。前两件是功能第三件是可靠性。4.1 为什么要用 GPIO 模拟 I2C而不用 F103 的硬件 I2CSTM32F103 的硬件 I2C 外设在总线异常时容易卡死从机拉住 SDA 不放主机的状态机停在某个等待位只能靠复位恢复。这个问题在调试阶段特别消耗时间尤其是 INA226 上电时序和 MCU 不同步的时候。用两个 GPIO 模拟 I2C时序完全可控出问题也能自己发时钟把从机推出来。代价是占用 CPU但 INA226 的更新周期在几十毫秒量级软件模拟跑 100kHz 完全够用。/* 软件 I2CPB6 SCLPB7 SDA均配置为开漏输出 */ #define I2C_PORT GPIOB #define I2C_SCL_PIN GPIO_Pin_6 #define I2C_SDA_PIN GPIO_Pin_7 #define INA226_ADDR 0x40 /* A1 A0 GND7 位地址 */ static void i2c_delay(void) { /* 72MHz 下约 2~3µs实测总线速率约 100kHz */ __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); } static void scl_write(uint8_t level) { GPIO_WriteBit(I2C_PORT, I2C_SCL_PIN, level ? Bit_SET : Bit_RESET); i2c_delay(); } static void sda_write(uint8_t level) { GPIO_WriteBit(I2C_PORT, I2C_SDA_PIN, level ? Bit_SET : Bit_RESET); i2c_delay(); } /* 开漏模式下直接读 IDR不需要切换输入输出方向 */ static uint8_t sda_read(void) { return GPIO_ReadInputDataBit(I2C_PORT, I2C_SDA_PIN); }开漏输出模式读 IDR 这个技巧省掉了方向切换代码量少一半但前提是外部有上拉电阻。如果板上没焊上拉读回来的永远是 1表现为读到的数据全是 0xFFFF这个症状记住排查时能省时间。起始、停止和字节收发的实现static void i2c_start(void) { sda_write(1); scl_write(1); sda_write(0); /* SCL 高电平期间 SDA 下降沿 起始 */ scl_write(0); } static void i2c_stop(void) { sda_write(0); scl_write(0); scl_write(1); sda_write(1); /* SCL 高电平期间 SDA 上升沿 停止 */ } /* 返回 0 表示收到 ACK返回 1 表示 NACK */ static uint8_t i2c_write_byte(uint8_t dat) { uint8_t ack; for (uint8_t i 0; i 8; i) { sda_write((dat 0x80) ? 1 : 0); dat 1; scl_write(1); scl_write(0); } sda_write(1); /* 释放 SDA等待从机应答 */ scl_write(1); ack sda_read(); scl_write(0); return ack; } static uint8_t i2c_read_byte(uint8_t ack) { uint8_t dat 0; sda_write(1); /* 释放 SDA准备接收 */ for (uint8_t i 0; i 8; i) { scl_write(1); dat (dat 1) | (sda_read() ? 1 : 0); scl_write(0); } sda_write(ack ? 0 : 1); /* 主机应答或非应答 */ scl_write(1); scl_write(0); return dat; }读写寄存器的封装。INA226 的寄存器是 16 位高字节在前读的时候要先写寄存器地址再发重复起始转成读方向static uint8_t ina226_write_reg(uint8_t reg, uint16_t val) { i2c_start(); if (i2c_write_byte(INA226_ADDR 1)) { i2c_stop(); return 1; } i2c_write_byte(reg); i2c_write_byte(val 8); i2c_write_byte(val 0xFF); i2c_stop(); return 0; } static uint16_t ina226_read_reg(uint8_t reg) { uint16_t val; i2c_start(); i2c_write_byte(INA226_ADDR 1); i2c_write_byte(reg); i2c_start(); /* 重复起始切换读方向 */ i2c_write_byte((INA226_ADDR 1) | 1); val (uint16_t)i2c_read_byte(1) 8; /* 高字节后给 ACK */ val | i2c_read_byte(0); /* 低字节后给 NACK */ i2c_stop(); return val; }写寄存器返回 ACK 状态是为了让上层能判断器件在不在线。读寄存器没有做异常返回实际工程里我一般加一个超时计数SDA 一直不释放就跳出循环回报错码。4.2 配置寄存器与校准寄存器的计算配置寄存器 0x00 的位定义bit15 复位bit13-11 平均次数bit10-9 总线电压转换时间bit8-6 分流电压转换时间bit5-3 工作模式。按 2.3 节的预算AVG16、两个通道各 1.1ms、连续模式下配置值算出来是 0x1938。总线电压和分流电压的转换时间档位在手册里有完整表格课程设计里选 1.1ms 就够了兼顾刷新率和噪声。校准寄存器 0x05 是这套方案的核心公式#define R_SHUNT_OHM 0.010f /* 10mΩ 采样电阻 */ #define CURRENT_LSB_A 0.00015625f /* 156.25µA取 2 的幂便于换算 */ /* CAL 0.00512 / (Current_LSB × Rshunt) */ /* 0.00512 / (0.00015625 × 0.010) 3276.8 → 3277 */ #define INA226_CAL_VALUE 3277Current_LSB 取 156.25µA 而不是严格按 5A/32768 算出的 152.6µA是为了让后续的 mA 换算变成移位操作代码里不出现除法。代价是电流满量程略微放大到 5.12A反正采样电阻那边留了余量。功率寄存器的 LSB 由 Current_LSB 推出25 × 156.25µA 3.90625mW。这个关系是固定的改采样电阻或者改 Current_LSB 都要重算。初始化顺序不能反先写配置再写校准#define INA226_REG_CONFIG 0x00 #define INA226_REG_SHUNT 0x01 #define INA226_REG_BUS 0x02 #define INA226_REG_POWER 0x03 #define INA226_REG_CURRENT 0x04 #define INA226_REG_CAL 0x05 void ina226_init(void) { i2c_gpio_init(); /* 配置 PB6/PB7 开漏 */ ina226_write_reg(INA226_REG_CONFIG, 0x1938); /* AVG16, 1.1ms, 连续模式 */ ina226_write_reg(INA226_REG_CAL, INA226_CAL_VALUE);/* 校准值必须在配置之后写 */ }注意校准寄存器写入 0 时电流和功率寄存器会一直返回 0但分流电压和总线电压正常。这个现象很容易被误判成芯片坏了。4.3 读数、符号扩展与滤波实现分流电压是有符号数电流寄存器也是 int16负电流比如电池放电方向反了用补码表示。读回来先转 int16再乘 LSBfloat ina226_read_current(void) { int16_t raw (int16_t)ina226_read_reg(INA226_REG_CURRENT); return raw * CURRENT_LSB_A; } float ina226_read_bus_voltage(void) { uint16_t raw ina226_read_reg(INA226_REG_BUS); return raw * 0.00125f; /* 总线电压 LSB 1.25mV */ } float ina226_read_power(void) { uint16_t raw ina226_read_reg(INA226_REG_POWER); return raw * 25.0f * CURRENT_LSB_A; /* 功率 LSB 25 × Current_LSB */ }浮点乘法在 F103 上没有硬件 FPU全用 float 会拖慢主循环。实际工程里我一般把结果转成定点毫安和毫瓦只在显示层做一次除法。课程设计对实时性要求不高用 float 换可读性也说得过去。数据滤波分两级。第一级靠芯片内部的平均次数16 次平均已经把随机噪声压下去一截第二级在 MCU 端做滑动平均窗口取 8#define FILTER_N 8 static int32_t win[FILTER_N]; static uint8_t win_idx; static int32_t win_sum; int32_t current_filter(int32_t sample_ma) { win_sum - win[win_idx]; win[win_idx] sample_ma; win_sum sample_ma; win_idx (win_idx 1) % FILTER_N; return win_sum / FILTER_N; /* 返回 8 点滑动平均单位 mA */ }滑动平均的窗口不能太大。窗口 8 配合 35ms 更新周期阶跃响应延迟约 280ms做负载切换监测已经偏慢窗口开到 32响应延迟超过 1 秒看到的就是滞后曲线。如果被测对象有突变加一级中值滤波先剔野点再做平均比单纯加大窗口有效。4.4 异常处理总线恢复与数据有效性判断I2C 从机死锁时的恢复流程主机手动发 9 个 SCL 时钟把从机的移位寄存器推空然后发一个停止条件总线就回到空闲态。void i2c_bus_recover(void) { sda_write(1); for (uint8_t i 0; i 9; i) { /* 9 个时钟足以推空 8 位数据 1 位应答 */ scl_write(0); scl_write(1); } i2c_stop(); /* 补一个停止条件复位从机状态机 */ }数据有效性判断放在上层。每次读取前先确认校准寄存器还能读回原值读回 0 说明芯片掉电重启过需要重新初始化连续 N 次读到的电流值完全一致且等于满量程多半是采样电阻开路或者 IN 引脚虚焊。这两条检查加起来不到十行代码但在整机联调时能省掉大量猜测。5. 避坑与排查五类最容易翻车的问题这些坑基本都踩过一遍写出来是为了让后来的人少走弯路。每条按现象、原因、解决三段说。第一类小电流段读数完全没有参考价值。现象是大电流下测得挺准一到 100mA 以下就飘正负几十毫安乱跳。原因多半是取样点没走开尔文IN/IN- 从采样电阻外侧或者走线中途引出中间夹了一段铜箔电阻和焊锡电阻几百微欧量级配上温漂就是移动的零点。解决办法是把取样线改到焊盘内侧、电流路径的内边缘两条线等长平行改完重新标定零点。第二类总线电压读数正常电流和功率恒为 0。现象很迷惑因为电压通道工作正常容易怀疑电流通道坏了。原因是校准寄存器没写或者写成了 0INA226 内部做电流换算时要乘校准系数系数为 0 结果自然为 0。排查方法是把 0x05 寄存器读回来比对确认写入顺序是配置在前、校准在后中间没有复位动作。第三类I2C 通信时通时断复位后又好了。现象是运行几分钟到几十分钟不等SDA 被拉低不释放主机卡在等待 ACK。原因是总线受功率回路的干扰或者上拉电阻阻值偏大导致上升沿太缓400kHz 下从机采样出错。解决办法是把 I2C 走线挪离功率区、上拉改到 4.7kΩ、软件里加 9 时钟恢复流程三件事一起做单独做一件往往还会复发。第四类读数呈现规律性跳动跳动周期和转换时间同步。现象是数值在一个固定范围内来回摆平均值是对的单次值不能看。原因是 VS 引脚去耦不足内部 ADC 转换瞬间的电流尖峰把供电拉低参考跟着抖。排查时用示波器看 VS 引脚的纹波能看到和转换周期同步的尖峰。解决办法是 0.1µF 紧贴引脚、过孔就近下地必要时再并一颗 1µF。第五类满量程附近读数削顶大电流时数值不再增长。现象是电流超过某个值后读数卡住不动或者突然跳到负值。原因是采样电阻取值过大5A 时压降超过 81.92mV分流电压通道饱和另一种可能是母线电压超过 36V 的共模上限。解决办法是按第 3 章的表重新核算采样电阻把 5A 时的压降控制在满量程的 60% 左右母线超压则必须改前端方案不能靠软件补。6. 验收与答辩怎么证明这块板子测得准方案做完最怕的是能出数但说不清准不准。验收要拿出可复现的数据不是一句误差很小。我一般按三步走。第一步零点验收断开负载只给母线电压记录 10 分钟的电流读数看零点漂移范围判据是全程波动不超过 ±3 个 LSB10mΩ 下约 ±0.75mA。第二步多点线性验收用电子负载从 0.2A 到 5A 取 6 个点每个点稳定 30 秒后记录 10 次读数的平均值和上位机标准表的读数比对算相对误差。第三步温漂验收满载连续跑 30 分钟每 5 分钟记录一次看读数随温度的变化趋势。验收表可以直接照这个格式填验收项方法判据零点漂移空载 10 分钟波动 ≤ ±3 LSB线性误差0.2A~5A 六点比对各点相对误差 ≤ 2%温漂满载 30 分钟末次与首次偏差 ≤ 1%刷新率示波器测 ALERT 或读寄存器与配置的转换时间一致线性误差的判据定在 2%是因为采样电阻本身 1% 的精度就占了误差预算的大头再叠加 INA226 的增益误差和走线电阻做到 1% 以内需要挑选电阻并且做单点校准。答辩时主动把这个误差预算讲出来——哪一项占多少、哪一项能优化——比硬报一个漂亮数字更有说服力。答辩还有两个常被问到的点。一是为什么不用 MCU 内部 ADC回答落在误差源数量和标定成本上别只说精度高。二是采样电阻为什么选 10mΩ把压降、功耗、满量程三者的取舍摆出来顺带说明 20mΩ 会超量程这个问题就闭环了。从那以后我每次做这类小信号采集的板子都会在投板前把开尔文取样点和去耦位置单独画一张局部图让另一个人照着图找一遍错。这张图花不了十分钟但能挡掉后面几天的返工。希望帮到你。本文还有配套的精品资源点击获取