STM32F103读取ACS712电流传感器:原理、代码与滤波实践
简介涵盖ACS712、GY-712-5A及20A、30A量程的单片机电流传感器模块开发实例基于STM32F103和Keil标准库实现适用于嵌入式物联网项目中的电流检测、数据采集、传感器驱动调试等场景也适合做STM32外设编程的进阶练习。压缩包共159个文件大小约4.18MB内部包含c/h源码、Keil工程文件uvprojx/uvoptx、编译生成的hex/axf固件、PDF器件手册以及重置编译和清理中间文件的批处理脚本文件类型覆盖开发、编译、烧录、查阅等多个环节结构清楚。截止目前已有616人学习下载。整套代码均来自实际项目验证若换成STM32F103系列其他型号只需在Keil中调整芯片型号与Flash容量模块接线定义在代码中配合PDF手册可快速对照实物完成电流采样。工程内注释点出了关键寄存器和采样流程便于二次开发适合入门者系统理解标准库工程组织方式并快速完成电流传感器模块的移植与调试。1. ACS712 与 STM32F103电流采集最省事的一对组合做电机堵转检测时很多新手直接把 ACS712 的 ADC 原始值当电流用5A 量程下空载读数不是 0而是一千五百多。问题出在模块输出的 2.5V 零电流偏置没处理。ACS712 是霍尔效应电流传感器5A 量程灵敏度 185mV/A配合 STM32F103 内置 12 位 ADC是嵌入式物联网项目里最常见的电流采样组合电机控制、电池放电监测、电源负载分析都会用到。这套资料带 KEIL 标准库工程代码里已经把 ADC 初始化、采样滤波、串口输出整理好拿来改量程和通道就能用。适合做课程设计的在校生也适合急着评估 ACS712 精度的工程师——不用从寄存器查起直接看关键代码怎么改参数就行。2. ACS712 工作原理与量程选型2.1 霍尔效应与模块内部结构ACS712 内部真正流过电流的是一条低阻铜排而不是普通采样电阻。电流从 IP 流到 IP- 时铜排周围产生与电流近似成正比的磁场封装内的差分霍尔片感应这个磁场经过放大和温度补偿电路后从 VIOUT 引脚输出电压。因为测的是磁场而非直接测电压降ACS712 本身具备电气隔离特性也不引入明显的插入损耗。模块支持双向电流测量以 5A 量程为例-5A 到 5A 被映射到以 2.5V 为中心、跨度约 ±0.925V 的电压区间。GY-712-5A 这类模块板只是把芯片引脚转接出来没有额外信号调理所以最终精度取决于芯片本身和后面的采样电路。这一点决定了我们在软件里必须做两件事采零电流基准、做数字滤波。2.2 量程、灵敏度与关键参数参数GY-712-5A / ACS712-05BACS712-20AACS712-30A测量范围±5 A±20 A±30 A灵敏度185 mV/A100 mV/A66 mV/A零电流输出VCC5V2.5 V2.5 V2.5 V通流电阻1.2 mΩ1.2 mΩ1.2 mΩ典型带宽80 kHz80 kHz80 kHz选型逻辑很简单被测电流峰值不超过量程的 70%~80%然后选灵敏度最高的型号。5A 量程 1A 电流对应 185mVSTM32F103 的 12 位 ADC 在 3.3V 参考下每个 LSB 约 0.806mV理论分辨率接近 4.4mA20A 量程每 LSB 对应约 8mA。但芯片输出本身带十几到几十毫伏的噪声所以实际分辨率要靠软件滤波去逼近。2.3 为什么这个例程用内置 ADC 而不是外置 ADCSTM32F103 的 ADC1 是 12 位逐次逼近型ADC 时钟最高 14MHz单次转换约 1μs 级别。ACS712 带宽 80kHz对电机电流、电池放电这类几十到几百赫兹的缓变信号F103 内置 ADC 完全够用。需要外置 ADC 的场景主要有三种要真正 16 位以上分辨率、要求主控与采样端电气隔离、多通道需要同步采样。对课程设计和一般物联网节点外挂 I2C 型高精度 ADC 反而引入总线时序和功耗问题。这个例程固定走 ADC1 PA1标准库里 stm32f10x_adc.c 已经写好改通道只需要动一个宏。3. KEIL 标准库工程搭建与 ADC1 通道初始化3.1 工程文件结构和编译前的处理资料里的文件看起来杂其实只有几类ADC.uvprojx 是 KEIL 工程文件ADC.uvguix 是界面布局配置ADC.axf 是编译生成的映像文件ADC_sct.Bak 是分散加载文件备份。还有两个批处理keilkilll.bat 和 重置KEIL编译.bat作用都是清理编译中间文件。我拿到例程的第一件事是先跑一遍清理脚本再重新 Build避免旧的 .o 和 .axf 残留导致改了代码不生效的假象。用 Keil uVision5 打开工程后第一件事是确认 Device 型号。STM32F103C8 是 64KB FlashCB 是 128KBZET 是 512KB。Options for Target - Device 里选成板子实际型号Flash Download 页面里编程算法也要对应。标准库代码对 F103 全系通用但 Flash 容量配错会直接导致下载失败。3.2 时钟、GPIO 与 ADC1 初始化代码void ACS712_ADC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; // 使能 ADC1 与 GPIOA 时钟PA1 属于 GPIOA RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE); // PCLK2 72MHz6 分频得到 12MHz ADC 时钟 RCC_ADCCLKConfig(RCC_PCLK2_Div6); // PA1 配置为模拟输入ADC 引脚不能用复用推挽 GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, GPIO_InitStructure); // 独立模式单次转换软件触发右对齐1 个通道 ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode DISABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); // 规则组第 1 个通道为 ADC_Channel_1采样时间 55.5 周期 ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5); ADC_Cmd(ADC1, ENABLE); }时钟分频选 RCC_PCLK2_Div6 是 F103 的常见做法APB2 总线 72MHzADC 时钟不能超过 14MHz6 分频得到 12MHz余量充足。如果用 Div4 得到 18MHz超出上限后转换结果会不稳定。PA1 必须配成 GPIO_Mode_AIN 模拟输入否则引脚上的施密特触发器会干扰采样。3.3 采样时间与触发方式的选择依据采样时间配置总转换周期适用场景ADC_SampleTime_1Cycles514低阻源、追求最高吞吐ADC_SampleTime_7Cycles520普通电压信号ADC_SampleTime_13Cycles526中等阻抗源ADC_SampleTime_28Cycles541高阻抗传感器ADC_SampleTime_55Cycles568带噪声的传感器输出ADC_SampleTime_239Cycles5252极慢变化的直流信号ACS712 输出阻抗很低理论上用 1.5 周期采样也读得准。但传感器输出带噪声较长采样时间等效于在硬件前端做了一次积分低通能明显压低波形毛刺。例程选 55Cycles5 是合理折中单通道转换时间约 5.7μs采样率仍然足够。触发方式关掉连续转换、只用软件触发目的是让滤波循环里每一次采样都有明确边界。如果开了连续转换轮询 EOC 标志时可能拿到的是最新一次结果循环语义会混乱。4. 电流换算、滤波与串口验证4.1 零电流基准为什么不能直接用理论值理论上 VCC5V 时零电流输出 2.5V3.3V 参考下 ADC 读数应该是 2.5V / 3.3V × 4095 ≈ 3102。但芯片本身有零漂模块供电纹波也会叠加进去。我一般开机后先空载采样 200 到 500 次求平均把结果存成 zero_adc后面的换算以它作基准。#define ACS712_SENSITIVITY 185.0f // mV/A5A 模块 #define ADC_REF_VOLTAGE 3300.0f // mVSTM32F103 VDDA #define ADC_FULL_SCALE 4095.0f // 空载时调用返回零电流基准 ADC 值 uint16_t ACS712_CalibrateZero(void) { uint32_t sum 0; for (uint8_t i 0; i 256; i) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) RESET); sum ADC_GetConversionValue(ADC1); } return (uint16_t)(sum / 256); }4.2 采样均值与电流换算float ACS712_ReadCurrent(uint16_t zero_adc) { uint32_t sum 0; float voltage_mv; float zero_mv; float current; for (uint8_t i 0; i 32; i) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) RESET); sum ADC_GetConversionValue(ADC1); } // 平均 ADC 值换算成毫伏 voltage_mv (float)sum / 32.0f / ADC_FULL_SCALE * ADC_REF_VOLTAGE; // 零电流基准同样换成毫伏 zero_mv (float)zero_adc / ADC_FULL_SCALE * ADC_REF_VOLTAGE; // 减偏置后除以灵敏度得到安培 current (voltage_mv - zero_mv) / ACS712_SENSITIVITY; return current; }这段代码把 ADC 原始值先归一化到毫伏再统一做减法最后除以灵敏度。注意 ACS712 测量的是双向电流换算结果是带符号的 float正负代表电流方向。整个过程中间变量都用 float避免整数除法截断在 32 位 MCU 上浮点运算几十微秒对采样率没有影响。4.3 数字滤波方式怎么选滤波方式窗口抗脉冲能力实时性适用场景算术平均16~256差中等稳态电流监测滑动平均8~16一般好变化较慢的负载中值滤波5~11强差电机启动、继电器通断去极值平均先剔除最大最小再平均强中等推荐用于电机回路电机启动瞬间会有尖峰毛刺中值滤波能干净地剔除电源功耗监测则适合滑动平均因为波形相对平缓。例程的 32 次算术平均是通用起步配置简单且不容易把有效信号滤掉。改滤波方式时注意窗口越大延迟越大做堵转保护时迟滞必须留够。4.4 串口输出与实测验证验证电流读数最直接的办法是串口打印。例程用 USART1 重定向 printf接一个 USB-TTL 模块波特率 115200串口助手每秒刷新一次即可。如果换成串口 3注意 USART3 挂在 APB1 总线上时钟使能要用 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE)同时波特率计算的分频基准也从 72MHz 变成 36MHz这是标准库里串口 1 和串口 3 最容易踩的差异。int fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t)ch); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); return ch; }重定向之后printf(Current: %.2f A\r\n, current) 就能直接在上位机看到数值。验证方法拿一个 12V 灯泡串在 ACS712 的 IP 与 IP- 之间万用表电流档对照读数偏差一般在 2% 以内。5. 量程切换、下载器与 3.3V/5V 电压匹配5.1 换 20A、30A 模块只需改灵敏度#define ACS712_SENSITIVITY 100.0f // 20A 模块 #define ACS712_ZERO_VOLTAGE 2500.0f // mVVCC5V换模块后只改这一个宏换算逻辑不用动。5A 模块全量程输出 2.5V ± 0.925V20A 模块是 2.5V ± 2V30A 模块是 2.5V ± 1.98V。5.2 J-LINK、ST-LINK 与 axf 缺失KEIL 里 Debug 页面必须与实际下载器一致J-LINK 选 J-LINK / J-Trace CortexST-LINK 选 ST-Link Debugger。选错会报 No J-Link found 或 Error: Flash Download failed。另外如果 Build 后发现工程目录下没有 .axf 文件说明编译并未真正完成先看 Build Output 里的 Error 数量再查 Options - Output 里 Create HEX File 是否勾选。5.3 5V 输出超过 3.3V ADC 量程的解法ACS712 必须 5V 供电它的供电范围下限是 4.5V直接接 3.3V 不可靠。但 5V 供电时20A 模块在 10A 以上输出就超过 3.3V 了。F103 的 ADC 输入不能超过 VDDA0.3V也就是约 3.6V超过轻则读数饱和在 4095重则损伤引脚。我的做法是输出端加电阻分压R1 取 10kΩ、R2 取 20kΩ分压比 2/3满量程 4.5V 降到 3.0V留出安全余量。软件里把换算电压除以 2/3同时重新采集一次零电流基准因为分压后 2.5V 变成了约 1.667V。方向放在分压系数和零基准上读数才会和实际电流对得上。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →