简介INA226.rar 是一份面向嵌入式开发者的完整驱动工程围绕 TI INA226 高精度电流/功率监控芯片在 STM32 平台上的接入与数据采集展开适合正在做电源管理、电池管理、负载检测或传感器课程设计的中初级开发者参考。压缩包共 151 个文件约 2.97MB包含 36 个 h 头文件、35 个 c 源文件以及 Keil MDK 工程文件、编译中间文件、hex 输出和说明文本等源码主要承担寄存器配置、I2C 通信、测量启动与数据读取工程文件便于直接编译调试。资源已有 4713 人学习价值在于提供可运行的软硬件交互范例读者可据此理解 INA226 的配置流程、电压电流功率换算、STM32 外设初始化与工程组织方式也能参考其从初始化到读取的完整链路并在此基础上改造成自己的监测方案或排错参照对课程设计、毕业设计或项目原型验证都较有参考意义。1. INA226 不是“读个电压”那么简单从一块电流监测板说起INA226 这颗电流/功率监测芯片配上 STM32 和 C 语言工程在 Keil MDK 里跑通并不难难的是把读数做稳。它把分流电压、总线电压、电流和功率都放进寄存器I2C 一读就能出数适合做电源监测、电池充放电、电机电流采样这类传感器场景。不少人第一次接上模块串口能打印出 0.00A 就以为成了换个负载才发现小数点后全是噪声。INA226 的坑不在“能不能读到”而在“读到的数能不能信”分流电阻、校准寄存器、平均次数和采样周期任何一处对不上结果就会偏。这份 INA226.rar 工程把驱动、校准和采样任务串在一起适合已经会点灯、能建 Keil MDK 工程但一碰传感器就卡在时序和校准上的人。先别急着抄代码把分流电阻和寄存器之间的关系理清后面少翻车。2. 硬件连接与 I2C 时序先让 STM32 认到 0x40INA226 的硬件连接不复杂VCC 接 3.3VGND 共地SCL/SDA 各挂一只上拉电阻分流电阻串在待测回路里A0/A1 决定 I2C 地址。真正容易出问题的是分流电阻选型和地址字节前者决定量程和分辨率后者决定 STM32 能不能在总线上找到它。2.1 分流电阻与量程的选型账INA226 测电流的方式是测分流电阻两端的压降分流电压满量程是 ±81.92mV。选 Rshunt 时先看最大电流和允许功耗再留 20%30% 余量。比如最大 2A用 10mΩ满量程压降 20mV电阻功耗 40mW0603 或 0805 都能扛如果最大 5A同样 10mΩ 压降 50mV功耗 250mW最好用 2512 封装且选低温漂的合金电阻。最大电流分流电阻满量程分流电压电阻功耗常见处理2A10mΩ20mV40mW0805 合金电阻Kelvin 走线5A10mΩ50mV250mW2512 封装注意散热10A5mΩ50mV500mW四线开尔文远离热源20A2mΩ40mV800mW专用分流器铜排连接分流电阻不是越小越好。阻值太小小电流下压降只有几十微伏INA226 的 2.5μV LSB 还能分辨但板上的热电动势和运放失调会把它淹没阻值太大功耗和压降上去了负载端电压被拉低测出来的电流也会随温度漂。常见做法是让最大电流对应的分流电压落在 40mV60mV既留了余量又不会让功耗失控。PCB 上一定要做开尔文连接。分流电阻两个电流脚走大电流两个电压采样脚单独引到 INA226 的 IN 和 IN-采样线从电阻焊盘内侧引出不要和电流路径共用一段铜箔。否则那段铜箔的电阻会直接叠进测量结果几毫欧的走线电阻在 10A 下就是几十毫伏的误差校准寄存器怎么调都救不回来。注意分流电阻的功率降额要按实际工作温度算。标称 1W 的 2512 电阻在 70℃ 环境里可能只能用到一半长期跑 5A 以上时摸上去烫手不是正常现象。2.2 STM32 的 I2C 外设配置与扫描地址STM32 这边用硬件 I2C 或软件模拟都可以INA226 支持 100kHz 和 400kHz。板上总线不长时用 400kHz 能缩短读取时间但上拉电阻要跟着调常见 4.7kΩ 对应 100kHz400kHz 可以降到 2.2kΩ 左右。上拉接到 3.3V不要接到 5VINA226 的 IO 耐压虽然不低但和 STM32 共总线时没必要冒这个险。INA226 的 7 位地址由 A0/A1 决定默认都接地是 0x40。用 HAL 库时传入的是左移一位后的 8 位地址也就是 0x80。很多人写HAL_I2C_Mem_Read(hi2c1, 0x40, ...)读不到就是因为把 7 位地址直接塞进了 8 位参数。先跑一遍总线扫描确认 0x40 能应答再往下写寄存器。#include stdio.h #include i2c.h /* 扫描 7 位地址打印能应答的器件地址 */ uint8_t i2c_scan(I2C_HandleTypeDef *hi2c) { uint8_t found 0; for (uint8_t addr 0x08; addr 0x78; addr) { /* HAL 接口要传 8 位地址所以左移一位 */ if (HAL_I2C_IsDeviceReady(hi2c, (uint16_t)(addr 1), 3, 10) HAL_OK) { printf(I2C device found: 0x%02X\r\n, addr); found; } } return found; }这段扫描代码的逻辑很直接从 0x08 到 0x77 逐个发地址HAL_I2C_IsDeviceReady返回HAL_OK说明从机应答。第三个参数是重试次数给 3 次能过滤掉偶发干扰第四个参数是超时单位毫秒10ms 对 100kHz 总线足够。扫描到 0x40 后下一步不是马上读电流而是先读配置寄存器 0x00看返回值是不是复位默认值 0x4127确认写保护、电源和总线都正常。如果扫描不到先量 SCL/SDA 的静态电平正常应该被上拉到 3.3V再量 INA226 的 VCC 和 GND别只看模块上的丝印最后确认 A0/A1 的电平有些模块把地址脚焊死了丝印写的 0x40 实际可能是 0x41。总线扫描是排错的第一道黑匣子别跳过它直接怀疑芯片坏了。3. 寄存器配置与校准把原始码变成安培和瓦特INA226 读出来的是 16 位原始码不写校准寄存器电流寄存器就是一堆没有单位的数。配置寄存器决定平均次数和转换时间校准寄存器决定电流 LSB两者配合才能把原始码换成安培。顺序上先写配置、再写校准最后读电流中间留够一次转换时间。3.1 配置寄存器 0x00 的四个关键位配置寄存器 0x00 的 bit15 是复位位写 1 会软复位bit119 是 AVG决定平均次数bit86 是 VBUSCT总线电压转换时间bit53 是 VSHCT分流电压转换时间bit20 是 MODE选择连续或触发模式。常用值是 AVG216 次平均、VBUSCT41.1ms、VSHCT41.1ms、MODE7分流和总线连续转换合起来是 0x0527。位段名称本例取值含义bit15RST0不复位bit119AVG216 次平均bit86VBUSCT4总线转换 1.1msbit53VSHCT4分流转换 1.1msbit20MODE7分流总线连续转换按这个配置一次分流加总线转换约 2.2ms16 次平均后约 35ms 才更新一轮数据。采样任务如果设成 5ms 读一次读到的多半还是旧值看起来像“数据不刷新”。我一般把采样周期设在 50ms 以上或者把 AVG 降到 4 次先用短周期看趋势稳定后再加平均。#define INA226_ADDR (0x40 1) #define INA226_REG_CONFIG 0x00 #define INA226_REG_CALIB 0x05 static HAL_StatusTypeDef ina226_write_u16(uint8_t reg, uint16_t val) { uint8_t buf[2]; buf[0] (uint8_t)(val 8); buf[1] (uint8_t)(val 0xFF); return HAL_I2C_Mem_Write(hi2c1, INA226_ADDR, reg, I2C_MEMADD_SIZE_8BIT, buf, 2, 100); } void ina226_config(void) { /* AVG16, VBUSCT1.1ms, VSHCT1.1ms, 连续分流总线 */ uint16_t config (2u 9) | (4u 6) | (4u 3) | 0x7u; ina226_write_u16(INA226_REG_CONFIG, config); }这段代码只写了配置寄存器config的计算按位段拼出来比直接写 0x0527 更容易改。HAL_I2C_Mem_Write的第四个参数是寄存器地址长度INA226 是 8 位寄存器地址所以用I2C_MEMADD_SIZE_8BIT超时给 100ms避免总线被拉死时卡在中断里。写完后建议回读一次 0x00确认值确实是 0x0527有些模块的 I2C 上拉太弱写进去的值会丢。3.2 校准寄存器 0x05 与电流 LSB 的推导电流 LSB 是电流寄存器最低位代表的安培数通常取最大预期电流除以 32768。比如最大 2ACurrent_LSB 2 / 32768 ≈ 61.035μA。校准寄存器 CAL 的公式是CAL 0.00512 / (Current_LSB × Rshunt)其中 0.00512 是芯片内部固定值Rshunt 单位是欧姆。2A、10mΩ 代入CAL 0.00512 / (61.035e-6 × 0.01) ≈ 8389。这个值要写进 0x05且必须在配置寄存器之后写。CAL 是 16 位无符号数写 0 或写错单位都会让电流寄存器输出 0 或满量程。有人把 10mΩ 写成 10算出来的 CAL 小了 1000 倍读出来的电流也小 1000 倍查半天以为是传感器坏了。static float g_current_lsb 0.0f; void ina226_calibrate(float r_shunt_ohm, float i_max_a) { /* 电流 LSB 单位A/bit */ g_current_lsb i_max_a / 32768.0f; /* CAL 公式中的 0.00512 是固定常数Rshunt 单位欧姆 */ float cal_f 0.00512f / (g_current_lsb * r_shunt_ohm); uint16_t cal (uint16_t)(cal_f 0.5f); ina226_write_u16(INA226_REG_CALIB, cal); }r_shunt_ohm传 0.01 而不是 10i_max_a传 2.0 而不是 2000单位统一成安培和欧姆后公式才不会错。cal_f 0.5f是四舍五入直接截断在临界值上会差 1 个 LSB。写完后把g_current_lsb存成全局变量后面读电流直接乘不要每次重算否则改了分流电阻忘了同步数据会悄悄偏掉。3.3 连续读取与数据就绪标志电流寄存器 0x04 是有符号 16 位读出来先转int16_t再乘g_current_lsb总线电压寄存器 0x02 是无符号LSB 固定 1.25mV功率寄存器 0x03 是无符号LSB 是25 × Current_LSB。分流电压寄存器 0x01 也常用LSB 2.5μV适合看小电流下的原始噪声。static uint16_t ina226_read_u16(uint8_t reg) { uint8_t buf[2] {0}; if (HAL_I2C_Mem_Read(hi2c1, INA226_ADDR, reg, I2C_MEMADD_SIZE_8BIT, buf, 2, 100) ! HAL_OK) { return 0; } return ((uint16_t)buf[0] 8) | buf[1]; } float ina226_read_current_a(void) { int16_t raw (int16_t)ina226_read_u16(0x04); return raw * g_current_lsb; } float ina226_read_bus_voltage_v(void) { uint16_t raw ina226_read_u16(0x02); return raw * 0.00125f; }读电流时先做强转int16_t因为反向电流会表现为补码负数直接按uint16_t乘会得到几万安培的离谱值。总线电压的 0.00125f 是 1.25mV 的浮点写法不要写成 1.25否则读出来是千伏级。读失败返回 0 只是兜底实际工程里要把HAL_StatusTypeDef往外传让上层决定是重试还是报警。数据就绪标志在屏蔽/使能寄存器 0x06 的 bit10连续模式下可以不查靠固定周期读触发模式下必须等这一位置 1 再读否则读到的是上一次的旧数据。我一般在连续模式里也保留一次回读 0x06 的动作初始化阶段确认转换真的在跑比盲等 50ms 踏实。4. Keil MDK 工程落地C 语言驱动分层与采样任务Keil MDK 里最怕把所有东西塞进 main.cINA226 的驱动、校准参数和业务采样混在一起改一个分流电阻要翻三处。比较稳的做法是分三层ina226.c/h只负责寄存器和原始码转换app_power.c/h做滤波和告警main.c只调用初始化、启动定时器、跑主循环。C 语言工程不需要多复杂但接口要清楚。4.1 驱动文件划分与接口定义ina226.h里暴露初始化、读电流、读总线电压、读功率四个接口寄存器地址和 I2C 句柄放在.c里上层不直接碰HAL_I2C_Mem_Read。这样换 I2C 外设或改地址时只动一个文件。Keil MDK 的 C/C 选项里勾选 C99包含路径加上驱动目录ina226.c加进工程组编译时就不会报隐式声明。#ifndef INA226_H #define INA226_H #include stdint.h void ina226_init(float r_shunt_ohm, float i_max_a); float ina226_read_current_a(void); float ina226_read_bus_voltage_v(void); float ina226_read_power_w(void); uint8_t ina226_is_ready(void); #endif头文件只放函数声明和stdint.h不把i2c.h暴露出去避免上层工程被 HAL 头文件缠住。ina226_init的两个参数就是分流电阻和最大电流调用时写ina226_init(0.01f, 2.0f)和硬件选型表对上。ina226_is_ready返回 0/1业务层可以用它做超时判断。ina226.c里定义#define INA226_ADDR (0x40 1)把配置和校准写进ina226_init读功率可以直接读 0x03 乘功率 LSB也可以用电流 × 总线电压自己算。前者省事后者方便做交叉验证。我一般两个都留调试时打印出来对比差太多说明校准或量程有问题。4.2 定时采样与滑动平均滤波采样周期要大于一次完整转换时间。按 3.1 的配置16 次平均约 35ms定时器可以设 50ms 一次。滑动平均窗口取 8 或 16窗口太大响应慢太小滤波效果不明显。用环形缓冲区实现避免每次搬数据。#define FILTER_N 16 static float s_current_buf[FILTER_N]; static uint8_t s_index 0; static uint8_t s_count 0; float current_filter(float new_sample) { s_current_buf[s_index] new_sample; s_index (s_index 1) % FILTER_N; if (s_count FILTER_N) { s_count; } float sum 0.0f; for (uint8_t i 0; i s_count; i) { sum s_current_buf[i]; } return sum / s_count; }环形缓冲区只覆盖已写入的s_count个点上电初期不会把 0 算进平均否则前几秒电流会明显偏低。FILTER_N取 16 时50ms 采样对应 800ms 的平滑窗口适合看电池充放电趋势如果要抓电机启动电流把窗口降到 4或者直接去掉滤波看原始值。滤波只对电流做总线电压本身比较稳做不做影响不大。定时器回调里不要直接调用HAL_I2C_Mem_ReadI2C 是阻塞操作放在中断里会拖慢其他任务。常见做法是回调里置标志位主循环查到标志再读。读回来的原始电流送current_filter滤波结果存到全局结构体串口打印和告警都用这一份避免多处读取导致数据不一致。4.3 功率与告警的二次计算功率寄存器 0x03 的 LSB 是25 × Current_LSB2A/10mΩ 配置下约 1.525mW。读出来乘这个系数就是瓦特。也可以直接用滤波后的电流乘总线电压两种算法在稳态下应该很接近差超过 2% 就要查校准寄存器是不是写对了。#define OVER_CURRENT_A 1.8f #define UNDER_VOLTAGE_V 3.0f typedef struct { float current_a; float bus_v; float power_w; uint8_t over_current; uint8_t under_voltage; } power_status_t; void power_task(power_status_t *st) { float raw_i ina226_read_current_a(); st-current_a current_filter(raw_i); st-bus_v ina226_read_bus_voltage_v(); st-power_w st-current_a * st-bus_v; st-over_current (st-current_a OVER_CURRENT_A) ? 1 : 0; st-under_voltage (st-bus_v UNDER_VOLTAGE_V) ? 1 : 0; }阈值用宏定义改的时候不用翻业务代码。过流判断用滤波后的电流避免尖峰误触发如果要做硬件保护还是得靠比较器或 MCU 的模拟看门狗软件告警有采样周期延迟。欠压判断放在功率计算之后母线掉电时电流也会跟着掉先看电压能区分“负载关了”和“电源塌了”。5. 避坑与排查读不到、跳变、温漂的六条血泪经验INA226 的排查顺序和普通 I2C 传感器差不多先看总线有没有应答再看寄存器写得对不对最后看模拟前端和校准。下面五条是实际工程里最容易翻车的地方每条按现象、原因、解决写照着顺序排能省不少时间。5.1 现象I2C 扫描不到 0x40现象是HAL_I2C_IsDeviceReady一直返回HAL_ERROR示波器上看 SCL 有波形SDA 偶尔被拉低。原因通常是地址字节没左移、上拉电阻缺失、INA226 的 VCC 没到 3.3V或者 A0/A1 被模块焊到了非默认电平。有人把 7 位地址 0x40 直接传给 HALHAL 再左移一次变成 0x80实际发出去的是 0x80 地址当然不应答。解决方式是先跑 2.2 的扫描代码确认总线上有没有 0x40 或其他地址。如果扫到 0x41说明 A0 被拉高改INA226_ADDR就行。扫不到就量电压VCC 对 GND 是不是 3.3VSCL/SDA 静态是不是 3.3V上拉电阻是不是 4.7kΩ 左右。还不行就断开 INA226只留 STM32 和上拉用扫描代码看总线能不能释放排除总线被拉死。注意有些模块把 I2C 上拉电阻和 LED 做在一起LED 亮的时候 SDA 电平会被拉低扫描时好时坏。先把无关负载断开再测。5.2 现象电流值固定为 0 或满量程现象是电流寄存器读出来一直是 0或者一直是 32767 附近的数换负载也不变。原因多半是校准寄存器没写、CAL 写成了 0、Current_LSB 单位算错或者分流电阻值传参时把 10mΩ 写成 10Ω。CAL 为 0 时电流寄存器不更新读出来就是 0CAL 过大时一点点分流电压就顶到满量程。解决方式是打印 CAL 和g_current_lsb2A/10mΩ 时 CAL 应该在 8389 左右g_current_lsb约 0.000061。如果 CAL 是 8389000说明 Rshunt 传成了 0.00001 或者电流 LSB 少除了 1000。还要确认写 CAL 之前配置寄存器已经写成功有些模块写配置后需要 1ms 才接受校准写入中间加个短延时更稳。5.3 现象小电流跳动大现象是 100mA 以下电流读数在 0.08A0.12A 之间跳负载稳定但数字不稳。原因是分流电阻太小100mA 在 10mΩ 上只有 1mV除以 2.5μV 的 LSB 只有 400 个码再被电源纹波和 ADC 噪声一搅低位全是抖动。平均次数低、采样周期短也会放大这个问题。解决方式是先加大分流电阻比如 2A 量程用 20mΩ100mA 对应 2mV码值翻倍再把 AVG 提到 64 或 128 次采样周期放到 100ms 以上。电源端加 10μF 加 100nF 去耦分流电阻采样线远离 DC-DC 电感。如果还要更快响应就别硬压噪声改用硬件比较器做过流保护INA226 只负责监测。5.4 现象长时间运行后读数漂移现象是刚上电校准完很准跑半小时后电流偏了 3%5%环境温度越高越明显。原因是分流电阻温漂、电阻自热、PCB 铜箔热电动势或者 INA226 靠近发热器件。普通厚膜电阻的温度系数可能到 200ppm/℃10mΩ 在 50℃ 温升下阻值变化 0.1%听起来不大但满量程 2A 时就是 2mA 的固定偏差。解决方式是换低温漂合金电阻温度系数 50ppm/℃ 以内功率降额至少 50%。分流电阻下面铺铜散热但采样线不要从散热铜箔上取。软件上可以做温度补偿用板上的温度传感器读环境温度按电阻温度系数修正增益没有温度传感器就做定期零点校准在已知零电流时记下偏移。5.5 现象Keil 下载后第一次正常复位后异常现象是 Keil MDK 下载完程序串口数据正常按复位键或断电重上电后电流读数为 0 或乱跳。原因是初始化顺序依赖了调试器的复位状态或者 INA226 没有软复位配置寄存器还留着上一次的值也可能是校准参数存在 RAM 里复位后没重新写。解决方式是在ina226_init开头先写配置寄存器 bit15 做软复位延时 1ms再写配置和校准。不要在main里先读电流再初始化读到的可能是上一次的残留值。复位后把 CAL 回读一遍确认和写入值一致。如果用了看门狗注意 I2C 阻塞超时不要设太长否则复位循环里卡在HAL_I2C_Mem_Read出不来。6. 进阶验证用已知负载和示波器把误差压到 1% 以内校准寄存器算得再准也只是纸面值。真正上板后分流电阻的的实际阻值、走线电阻和焊接应力都会带来偏差想压到 1% 以内得用已知负载做两点校准。先断掉负载读零电流偏移再接一个稳定电子负载读满度附近的值算增益和偏移比只写公式靠谱。/* 两点校准zero_raw 为零电流原始码full_raw 为已知负载原始码 */ float current_calibrated(int16_t raw, int16_t zero_raw, int16_t full_raw, float full_current) { float gain full_current / (float)(full_raw - zero_raw); return (raw - zero_raw) * gain; }这段代码里zero_raw要在负载断开、母线有电但无电流时读full_raw在电子负载设到 80% 量程时读full_current是负载实际值。增益算出来后小电流和大电流都按同一条直线修正。注意零点会随温度漂冷机校准完最好跑 10 分钟再复校一次。设定电流读回电流误差处理0.1A0.102A2.0%检查零点偏移0.5A0.503A0.6%在允许范围1.0A1.004A0.4%记录增益1.5A1.507A0.5%观察温升2.0A2.012A0.6%降额使用阶梯验证时每个点稳定 30 秒再记数同时用示波器看分流电阻两端的纹波纹波超过 5mV 就先处理电源别急着改校准。温度补偿只在宽温场景需要普通室内设备把低温漂电阻和两点校准做好1% 以内不难。从那以后我每次拿到新的分流电阻都先跑一遍零点和满度两点校准再上板省得后面把软件翻个底朝天。希望帮到你。本文还有配套的精品资源点击获取