简介INA226.rar 是一套基于 STM32 的 INA226 高精度电流与功率传感器驱动完整工程面向嵌入式开发、电源监控与电池管理场景适合具备 C 语言和 Keil MDK 使用经验的中级开发者也可作为电子竞赛或课程设计的参考工程。压缩包共 151 个文件约 2.97MB以 36 个 h 头文件与 35 个 c 源文件为核心同时包含 Keil 工程配置uvprojx、编译产物hex、axf、map及调试辅助文件dep、crf目录结构清晰便于按模块检索。源码依托 stm32f10x 标准外设库覆盖 USART、I2C、ADC、TIM、Flash、RCC 等模块可参考 INA226 驱动的 I2C 读写、寄存器配置、电流电压采样与功率换算流程工程内保留链接脚本、描述文件、列表文件等构建细节并留有 USART 输出相关代码利于对照硬件调试与串口观察。已有 4690 人学习下载适合需要快速上手高精度电流监控、借鉴成熟驱动设计或开展电源状态监测的嵌入式开发者阅读、复用与二次移植。 前阵子清理网盘翻出一个名为“INA226.rar”的压缩包里面是中文手册、几张模块原理图还有几份别人写的demo工程。正好手头在给一台小设备做供电监测就把它捡起来认真研究了一周最终用STM32F4的HAL库把电压、电流、功率全部稳定读了出来。这篇就把整个过程中的关键点整理出来包含芯片工作原理、硬件接线、寄存器校准、代码示例和实测踩坑记录希望能帮到正在用INA226做电压电流采集的朋友。1. 拿到INA226.rar之后先搞懂这颗芯片到底能做什么很多人在网上搜“INA226中文手册”下载完对着寄存器表一头雾水。其实这颗芯片的核心能力概括起来就一句话它能在最高36V的电源轨上同时高精度测量负载电压和流过采样电阻的电流并通过I2C接口把结果交给MCU。和传统的“电阻分压单片机ADC”方案相比INA226内置16位ADC和可编程增益放大器测出来的数据稳定性和精度都要好得多。1.1 INA226的定位和适用场景INA226是TI推出的一款高侧/低侧电流感测芯片供电电压2.7V到5.5V自身功耗很低适合电池供电的设备。它的典型应用包括电池放电容量测试比如分析18650电芯的放电曲线太阳能板输出功率监测配合MPPT算法判断发电状态电机驱动板工作电流监测检测堵转和过载多路负载电源管理实时掌握每一路的电压和功耗我这次的项目是做一台小型直流负载的电压电流监控要求0到5A量程电压12V左右精度尽量高。用INA226正好能满足需求I2C接口接STM32F4也很省事。1.2 为什么不用单片机片上ADC或者INA219第一共模电压问题。单片机的ADC通常只能测0到3.3V的电压而高侧采样时采样电阻两端的电压都悬在电源电压上比如12V电源轨单片机根本没法直接测。INA226的差分输入能够承受较高的共模电压内部PGA会把采样电阻上几十毫伏的电压差放大后再送给ADC。第二数据稳定性。如果你用STM32F4自带的ADC配合外部运放做电流采集不仅元件数量多而且PCB布局不好容易引入工频干扰。INA226把放大、ADC转换、计算功率全做在芯片内部16位分辨率实测数据抖动量很小。第三和INA219对比。INA226和INA219都支持I2C但INA219的VBUS最高只能到26VINA226可以到36VINA226的ADC位数是16位总线电压的LSB是1.25mV电流和功率参数也比INA219丰富一些。如果项目需要测2节到8节锂电池串联的电压INA226明显更合适。2. 模块与硬件接线前必须搞清楚的引脚和电阻我拿到的INA226.rar里有一份模块原理图模块很小丝印上有VS、GND、VBUS、IN、IN-、SCL、SDA等引脚。第一次用的时候千万别急着上电先把引脚功能理清楚否则很容易把采样电阻烧掉。2.1 引脚功能与模块接线常见INA226模块的引脚如下引脚功能接线建议VS芯片电源接3.3V或5V注意不要超过5.5VGND地接系统GNDVBUS总线电压检测输入接负载的电源正极不是接芯片VSIN分流电阻高电位端接采样电阻靠近电源侧IN-分流电阻低电位端接采样电阻靠近负载侧SCLI2C时钟线接MCU的SCL注意上拉电阻SDAI2C数据线接MCU的SDA注意上拉电阻A0/A1地址配置引脚接GND或VS共16种组合ALERT报警输出可选用于限值报警提醒这里特别提醒一个容易搞错的地方VBUS引脚是用来测量总线电压的它应该接在要测量的电源轨正极而不是直接接INA226自己的VS供电脚。我给小型设备做监测时把VS接3.3VVBUS接被测的12V电源轨芯片内部的电阻分压网络会把12V降到ADC可以处理的范围内。高侧接法的示意图如下电源正极接分流电阻一端分流电阻另一端接负载正极同时把INA226的IN接电源侧IN-接负载侧VBUS也接在负载侧位置。这个接法能看到负载端的实际电压也能算出分流电阻上的压降。如果做低侧采样就把分流电阻放在负载的回路地端IN接负载地侧IN-接电源地侧好处是共模电压接近0V缺点是会抬高系统地的参考电位干扰敏感电路。2.2 分流电阻选型和功耗计算分流电阻的阻值直接决定了测量范围和发热量。电阻越大同样电流下产生的压降越大芯片的分辨率越高但压降过大会造成负载端电压跌落电阻自身功耗也上升。常用公式是P I² × R比如量程5A选择10mΩ分流电阻最大功耗是5² × 0.01 0.25W如果电阻封装功率余量不足长期工作会发热温漂会让阻值飘动电流读数就不准。我习惯选择额定功率2倍以上的电阻比如0.25W功耗至少选0.5W封装。另外阻值温漂也很关键普通厚膜电阻温漂可能到200ppm/°C合金电阻一般能做到50ppm/°C左右。做电池容量测试时电流比较大我会故意选阻值稍小的分流电阻比如5mΩ或2mΩ把发热降到最低。PCB布局上分流电阻和INA226的IN/IN-之间的连线要尽量短最好采用开尔文接法就是让电流流过的路径和电压采样路径分开走线。如果直接从大电流走线上引出细线到芯片细线上的铜箔电阻会影响测量结果尤其在大电流下误差会更明显。这个点很多人忽视实测下来影响非常大。3. 核心寄存器与校准决定测量精度的关键一步INA226使用起来并不复杂但要想读出来的电流和功率准确校准寄存器必须设置正确。这一节是整套方案里最容易出问题的地方我仔细写过很多次代码踩过的坑基本都集中在这里。3.1 寄存器地图与数据换算INA226的寄存器都是16位通过I2C访问寄存器地址从0x00到0x07。常用几个如下寄存器地址名称说明0x00Configuration配置平均模式、转换时间、工作模式0x01Shunt Voltage分流电压原始值带符号16位0x02Bus Voltage总线电压原始值LSB1.25mV0x03Power功率计算结果LSB25倍电流LSB0x04Current电流计算结果LSB等于设置的电流LSB0x05Calibration校准寄存器决定电流和功率精度0x06Manufacturer ID厂商IDINA226固定为0x54490x07Device ID设备IDINA226固定为0x2260配置寄存器0x00的默认值是0x8000表示关机模式。上电后需要写入合适的配置比如启用连续转换模式、设置平均次数、设定ADC转换时间。一般我会写0x4527意思是连续测量分流电压和总线电压平均16次每条通道转换时间约1052µs。如果要求响应快可以改成平均4次甚至1次转换时间也会缩短到几百微秒。数据换算时注意几个LSB值分流电压0x01默认PGA增益下1 LSB 2.5µV带符号要先把u16转成i16再计算总线电压0x021 LSB 1.25mV读取出来乘以1.25就是毫伏数电流0x041 LSB取决于你自己设定的Current_LSB带符号功率0x031 LSB 25 × Current_LSB带符号很多人在总线电压上吃过亏因为寄存器低3位永远是0。直接把0x02寄存器右移3位再乘以10mV也是等价的写法。我习惯用原始值乘以1.25mV逻辑更直白不容易把自己绕晕。3.2 校准寄存器的计算公式与实例校准寄存器是最容易懵的地方。INA226内部的计算逻辑是芯片自动读取分流电压乘上校准值再除以一个固定系数得到电流寄存器的值。所以我们必须告诉芯片两个信息采样电阻的阻值以及我们希望电流寄存器的LSB是多少。先说这个LSB的选择。INA226的电流寄存器是16位带符号数最大正数32767你希望测量的最大电流Imax要满足Imax Current_LSB × 32767通常的做法是Current_LSB Imax / 32768算出来之后为了凑一个整齐的数可以向上取到一个容易记忆的数值比如100µA、1mA。Current_LSB不能太小否则校准寄存器计算出来的值会超过0xFFFF芯片写不进去。校准值的计算公式是Calibration 0.00512 / (Current_LSB × Rshunt)注意这里0.00512是芯片内部的魔法系数Current_LSB单位用安培Rshunt单位用欧姆。算出来取整写入0x05寄存器。举一个实际例子。我的项目量程5A选10mΩ分流电阻Imax 5ACurrent_LSB 5 / 32768 ≈ 152.6µA取整为200µA更合适也就是0.0002A这样最大电流可以到0.0002 × 32767 ≈ 6.55A留了余量Calibration 0.00512 / (0.0002 × 0.01) 2560那么电流寄存器读到的值乘以0.0002就是实际电流功率寄存器读到的值乘以25 × 0.0002 0.005W就是实际功率。这里有个细节我反复提醒自己Calibration值必须取整取整带来的微小误差会导致电流读数轻微偏差。如果项目对精度要求极其苛刻可以先用这个LSB试算得到一个Calibration值后反推实际电流LSB再用实测值做最终标定。对大多数嵌入式项目来说取整误差在可接受范围内不需要过度纠结。4. STM32F4HAL库实战I2C读取INA226的完整代码如果只是用现成模块连着玩读几个寄存器很简单。但要在工程里稳定跑起来I2C时序、寄存器读写顺序、数据解析这几个环节都得处理利索。下面是我在STM32F407上验证过的代码思路。4.1 硬件I2C初始化我用的CubeMX生成I2C1初始化速率设为400kHz。INA226支持快速模式400kHz完全没问题。初始化代码大致如下I2C_HandleTypeDef hi2c1; void MX_I2C1_Init(void) { hi2c1.Instance I2C1; hi2c1.Init.ClockSpeed 400000; hi2c1.Init.DutyCycle I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 0; hi2c1.Init.AddressingMode I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 0; hi2c1.Init.GeneralCallMode I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(hi2c1) ! HAL_OK) { Error_Handler(); } }INA226的7位地址默认是0x40A0和A1都接地。在STM32的HAL库里传参时需要用8位地址形式也就是左移一位#define INA226_ADDR (0x40 1)这个地址一定要用左移后的值很多新手在这里栽跟头I2C扫描不到设备改过来立刻通了。4.2 读写函数与数据处理INA226的寄存器读写都是先发寄存器地址然后连续传2字节数据数据是大端格式先传高字节再传低字节。我用HAL库封装了读写函数#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 static HAL_StatusTypeDef INA226_ReadReg(uint8_t reg, uint16_t *value) { uint8_t buf[2]; HAL_StatusTypeDef status HAL_I2C_Mem_Read(hi2c1, INA226_ADDR, reg, I2C_MEMADD_SIZE_8BIT, buf, 2, 100); if (status HAL_OK) { *value ((uint16_t)buf[0] 8) | buf[1]; } return status; } static HAL_StatusTypeDef INA226_WriteReg(uint8_t reg, uint16_t value) { uint8_t buf[2]; buf[0] (uint8_t)(value 8); buf[1] (uint8_t)(value 0xFF); return HAL_I2C_Mem_Write(hi2c1, INA226_ADDR, reg, I2C_MEMADD_SIZE_8BIT, buf, 2, 100); }初始化函数中先写配置寄存器再写校准寄存器#define SHUNT_RESISTOR 0.01f #define CURRENT_LSB 0.0002f void INA226_Init(void) { uint16_t cal; INA226_WriteReg(INA226_REG_CONFIG, 0x4527); cal (uint16_t)(0.00512f / (CURRENT_LSB * SHUNT_RESISTOR)); INA226_WriteReg(INA226_REG_CAL, cal); }读取数据时注意电流和功率寄存器要当成带符号数处理void INA226_ReadAll(float *voltage, float *current, float *power) { uint16_t raw; int16_t shunt_raw, current_raw, power_raw; uint16_t bus_raw; INA226_ReadReg(INA226_REG_SHUNT, raw); shunt_raw (int16_t)raw; INA226_ReadReg(INA226_REG_BUS, raw); bus_raw raw; INA226_ReadReg(INA226_REG_CURRENT, raw); current_raw (int16_t)raw; INA226_ReadReg(INA226_REG_POWER, raw); power_raw (int16_t)raw; *voltage (float)bus_raw * 0.00125f; *current (float)current_raw * CURRENT_LSB; *power (float)power_raw * 25.0f * CURRENT_LSB; }总线电压的换算也可以用移位方式把0x02寄存器右移3位再乘以10mV。两种写法结果一样我选了乘1.25mV的方案代码里看起来更直观。这里再补充一个经验如果你嫌HAL库的I2C在某些频率太高或者中断优先级配置麻烦也可以用软件模拟I2C就是GPIO翻转 延时函数。INA226时序并不复杂400kHz协议速度用模拟I2C也能稳定跑。前提是让GPIO带上拉输入模式SCL、SDA各接4.7kΩ上拉电阻并且不要在主循环里一直轮询否则会卡住其他任务。5. 实测遇到的问题与排查心得任何芯片都要实测才能暴露问题。我在这套方案上前后调试了两三天遇到过几个典型问题列个速查表供参考遇到类似现象能少走很多弯路。5.1 常见问题速查表问题现象可能原因排查思路I2C扫描不到设备地址传参错误、模块没供电、SDA/SCL接反确认是0x80而不是0x40测VS电压检查上拉电阻读回全0xFFI2C地址不匹配、芯片没工作用IO扫描地址检查VS是否在上限内总线电压读数很大或很小VBUS接错位置、寄存器换算用错LSBVBUS应接负载正极确认用1.25mV而不是10mV电流一直为0校准寄存器没写、分流电阻太小、IN/IN-接反读0x05确认不是0增加负载电流交换IN/IN-电流读数为负IN和IN-接反或者电流方向是反向放电检查方向负值属于正常放电状态功率数值离谱校准值算错、功率LSB用错重算0x00512公式确认Power_LSB25×Current_LSBALERT引脚乱拉低限值寄存器配置不生效或触发先忽略ALERT或读0x00确认报警屏蔽位排查I2C问题时建议先读寄存器的厂商ID和设备ID。INA226的Manufacturer ID固定是0x5449Device ID固定是0x2260。如果这两个值能读出来说明I2C通信完全正常问题大概率出在寄存器配置或接线逻辑上。这一步看起来基础但能一下缩小问题范围比盲猜快得多。5.2 几个容易踩的坑第一个坑是模块自带的采样电阻阻值不确定。市面上的INA226模块默认往往焊了一个10mΩ电阻但也有模块用的是5mΩ或0.1Ω。我一开始想当然用了10mΩ计算校准值实际一测模块上是5mΩ结果电流读数直接偏了一倍。后来老老实实用万用表量了模块焊盘位置的实际阻值再代入公式数据立刻准确了。第二个坑是VBUS和VS的接线。VS只是芯片自己的电源而VBUS才是测量电压的采样输入。如果为了省事把VBUS直接接到3.3V上那你测到的永远是3.3V而不是实际负载电压。正确做法是VBUS接到被测电源轨上用电阻分压网络在内部完成降压芯片本身并不从VBUS取电。第三个坑是分流电阻的布局。之前调试时我用杜邦线把IN/IN-从分流电阻上引出来结果大电流下读数飘得厉害。换成分流电阻放在PCB上以开尔文方式从焊盘根部引出采样线后问题彻底消失。这就是走线电阻和电感造成的误差直流和交流大电流场景都存在布局上一定要重视。第四个坑是配置寄存器的平均模式。默认情况下平均次数是1次ADC转换时间也比较短读数噪声会明显偏大。我设置成0x4527也就是平均16次每条通道转换时间约1052µs读取稳定性好很多。但如果你的负载电流变化很快比如电机启动瞬间平均次数过多反而会掩盖过流尖峰。这时候要适当减少平均次数或用ALERT引脚配合限值寄存器做快速报警。最后再分享一个我个人的体会INA226用得好不好九成取决于校准公式有没有理解透。很多时候不是芯片不行而是Current_LSB和分流电阻值没配对导致读出来的数据空间错位。建议在代码里把校准值、分流电阻阻值这些参数定义成带注释的宏换模块时只改一处就能重新标定。按照这套流程走下来电压电流功率三路数据基本一次就能调通。本文还有配套的精品资源点击获取