
简介面向STM32嵌入式开发者的一套INA226电流功率传感器驱动完整工程解决电源管理、电池监控和负载检测场景中高精度电流、电压及功率采集需求。工程基于C语言和Keil MDK开发覆盖初始化配置、寄存器读写、数据采集与串口输出等完整驱动层实现适合中高级嵌入式工程师项目移植或学习参考。压缩包共151个文件以36个h头文件与35个c源文件为核心另含hex烧录文件、uvprojx工程文件、map映射及axf调试镜像等编译辅助文件整体2.97MB。工程整合了STM32标准外设库的USART、I2C、ADC与定时器模块结合INA226最高260V电压和40A电流的测量能力可快速搭建高精度电流功率监测方案已有4690人学习该资源。从源码布局与完整驱动链路来看读者既能对照学习I2C读取传感器数据的编程思路也可将驱动直接移植至电池管理、电源监控、负载检测等自有项目中有效缩短开发周期。 做嵌入式电源监测这块的朋友应该都听过INA226这颗芯片。它是TI德州仪器家一颗非常经典的16位电流/电压/功率监测芯片I2C接口能同时读总线电压、分流电压、电流和功率。之前有个项目要做电池充放电的在线监测网上找了一圈资料零零散散英文手册啃起来又费劲后来干脆整理了一个INA226.rar压缩包把器件手册、参考电路、寄存器速查表、还有基于STM32F4 HAL库的例程全塞进去方便自己后面复用也顺手分享给了同事。这篇就基于这个包里的内容把INA226从硬件设计到F4单片机读取的完整流程拆开讲讲包括踩过的坑给准备用它做电源监测的朋友做个参考。1. INA226核心原理与选型思路1.1 为什么选择INA226而不是INA219或分立方案现在市面上测电流的方案其实很多最简单的悬一个采样电阻用运放放大后送给ADC。但这种方案的缺点很现实低温漂的精密电阻不便宜运放失调和温漂会影响精度PCB上还要专门做差分走线调零和校准的工作量不小。如果只是做一个样机这么折腾一圈下来往往比直接用专用芯片更费时间。INA226这类专用监测芯片内部是把差分放大器、ADC、基准源、I2C接口、告警比较器全集成在一起。外部只需要一个采样电阻芯片直接输出电流和功率的换算结果省掉了模拟前端的设计调试。校准也简单往校准寄存器里写一个值芯片就自动算好电流和功率。和另一颗常用芯片INA219对比INA226有几个明显差异。INA219的I2C地址范围是0x40~0x4F有4个地址引脚A0/A1/A2INA226只有A0和A1两个地址引脚可配置地址是0x40~0x4F恰好也是16个。但INA226能测的总线电压更高可以达到36V而INA219最高只有26V所以做24V系统或者48V系统经分压后时INA226更有优势。另外INA226的转换时间和平均模式可配置范围更宽响应更快更适合动态电流监测。像ADS1115这类通用ADC虽然也能测电压但测试范围内很有限需要外部分压电路才能测高压而且每次转换后要自己用代码算电流没有告警功能。相比之下INA226自带的比较器可以做到过流报警不必单片机时刻盯着在某些场景下能省不少CPU时间。1.2 INA226模块的电路设计与采样电阻选择INA226的典型应用电路很简单分流电阻采样电阻串在负载回路里芯片的IN和IN-分别接在采样电阻两端VBUS引脚直接连到负载电源端用来测总线电压。芯片本身的VS供电可以单独给3.3V也可以从被测电压那边取但注意VS最高只能到5.5V所以如果打算从48V那边供电必须加LDO降压。模块设计时采样电阻的选择很关键直接决定了你要设置的电流量程和校准值推荐参考以下参数参数推荐范围说明采样电阻阻值1mΩ ~ 100mΩ阻值大分辨率高但功率损耗和压降也大采样电阻功率≥ 2倍最大电流平方×阻值预留充足余量防止发热导致阻值漂移电阻类型低温度系数合金电阻建议≤50ppm/℃锰铜或康铜材质更稳差分走线平行短走线减小寄生电感避免引入噪声VS去耦电容0.1μF 10μF并联紧靠芯片电源脚I2C上拉电阻2.2kΩ ~ 10kΩ根据I2C总线长度和速率调整我实际测试时用过5mΩ和10mΩ两种电阻。5mΩ用于3A以内的电流检测采样电阻上最大压降15mV对系统本身的电压影响很小10mΩ用在1A左右的小电流场景信号幅度大读数稳定。这里要注意一个细节不要为了追求分辨率无限增大采样电阻否则负载侧的电压跌落会非常明显尤其是低压大电流系统里往往得不偿失。1.3 分压场景下的注意事项INA226的VBUS输入范围是0~36V不会直接从VS取电所以即便被测总线电压超出5.5V只要不超过36V都没问题。但如果被测电压超过36V就得分压后再接到VBUS上此时总线电压寄存器的读数就需要乘一个分压系数。网上有些模块使用两颗电阻对VBUS进行分压用于扩展量程这种做法要特别留意分压电阻的精度和温度系数否则电压测出来会偏差很大。如果只是测单节锂电池3.7V~4.2V或常见的12V/24V系统我建议直接接VBUS不加分压这样最简单也最准。我在项目中实测12V电源用INA226直接测读数和万用表误差在0.5%以内比较理想。2. INA226寄存器详解与中文手册关键点2.1 寄存器地图与快速索引INA226的所有操作都是通过I2C读写寄存器完成的。网上流传的INA226中文手册大部分是英文原版datasheet的翻译版或者某芯片商城重新整理的中文版精度参差不齐拿到手之后一定要和英文datasheet对照着看。尤其是寄存器地址和位定义翻译版本偶尔会出错建议以英文原版为准。我把常用的寄存器整理成了速查表寄存器名称地址读写属性作用Configuration0x00读写芯片运行模式、ADC采样配置Shunt Voltage0x01只读分流电压即采样电阻两端压差Bus Voltage0x02只读总线电压Power0x03只读功率需要配置校准寄存器才有效Current0x04只读电流需要配置校准寄存器才有效Calibration0x05读写校准值用于计算电流和功率Mask/Enable0x06读写告警功能配置Alert Limit0x07读写告警阈值Manufacturer ID0xFE只读固定为0x5449Device ID0xFF只读INA226固定为0x22602.2 配置寄存器位域解读配置寄存器是INA226的主控室上电后的默认值是0x4127。复位之后如果要重新配置最佳实践是先把复位位默认就是0置1等芯片完成复位再写入新的配置值。配置寄存器的位定义如下16位位号名称说明15RST写1触发软件复位自动清零14保留恒为013~12AVG[1:0]平均模式00:1次01:4次10:16次11:64次11~9VBUSCT[2:0]总线电压转换时间8~6VSHCT[2:0]分流电压转换时间5MODE[3]连续/触发模式选择4~3MODE[2:1]连续VS触发选择2~0MODE[0]测量模式关闭/分流/总线/分流总线转换时间位的配置直接影响响应速度和功耗。VBUSCT和VSHCT可以从140μs调到8.244ms。如果用在电池管理系统这种变化较慢的场景可以配置成最慢速率配合64次平均读数会非常平滑功耗也最小。如果是监测电机启动电流这种瞬态变化就必须用最快转换时间并关闭或减少平均。MODE位组合方式000关闭Shutdown芯片进入低功耗模式电流1μA001触发模式只测分流电压010触发模式只测总线电压011触发模式同时测分流和总线电压100关闭待命101连续模式只测分流电压110连续模式只测总线电压111连续模式同时测分流和总线电压最常用一般场景下我们直接配置成0x4127也就是AVG1次转换时间1.1ms连续测分流总线这个配置很均衡适合大多数应用。如果需要低功耗可以把模式改成关断或者触发模式需要测量时才给一个触发位读完数据再关掉。2.3 校准寄存器与电流、功率换算逻辑校准寄存器0x05是INA226里最需要理解清楚的一个寄存器。如果不写校准值电流寄存器和功率寄存器读出来是0只有分流电压和总线电压有效。校准值计算公式如下current_LSB 预期最大电流 / 32767 CAL 0.00512 / (current_LSB × R_shunt)其中0.00512是INA226内部固定的一个系数来源于内部增益和参考电压的乘积。计算出来之后要四舍五入取整再写回校准寄存器。需要注意的是这个值是一个无符号整数如果算出来超过0x7FFF说明current_LSB太小需要调大。做法是把current_LSB调大一档比如从1μA/bit调成2μA/bit。举例说明假设采样电阻是5mΩ最大预期电流是5A那么current_LSB 5A / 32767 ≈ 0.0001526A ≈ 152.6μA/bit CAL 0.00512 / (0.0001526 × 0.005) ≈ 0.00512 / 0.000000763 ≈ 6710 ≈ 0x1A36往校准寄存器写入0x1A36之后电流寄存器读出的原始值乘以152.6μA就是实际电流单位A。功率寄存器则是原始值乘以电流LSB再乘以25也就是0.003815W/LSB。这里的25是功率调节系数的倒数英文手册里写的是Power LSB 25 × Current LSB中文手册里如果写成功率LSB25*电流LSB意思是一致的。用一个小节强调一下校准计算的误差会直接影响电流和功率精度。如果预期最大电流估算不准选的current_LSB偏大那么电流的分辨率会变差但不会导致标定错误。如果偏小CAL值溢出变成负数或非常大的数写进去之后读出来的电流就是乱的。2.4 实测寄存器读数的换算公式读取到原始寄存器值后需要换算成实际物理量。换算关系如下分流电压Shunt Voltage原始值 × 2.5μV即0.0000025V总线电压Bus Voltage原始值 × 1.25mV即0.00125V电流Current原始值 × current_LSB功率Power原始值 × 25 × current_LSB有个细微但容易踩坑的点分流电压寄存器是有符号数16位补码表示因为分流电阻两端电压可能是负的电流反向时。总线电压寄存器虽然也是16位但在正常测量范围内只会读出正值。电流和功率寄存器同样是有符号数所以代码里的变量类型要定义为int16_t不能用uint16_t否则反向电流测出来会是个莫名其妙的大数。3. STM32F4 HAL库驱动INA226的完整实现3.1 硬件连接与I2C地址分配这里用最基础的STM32F407来演示I2C1连接INA226模块。硬件连接如下INA226引脚STM32F4引脚说明VCC3.3V芯片供电GNDGND共地SDAPB7I2C1_SDA数据线需上拉SCLPB6I2C1_SCL时钟线需上拉A0GND或VCC地址配置A1GND或VCC地址配置VS被测电源正极测总线电压用IN采样电阻靠近负载端差分正输入IN-采样电阻靠近电源端差分负输入A0和A1两个引脚的电平组合决定I2C从机地址。需要注意INA226手册中地址字节是7位地址左移一位后的结果而STM32 HAL库中的I2C地址参数通常要求是8位地址即已左移一位使用时要确认这一点。A0/A1与地址的对应关系A1A07位地址HAL库用地址GNDGND0x400x80GNDVCC0x410x82VCCGND0x440x88VCCVCC0x450x8A更完整的对照表可以查手册但实际应用中多数情况下系统里只有一颗INA226所以把A0和A1都接地直接用0x80最省事。3.2 HAL库I2C读写代码框架STM32CubeMX配置I2C1速率选100kHz或400kHz都行。INA226的I2C时序符合标准I2C协议不需要特殊的外设配置。这里分享一个基于HAL库的函数框架包括写寄存器、读寄存器、一次读取多个寄存器三个基础操作。#define INA226_ADDR 0x80 // A00, A10注意这是8位地址已左移 #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 I2C_HandleTypeDef hi2c1; // 写16位寄存器 HAL_StatusTypeDef INA226_WriteReg(uint8_t reg, uint16_t data) { uint8_t buf[2]; buf[0] (data 8) 0xFF; buf[1] data 0xFF; return HAL_I2C_Mem_Write(hi2c1, INA226_ADDR, reg, I2C_MEMADD_SIZE_8BIT, buf, 2, 100); } // 读16位寄存器 HAL_StatusTypeDef INA226_ReadReg(uint8_t reg, uint16_t *data) { 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) { *data (buf[0] 8) | buf[1]; } return status; }注意数据字节序INA226寄存器是大端格式高位在前。HAL函数按顺序读取两个字节时第一个字节是高位第二个是低位代码里必须这样拼否则读出来的值完全不对。3.3 初始化与校准流程初始化流程我一般按以下顺序执行软复位、设置配置寄存器、计算并写入校准值、读取设备ID做自检。void INA226_Init(void) { uint16_t device_id 0; // 1. 软件复位 INA226_WriteReg(INA226_REG_CONFIG, 0x8000); HAL_Delay(10); // 等待复位完成 // 2. 配置寄存器0x4127 // AVG1VBUSCT1.1msVSHCT1.1ms连续分流总线测量 INA226_WriteReg(INA226_REG_CONFIG, 0x4127); // 3. 写入校准值假设Rshunt5mΩ最大预期电流5A // current_LSB 5 / 32767 0.0001526 A/bit // CAL 0.00512 / (0.0001526 * 0.005) 6710 0x1A36 INA226_WriteReg(INA226_REG_CAL, 0x1A36); // 4. 读取设备ID验证通信 INA226_ReadReg(0xFF, device_id); if ((device_id 0xFFF0) 0x2260) { // 检测成功 } }设备ID寄存器高12位是0x226低4位是修订版本号所以判断时要用掩码0xFFF0。我见过有人直接拿函数返回值当寄存器地址用结果读出来一个奇怪的值排错排了很久。一定要看清楚读回来的值再判断。3.4 读取并换算电压、电流、功率初始化完成之后读取就非常简单了核心代码如下float INA226_ReadBusVoltage(void) { uint16_t raw 0; INA226_ReadReg(INA226_REG_BUS, raw); return raw * 0.00125f; // 单位V } float INA226_ReadShuntVoltage(void) { int16_t raw 0; INA226_ReadReg(INA226_REG_SHUNT, (uint16_t*)raw); return raw * 0.0000025f; // 单位V } float INA226_ReadCurrent(void) { int16_t raw 0; INA226_ReadReg(INA226_REG_CURRENT, (uint16_t*)raw); return raw * 0.0001526f; // 单位A } float INA226_ReadPower(void) { int16_t raw 0; INA226_ReadReg(INA226_REG_POWER, (uint16_t*)raw); return raw * 0.0001526f * 25.0f; // 单位W }这里要特别提醒电压寄存器是无符号电流、功率、分流电压寄存器都要当有符号数处理。我自己第一次用的时候把电流寄存器直接定义成uint16_t结果电流方向反了或者接近零时读出来的值就成了6万多看起来非常离谱。上面这个current_LSB和校准值都是跟着硬件配置走的。如果换了采样电阻或者改了最大电流预期校准值和换算系数必须同步改这个参数强烈建议用宏定义或结构体集中管理避免散落在代码各处。3.5 I2C时钟频率与稳定性I2C在100kHz标准模式下INA226读写基本不会出问题。400kHz高速模式下要注意一点总线上如果有多个设备线长超过10cm时建议示波器看一下波形上升沿太缓的时候可以减小上拉电阻。如果总线上挂了多个INA226地址不同读写逻辑都一样只要把地址提取出来做成一个结构体就行。另外在低功耗场景中可以在不用的时候把INA226配置成Shutdown模式配置寄存器最低三位写000需要测量时再切回连续模式。从Shutdown模式恢复的典型时间是芯片内部参考源的稳定时间手册没写明确实测大概几十微秒到几百微秒我的做法是切换模式后加一个短延时再读数据效果稳定。4. 实操过程中的典型问题与排查思路4.1 数据乱跳、波动大的原因与处理这是INA226最常见的问题。现象是连着读总线电压数据有小幅波动电流更是忽大忽小但万用表测出来明明是稳定的。原因一采样电阻的噪声。采样电阻本身阻抗很低信号幅度只有几个毫伏很容易受外部干扰。如果PCB上走线过长或采样电阻离感性负载近都会耦合噪声。处理办法把INA226尽量靠近采样电阻差分线走在一起采样电阻用低感设计。原因二配置的平均次数太少。转换时间配置成了最快速率140μs同时AVG1这样采到的瞬时噪声就会直接反映在数据里。如果应用对实时性要求不高我建议把转换时间调到2.164ms或8.244ms平均次数设成16或64数据会平滑很多。代价是响应变慢但大多数电源监测场景根本不需要微秒级响应。原因三参考源的波动。INA226内部基准在刚上电或刚从Shutdown模式唤醒时需要时间稳定此时读出的数据会偏。解决办法是在初始化之后等待至少1ms再开始读数或者丢弃前几次转换结果。4.2 读数整体偏低或偏高怎么校准如果读数与万用表对比出现固定比例偏差大概率问题是校准值不准确。最常见的因素是采样电阻的实际阻值和标称阻值不一致。标称5mΩ的电阻实际可能是4.9mΩ那么电流读数和真实值之间就会有一个比例误差。修正方法有两种第一种用高精度万用表实测采样电阻阻值再重新计算校准值。第二种更实用直接在代码里做一次软件标定。比如接入一个已知电流的负载读回电流寄存器的原始值计算实际电流与原始值的比值把这个比值作为修正系数乘到换算结果里。我实际用的做法是硬件设计时预留一个标准电流源接口量产前用100mA或1A的恒流源做一次单点标定把修正系数存到Flash里。这种方案可以在采样电阻偏差±1%的情况下把电流精度做到±0.5%以内。4.3 I2C通信失败设备无应答I2C通信失败特别是读设备ID超时、返回HAL_ERROR是最让人头疼的问题。常见原因和检查顺序如下第一地址搞错。确认A0/A1的实际硬件接法对应查表得到地址。HAL库要求的是8位地址裸机I2C可能用的是7位地址这两种差别经常让人困惑。第二上拉电阻缺失。I2C总线必须有上拉电阻STM32的内部上拉不可靠建议外部加上2.2kΩ到10kΩ的上拉。第三接线错误尤其是SDA和SCL接反。第四芯片供电异常INA226的VS如果没供电整个I2C接口不工作。有一个调试技巧非常实用先只读一次Manufacturer ID0xFE如果读回来是0x5449说明I2C通路和芯片硬件都正常如果读不到就先用示波器抓SDA和SCL波形看是否有起始信号和ACK。这个做法可以把问题定位到硬件链路还是寄存器配置。4.4 电流方向反了怎么办IN和IN-接反是最容易犯的错误。如果发现电流读数为负分流电压为负那就说明采样电阻两端接反了。解法有两种硬件上交换IN和IN-接线软件上把读到的分流电压和电流取绝对值。从工程规范角度如果是自己做板子建议直接在原理图上保持IN接负载端、IN-接电源端的习惯这样所有代码和读数的符号约定都是统一的不用额外做正负转换。4.5 告警功能使用要点Mask/Enable寄存器0x06可以配置告警条件比如分流电压超限、总线电压超限、功率超限等。配置好Alert Limit寄存器0x07之后当满足条件时ALERT引脚会被拉低。这颗引脚的输出极性可以配置默认是低电平有效也可以配置成高电平有效。使用告警功能时注意一个容易忽略的细节告警产生后如果条件还在ALERT引脚会一直保持只有当读取相关寄存器或者清除标志位后引脚才会恢复。具体清除方式取决于告警类型在连续模式下读取转换结果或者修改告警配置也能清除。实际项目里我通常直接用一个外部中断引脚接ALERT触发后通过I2C读取标志寄存器判断是哪种告警再执行对应的保护逻辑。5. 个人使用经验与扩展建议INA226这颗芯片我前后用了大半年整体感受是硬件设计简单软件驱动也简单但要做到读数稳定准确功夫全在细节里。比如采样电阻的选型、PCB布局的走线、I2C地址的分配、校准值的计算每一步稍有不慎数据就会有偏差。最值得推荐的组合是5mΩ或10mΩ低感采样电阻 转换时间2.164ms 16次平均 单点软件标定这样搭配下来基本上不需要再花时间调噪声读数稳定性和精度都很可观。另外如果项目里不止一颗INA226建议在驱动层做一个设备结构体把I2C地址、校准值、转换系数打包在一起初始化时传入不同的参数。这样代码复用性很高后续扩展多个监测点只是多实例化几个结构体的事。这个INA226的驱动代码和中文手册整理包适合做电池管理、电源监控、功率计这类的项目。如果后面有空我打算再补一个基于FreeRTOS的轮询任务模板把多路INA226的数据采集封装成统一接口用消息队列上抛给应用层这样可以省去手动管理各路采集逻辑的麻烦。踩过坑之后才能体会到这类芯片用起来真的不难难的是在开始动手前就想清楚量程、校准、布局这三个关键点。本文还有配套的精品资源点击获取