简介本资源是面向全国大学生电子设计竞赛电赛参赛者与嵌入式初学者的STM32F103INA219高精度电流检测实战项目聚焦解决嵌入式系统中微弱电流信号易受噪声干扰、原始采样值波动大、测量稳定性不足等典型问题。项目完整实现基于I²C总线的硬件通信、寄存器配置、原始数据读取并集成数字FIR滤波算法对电流序列进行实时去噪处理显著提升检测精度与抗干扰能力适用于电源监控、电池管理系统、智能仪表等实际场景。压缩包含221个文件6.34MB涵盖40个.h头文件外设驱动与滤波参数定义、39个.c源文件I²C底层驱动、INA219驱动、FIR滤波核心逻辑、主循环调度、39个.o目标文件及配套hex、axf、map、sct等编译产物目录结构清晰体现标准Keil MDK工程组织方式。已有365人学习下载提供可直接编译运行的完整工程代码、关键模块注释详尽、滤波系数可调助力读者快速掌握传感器融合、嵌入式数字信号处理与电赛级系统调试全流程。 电赛里只要沾上“电流检测”四个字方案翻来覆去就那么几个采样电阻加运放、霍尔传感器、或者直接上INA219。我这两年做控制类题目一直用INA219配合STM32F103这颗老单片机原因是这套组合足够简单、稳定而且不用折腾差分放大电路。INA219一颗芯片能同时测总线电压、分流电压、电流和功率I2C读几个寄存器就全出来了配合F103的硬件I2C或者软件模拟I2C都很方便。这篇文章把我从选型到调通的完整流程整理出来重点包括寄存器配置、校准计算、代码框架和实际踩过的坑给打算在电赛里用同一套方案的队伍一份能直接抄作业的参考。1. 方案选型为什么是INA219加STM32F1031.1 电赛电流检测的常见做法与对比电赛题里要测电流的场景最常见的是电机堵转保护、电池充放电监测、恒流源输出控制以及各种电源类题目的输出电流采集。早期我见过不少队伍用“采样电阻仪表放大器”的方案原理上没问题但实际做起来有几个麻烦运放需要正负电源或者单电源的共模问题、增益电阻需要精密配对、PCB布局稍微差一点噪声就大。霍尔传感器则有两个维度的问题——精度不高而且贵对很多基础题来说性能冗余了。INA219属于高边电流检测芯片内置12位ADC实际配置寄存器可以做到更高分辨率效果、12位DAC校准逻辑和I2C接口外部只需要一个分流电阻串联在电源正极和负载之间即可。它的优势在于把整个信号链都集成好了芯片内部完成差分放大、ADC采样和数字校准MCU端只需要通过I2C读数据。最终选它就是图省事、可靠、便宜在学校实验室和电赛现场这类环境这种“少出错”的方案价值远大于极限性能。1.2 INA219的内部原理与优势INA219的采样原理是测量分流电阻两端的电压差这个电压叫Shunt Voltage通过内部可编程增益放大器PGA放大后送入ADC。PGA档位有±40mV、±80mV、±160mV、±320mV四档。总线电压则通过内部电阻分压网络直接测量不经过PGA量程可选16V或32V。芯片内部把差分电压转成数字量后再通过校准寄存器设置的系数换算成电流值。为什么要用高边检测而不是低边检测低边是把采样电阻放在负载和地之间电路简单但是会抬高负载参考地这个在很多控制电路里是不可接受的。高边检测把采样电阻放在电源正极不干扰负载地回路INA219本身就是为高边设计的VIN接电源正极、VIN-接负载端外部接线非常直接。另一个优势是I2C地址可通过A0、A1引脚配置成16种不同地址一颗F103的I2C总线上最多可以挂16片INA219做多路电流监测时非常方便。我在做板球系统或者多电机小车时用两片INA219分别测左右电机电流地址0x40和0x41读起来互不干扰。1.3 STM32F103最小系统在其中的角色STM32F103这颗芯片在电赛里的地位不用多说主流Cortex-M3内核72MHz主频I2C、SPI、USART、PWM定时器资源齐全库函数和HAL库的资料铺天盖地。用它做电流检测的上位控制器硬件上只需要一个最小系统板I2C两根线加上电源地就够。很多队伍手里都有现成的F103最小系统板直接用杜邦线连INA219模块就能验证方案不需要画专门的载板。我之前有一次做电源题主控正好是F103输出电流检测就用INA219搞定另一路PWM控制MOSFET调整输出整体逻辑很清晰。F103的I2C如果是硬件I2C需要仔细配置时序如果嫌麻烦可以用GPIO模拟I2C速度慢一点但可靠很多尤其是电赛现场环境复杂模拟I2C反而少很多莫名其妙的坑。2. 硬件设计与连接要点2.1 引脚连接与最低配置INA219模块通常有8个引脚VCC、GND、SDA、SCL、A0、A1、VIN、VIN-。与F103连接时VCC接3.3V模块上一般有板载稳压和电平转换接5V也能工作但SDA/SCL电平需要确认SDA接PB7SCL接PB6对应F103的I2C1外设。A0和A1悬空时默认地址是0x40如果总线上只挂一片直接悬空就好不需要接跳线。VIN和VIN-的接法要看采样点在哪个位置。以电机供电为例电源正极出来先进INA219模块的VIN再从VIN-出来接到电机的电源输入这样模块内部的分流电阻就串在了电源和负载之间。需要注意的是INA219测量的是VIN和VIN-之间的压差而不是负载电流本身——这个压差除以分流电阻阻值才是电流芯片内部有校准逻辑配置好校准寄存器后可以直接读电流寄存器。我自己习惯在模块的VIN和VIN-之间并联一个10uF电解电容在VCC和GND之间并联一个0.1uF瓷片电容作用是在电源有波动时给芯片一个稳定的供电和采样环境。这个电容不是必须的但实测对噪声大的开关电源场景有帮助。2.2 分流电阻选择与PGA设置分流电阻的阻值直接决定电流测量的分辨率和量程。INA219内部默认不带分流电阻模块上会焊一个板载电阻常见的是0.1Ω但也要看你买的具体模块有的可能是0.02Ω、0.05Ω等。用之前一定要用万用表实测一下模块上分流电阻的阻值因为这是所有电流计算的基础。PGA档位选择要结合分流电阻来看。假设分流电阻是0.1ΩPGA选±320mV档那么最大可测电流就是320mV / 0.1Ω 3.2A。如果要测的电流最大只有2A那用±320mV档是合适的留有余量且精度也不错。如果分流电阻是0.01Ω同样PGA下最大可测电流就是32A这时候分辨率会差一些0.01Ω对应每LSB的电流会很大。我一般选PGA的原则是让最大预期电流对应到PGA量程的70%到90%。比如预期2.5A那选±320mV配0.1Ω对应3.2A就比较合适。留太多余量会让低电流段的分辨率变差留太紧又怕瞬态电流超过量程导致读数削顶。2.3 布线经验与去耦高边电流检测的PCB布线有几个容易踩的坑。VIN和VIN-走线要尽量短、尽量粗因为这两根线上有负载的全部电流走线电阻会在分流电阻之外额外增加压降导致测量偏大。另外VIN和VIN-的采样走线应该从分流电阻两端单独引出而不是直接从大电流走线上分叉这个叫开尔文连接可以减少走线电阻的影响。去耦电容放置位置也很讲究。INA219的供电去耦电容要靠近VCC引脚我习惯VCC对GND放一个0.1uF再并联一个10uF两个电容都尽量靠近芯片。VBUS在测量16V以上电压时VIN引脚对GND最好也加一个0.01uF左右的电容滤波注意这个电容不要太大否则会影响高压侧的瞬态响应。这些细节在电赛现场未必会直接导致失败但能明显减少高电流场景下的读数波动。3. 寄存器级详解与校准计算3.1 六个核心寄存器INA219一共有6个寄存器但实际编程中90%的时间只操作其中4个。0x00是配置寄存器控制PGA量程、总线电压量程、ADC位数/采样模式和芯片工作模式上电默认值0x399F对应16V总线量程、±320mV PGA、12位ADC、连续采样模式直接使用默认值一般也能工作。0x01是分流电压寄存器存的是原始的差分电压ADC值单位是10uV每LSB。0x02是总线电压寄存器低3位为0有效值右移3位后乘以4mV得到总线电压。0x03是功率寄存器0x04是电流寄存器这两个都需要先写校准寄存器0x05才能得到有意义的值。校准寄存器是INA219的核心配置项它决定电流和功率寄存器的比例关系。如果校准寄存器为0电流和功率寄存器读出来永远是0这是一个很容易被忽略的点很多人在一开始读电流寄存器全为0然后就以为是I2C没通其实只是校准没配。3.2 校准值计算实例假设分流电阻R_shunt 0.1Ω最大预期电流I_max 2A校准计算步骤如下第一步确定电流分辨率Current_LSB。公式是Current_LSB I_max / 32768带入得0.000061A。但实际使用时这个值太小会导致校准寄存器数值很大有可能超过0xFFFF上限所以我一般把LSB取到比理论值稍大一点比如0.0001A/LSB。LSB取得大分辨率会稍微变差但在0.0001A级别对绝大多数电赛场景完全够用。第二步计算校准寄存器值。公式CAL 0.04096 / (Current_LSB × R_shunt)代入0.04096 / (0.0001 × 0.1) 4096换算成16进制是0x1000写入校准寄存器0x05。第三步写入后电流寄存器读出的值乘以Current_LSB就是实际电流安培数。功率寄存器的LSB是20倍电流LSB即0.002W/LSB读出后乘以20倍Current_LSB就是实际功率瓦特。如果配置了不同分流电阻或者不同最大电流只需要按照这两步重新算就好。计算出的校准寄存器值如果超过0xFFFF说明Current_LSB选太小了需要调大。3.3 数据读取换算流程假设配置和校准都已完成读取流程是先读总线电压寄存器右移3位乘0.004得到电压值V再读分流电压寄存器乘0.00001得到分流电压值V_shunt用V_shunt除以R_shunt也能算出电流但这个电流不经过校准精度受分流电阻精度影响然后读电流寄存器乘上Current_LSB得到校准后的电流值I读功率寄存器乘上20倍Current_LSB得到功率P。流程图我贴一张自己调试时用的简化版本I2C读0x02→计算电压→I2C读0x01→计算分流电压→I2C读0x04→计算电流→I2C读0x03→计算功率。实际写代码时我习惯把电压、电流、功率的读取和换算封装在一个函数里主循环直接调用。I2C读取本身是阻塞的一次完整读取四组数据大概2ms左右对电流检测来说这个速度足够快。4. 基于STM32F103的完整代码实现4.1 CubeMX初始化配置使用STM32CubeMX配置F103比较简单。选择STM32F103C8T6后在Pinout视图里把PB6设为I2C1_SCLPB7设为I2C1_SDA。I2C1参数配置Standard Mode时钟100kHz其他保持默认。如果需要串口打印调试可以把PA9、PA10设为USART1的TX、RX波特率115200。生成代码后在main.c里添加INA219的驱动代码。有一点要注意CubeMX默认生成的I2C初始化已经配置好了GPIO复用和上拉不需要额外设置。I2C速度选100kHz标准模式就行400kHz快速模式也能跑但有些模块线长或者接线质量不好时容易出错标准模式更稳。4.2 I2C读写驱动INA219的I2C读寄存器需要先发送寄存器地址然后再读取2字节数据。写寄存器则是先发寄存器地址再写2字节数据。头文件里定义地址时注意芯片地址0x40是7位地址而HAL库的I2C函数需要8位地址左移一位0x40左移一位是0x80这个细节很多人第一次用都会搞错。驱动代码里我习惯封装两个函数INA219_WriteReg和INA219_ReadReg底层用HAL_I2C_Mem_Write和HAL_I2C_Mem_Read来实现MemAddress就是寄存器地址MemAddSize用I2C_MEMSIZE_8BIT因为INA219的寄存器地址是8位。4.3 核心读取与换算函数配置和读取的主逻辑代码如下#define INA219_ADDR (0x40 1) // 7位地址左移一位 #define REG_CONFIG 0x00 #define REG_SHUNT 0x01 #define REG_BUS 0x02 #define REG_POWER 0x03 #define REG_CURRENT 0x04 #define REG_CALIBRATION 0x05 #define R_SHUNT 0.1f // 分流电阻阻值 #define CURRENT_LSB 0.0001f // 电流分辨率 void INA219_Init(void) { // 配置寄存器16V量程PGA±320mV12位ADC连续采样 uint16_t config 0x399F; INA219_WriteReg(REG_CONFIG, config); // 校准寄存器 uint16_t cal (uint16_t)(0.04096 / (CURRENT_LSB * R_SHUNT)); INA219_WriteReg(REG_CALIBRATION, cal); } void INA219_ReadAll(float *bus_voltage, float *current, float *power) { uint16_t bus_raw INA219_ReadReg(REG_BUS); uint16_t shunt_raw INA219_ReadReg(REG_SHUNT); uint16_t current_raw INA219_ReadReg(REG_CURRENT); uint16_t power_raw INA219_ReadReg(REG_POWER); *bus_voltage (float)((bus_raw 3) * 0.004); *current (float)(current_raw * CURRENT_LSB); *power (float)(power_raw * CURRENT_LSB * 20.0); }I2C读写函数用HAL库封装后非常简洁void INA219_WriteReg(uint8_t reg, uint16_t data) { uint8_t buf[2]; buf[0] (data 8) 0xFF; buf[1] data 0xFF; HAL_I2C_Mem_Write(hi2c1, INA219_ADDR, reg, I2C_MEMSIZE_8BIT, buf, 2, 100); } uint16_t INA219_ReadReg(uint8_t reg) { uint8_t buf[2]; HAL_I2C_Mem_Read(hi2c1, INA219_ADDR, reg, I2C_MEMSIZE_8BIT, buf, 2, 100); return ((uint16_t)buf[0] 8) | buf[1]; }这里有个细节读总线电压寄存器时数据手册说低3位始终为0所以右移3位后再乘4mV。如果不右移直接把0x03E8乘以0.004结果就偏了8倍。我一开始就犯过这个错串口打出来的电压值特别奇怪后来对着手册一行行检查才发现。4.4 主循环与应用逻辑主循环里最简单的调用方式就是周期读取并打印while (1) { float voltage, current, power; INA219_ReadAll(voltage, current, power); printf(U%.2fV I%.3fA P%.2fW\r\n, voltage, current, power); HAL_Delay(100); }在电赛题目里这个读取数据往往不是最终目的而是作为控制环的一个输入。比如做电机限流保护可以设定一个阈值电流超过阈值就降低PWM占空比。这里我把PWM配置和调用放在一起F103的TIM2_CH1可以输出频率可调的PWM配置好定时器和占空比后在主循环里配合INA219的电流值做阈值判断就能实现简单的过流保护。实测我做过一个直流电机驱动板电流超过1.8A就把PWM降到20%电机堵转时不会烧MOSFET。如果希望读取速度更快可以把I2C的中断或DMA模式开起来但我在比赛里用阻塞式读取已经足够主循环里没有其他需要极低延迟的任务。等到了需要PID快速闭环的项目可以考虑用DMAI2CF103的HAL库也支持这种方式配置会复杂一些。5. 实调踩坑与排查方法5.1 常见故障现象与原因对照调了这么久把遇到过的问题整理成一张速查表现象可能原因排查方法I2C读回0xFF接线错误、地址不对、没共地用示波器看SDA/SCL波形确认7位地址和HAL地址电流一直是0校准寄存器没写、分流电阻两端无压差先读分流电压寄存器确认有值电流为负值VIN和VIN-接反检查采样电阻两端接线方向电压正常但电流偏大/偏小校准值算错、分流电阻实际阻值和标称不符用万用表实测分流电阻阻值重新计算读数跳变剧烈电源噪声、线缆过长、没有去耦电容加电容、缩短杜邦线、开启ADC平均值模式大电流时读数削顶PGA量程不够降低PGA增益档位或提高量程5.2 定位问题的工具方法排查I2C问题时示波器是最好用的工具。把SDA和SCL分别接到示波器两个通道触发设置在SCL下降沿可以看到完整的I2C通信时序。正常波形应该有明显的起始条件、地址帧、数据帧和停止条件。如果看不到波形基本可以确定是接线或初始化问题。如果没有示波器用逻辑分析仪也行现在几十块钱的逻辑分析仪配合电脑软件就能解析I2C协议非常方便。没有示波器的条件下我一般先在F103里写一个回读函数把配置寄存器的值读回来打印看是不是0x399F。如果配置寄存器能读回正确的值说明I2C通路没问题问题大概率出在校准计算或者寄存器换算上。如果读回0xFF那就先把接线和地址检查一遍再看I2C初始化是否成功。另一个实用技巧是多打印原始寄存器值而不是只打印换算后的物理量。比如电流不对时把分流电压寄存器的原始值和当前寄存器原始值都打出来一眼就能看出是校准系数的问题还是采样本身的问题。用串口助手看数据时注意把波特率、数据位这些参数设置一致F103的USART驱动代码网上很多直接抄就好。6. 电赛应用扩展与经验总结6.1 电机限流保护与PWM联动电赛控制类题目里电机是最常见的执行机构。电机堵转时电流会瞬间飙升如果不做保护MOSFET或驱动芯片很容易烧。用INA219实时监测电机电流配合F103的PWM输出实现限流保护是很实用的扩展。我的做法是主循环里读电流如果超过设定阈值就把PWM占空比限制在一个安全值同时用串口打印报警信息。这个方案比单纯接一个保险丝或者热敏电阻要灵活得多因为阈值可以在代码里随时改不用动硬件。而且INA219可以同时上报电流值方便在比赛演示时通过屏幕或上位机把电流曲线实时显示出来这对评委来说也是个加分项。6.2 电池监测与数据记录电源类题目经常会涉及电池的充放电管理这时候INA219的功率和电量统计功能就派上用场了。我在一次做锂电池充电器的题目中用INA219监测充电电流配合F103的ADC采集电池电压在OLED上实时显示充电进度。因为INA219的电流精度还不错还可以通过时间积分估算充入的电量虽然不能和真正的电量计芯片比但对比赛演示完全够用。如果要记录长时间电流数据可以把采样间隔设成1秒每一条记录存到SD卡或通过串口发送到上位机。F103的SDIO接口和SPI接口都支持SD卡网上有现成的文件系统移植教程这里直接搭起来就可以用。我实际做过一个24小时电流记录仪SD卡存CSV格式导入电脑就能生成电流曲线图。6.3 一些留给后续比赛的建议做了几个项目后我对INA219的使用有几个固定的习惯。一是分流电阻的阻值先用万用表实测一下不要信模块上标注的数值因为廉价模块的采样电阻误差可能到1%甚至更多这直接决定了电流读数的绝对精度。二是配置寄存器里的ADC模式建议改成带平均的连续采样模式比如配置成8次平均值虽然读取速度会慢一点但抗噪声能力会好很多实测下来读数波动能从几十mA降到几个mA。三是在比赛前至少留出半天专门测一次长期稳定性测试把板子放在工作状态跑2小时看电流读数有没有漂移提前发现问题比现场找问题要省事得多。如果后面要做更高精度的电流采集比如微安级的小电流测量INA219就不太合适了可以考虑INA226或者外置高精度ADC。但对电赛里绝大多数的电流检测场景INA219这颗芯片已经是非常均衡的选择体积小、精度够用、软件简单和STM32F103经典组合在一起能省下大量调试时间把精力放在控制逻辑和题目本身真正考察的内容上。本文还有配套的精品资源点击获取