
简介面向STM32嵌入式开发者、物联网单片机学习者以及ACS712电流检测应用工程师这是一套基于STM32F103的ACS712系列电流传感器模块驱动例程覆盖5A、GY-712-5A、20A、30A多种规格采用KEIL标准库开发代码中已定义好单片机与模块的接线关系便于对照实物快速完成硬件连接。资源包共159个文件以h头文件、c源码文件为主另含pdf手册、hex烧录文件、axf调试文件、bat辅助脚本、uvprojx工程配置与文本说明等能覆盖源码阅读、工程编译、程序烧录、上板调试的完整流程压缩包仅4.18MB轻量易获取。已有616人学习下载。直接打开KEIL工程即可在STM32F103上运行换用同系列芯片只需调整芯片型号与FLASH容量例程均经实战检验ADC采样与电流换算逻辑清晰适合需要快速上手电流检测、传感器数据采集与嵌入式驱动开发的读者参考复用。1. 从ACS712的量程选择说起标题里同时出现 GY-712-5A、20A、30A并不是一块模块可以通过跳线切换量程而是同一套使用流程对应三个不同后缀的 ACS712 芯片。开发者在拿到这类霍尔电流传感器模块时最容易放进代码里的错误不是换算公式算错而是量程选错5A 模块要求 185mV/A 灵敏度20A 是 100mV/A30A 只有 66mV/A。如果拿 5A 模块去测 8A 直流输出早就超出线性区间后续所有 ADC 换算都是白费。这套实例的核心工作可以压缩成三步读懂手册的偏置与灵敏度、把模拟输出安全送进 STM32F103 的 ADC、用软件校准去掉零电流偏置。适合正在做电机电流监测、电池放电检测或小型电源保护电路的开发者Keil 工程里需要的所有步骤下面都会落到可以直接改的代码上。2. 手册参数、GY-712模块接线与Keil初始化2.1 手册必须先看的三个参数ACS712 的手册里真正决定代码写法的参数只有三个零电流偏置、灵敏度、供电电压范围。零电流偏置是 VCC/2也就是说在 5V 供电时 OUT 引脚静止在 2.5V没有电流时也始终有一个直流分量不能像使用电流互感器那样直接从 0V 起步。灵敏度则直接对应标题中的 5A、20A、30A 三个型号单位是 mV/A它决定了 AD 值每变化一个计数代表多少电流。供电范围 4.5V 到 5.5V意味着这类模块不能使用 3.3V 供电这和许多 STM32F103 最小系统板上的 3.3V 系统形成第一个冲突点。手册给出的灵敏度是典型值实际传感器因为芯片批次、模块上分压电阻误差和电源电压浮动会有百分之几的偏差。更高精度的做法是给整个测量链路做两点标定而不是只依赖手册经验值。不过对大多数过流监测、电机运行状态判断场景先用手册典型值把链路打通再在代码里加入零偏校准就足够了。2.2 GY-712模块引脚与供电关系GY-712 是这类模块最常见的 PCB 命名板面上通常只有 VCC、GND、OUT 三个引脚有的版本还会引出 IP 和 IP- 两路大电流端子。VCC 接 5VGND 与 STM32F103 共地OUT 是模拟输出。模块背面丝印如果是 ACS712ELCTR-05B对应 5A 量程20A 后缀是 20A30A 同理。板载退耦电容很小只能滤掉高频自激无法替外部电源纹波背锅所以模块供电不要直接从电机电源或继电器电源支路拉取最好用独立的 5V 稳压源。上电后的第一个检查动作不是接单片机和写程序而是用万用表直流电压档测 OUT 到 GND 的电压。没有负载电流时这里应该是 2.5V 附近如果偏差超过 50mV先检查 5V 是否准确。模块的电流方向在 PCB 上通常有箭头标记电流从箭头方向流入输出电压在 2.5V 基础上正向偏置反向则负向偏置。这个方向性决定了最终换算出来的电流正负号是否有实际意义。2.3 与STM32F103连接时的分压设计直接拿 OUT 接 STM32F103 的 ADC 引脚是这套方案里最隐蔽的一个坑。STM32F103 的 ADC 参考电压是 VDDA大多数最小系统板是 3.3V而 ACS712 在 5V 供电下满量程输出电压可以达到 4.5V。以 5A 模块为例正向 5A 时 OUT 约为 3.425V已经超过 3.3V20A 模块正向满量程接近 4.5V远超出 ADC 测量范围。不做任何处理直接采集大电流段会直接削波读数停在 4095 却显示不出来真实距离。可靠做法是在 OUT 与 ADC 引脚之间加两个 10kΩ 电阻分压从中间取点进 ADC。这种分压结构不会影响传感器静置电压只需要在换算时把测得的电压乘以 2 还原成 OUT 电压。两个 10kΩ 电阻并联使用也可以但分压比必须用万用表实测确认因为普通电阻精度只有 1% 或 5%理论上算出的分压比和实际测量值可能差几个毫伏对应到电流上就是几十毫安误差。在 ADC 引脚到地之间并联一个 1nF 到 10nF 的电容可以滤掉一部分高频辐射干扰前提是 ADC 采样时间足够长否则等效输入阻抗会拉低分压电压。2.4 Keil下先配置RCC、GPIO和ADC时钟打开 Keil MDK 新建标准外设库工程后第一步不是写采样循环而是把 ADC1 的时钟、GPIO 模式和时钟分频配置好。下面代码假设分压中点接在 PC0对应 ADC1 的通道 10。如果使用的引脚不同要同步修改 GPIO 引脚和 ADC_RegularChannelConfig 的通道号。void ADC1_Init(void) { GPIO_InitTypeDef gpio; ADC_InitTypeDef adc; RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); gpio.GPIO_Pin GPIO_Pin_0; gpio.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOC, gpio); adc.ADC_Mode ADC_Mode_Independent; adc.ADC_ScanConvMode DISABLE; adc.ADC_ContinuousConvMode DISABLE; adc.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; adc.ADC_DataAlign ADC_DataAlign_Right; adc.ADC_NbrOfChannel 1; ADC_Init(ADC1, adc); ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); }代码逻辑说明RCC_APB2PeriphClockCmd 同时打开 ADC1 和 GPIOC 的时钟PC0 作为模拟输入不使用上下拉所以不需要配置 GPIO_Speed。RCC_ADCCLKConfig 将 ADC 时钟设置为 PCLK2 的 6 分频在 72MHz 系统主频下得到 12MHz这是 STM32F103 ADC 时钟上限分频比再小会导致转换结果非线性。ADC_NbrOfChannel 配成 1 表示只采集一个通道ADC_ScanConvMode 为 DISABLE说明不使用扫描模式也不需要开启 DMA。ADC_SampleTime_239Cycles5 使用最长的采样周期原因是分压网络输出阻抗在 10kΩ 级别采样保持电容需要足够时间完成充电。工程中 RCC_APB2PeriphClockCmd 的参数名必须与原理图一致如果接线在 PA1就改成 RCC_APB2Periph_GPIOA 和 ADC_Channel_1。最后两行校准是 STM32F103 特有的 ADC 自校准流程启动后要等校准位清除否则第一次采样值偏大。校准只是修正内部电容误差不解决外部分压电阻精度问题。3. 用STM32F103的ADC采样并换算成实际电流3.1 从AD值还原分压点电压ADC 返回的是一个 16 位变量但有效值只有 12 位范围是 0 到 4095。第一步必须把它转换成实际电压公式是 v_half adc * 3.3 / 4095。这里的 3.3V 是参考电压如果最小系统板使用的基准芯片实际输出是 3.28V直接把 3.3 带进公式会产生系统性偏差最好用万用表测一下 VDDA 再替换常量。第二步是把分压后的电压还原成 ACS712 的 OUT 电压也就是乘以实测分压比。如果按两个 10kΩ 电阻理想分压就是乘 2如果实测模块 OUT 电压是 2.512V分压点是 1.263V那么实际分压比就是 2.512 / 1.263 1.989这个值比纯理论推算更可信。3.2 完整电流换算函数把前面几步串起来可以写一个最直接的换算函数。输入是 ADC1_Read 返回的原始值输出是带符号浮点电流。float ADC_to_Current(uint16_t adc_value) { float v_half, v_sensor, current; v_half (float)adc_value * 3.30f / 4095.0f; v_sensor v_half * 2.0f; current (v_sensor - 2.50f) / 0.185f; return current; }逻辑说明v_half 是分压点电压v_sensor 是 ACS712 OUT 端电压2.50f 是零电流偏置0.185f 是 5A 量程模块灵敏度 数值单位是 V/A。如果要换 20A 模块只需要把灵敏度改成 0.100f30A 模块改成 0.066f。换算结果的正负由电流方向决定前面接线反了则正负号也会反对接线方向不确定时可以用一个小电流负载验证。3.3 三种量程的换算参数速查模块丝印量程灵敏度满量程输出偏移ACS712ELCTR-05B±5A185 mV/A±925 mVACS712ELCTR-20A±20A100 mV/A±2000 mVACS712ELCTR-30A±30A66 mV/A±1980 mV所有偏移都以零电流偏置 2.5V 为起点。5A 模块正向满量程 3.425V经过 2 倍分压后为 1.7125V20A 模块正向满量程 4.5V分压后 2.25V30A 模块正向满量程 4.48V分压后 2.24V。这三个值都在 3.3V 参考范围内因此使用两个 10kΩ 分压不会造成 ADC 削波。需要注意如果 ACS712 供电电压不是精确 5V零电流偏置和满量程输出都会跟着漂不能继续套用 2.5V 这个固定值。3.4 让采样值更稳的连续读取单次 ADC 转换结果本身含有噪声尤其 ACS712 输出是模拟霍尔信号在开关电源负载下会有明显的随机抖动。常见做法是连续读多次取平均代价是响应时间变长。下面函数在每次主循环调用时读取 32 次把平均值返回给上层。uint16_t ADC1_Read_Average(uint8_t times) { uint32_t sum 0; uint8_t i; for (i 0; i times; i) { sum ADC1_Read(); } return (uint16_t)(sum / times); }逻辑说明ADC1_Read 内部完成一次软件触发转换并等待 EOC 标志然后在 release 中返回寄存器值。连续调用不会冲突因为每次转换都是完整结束之后才返回。times 取 32 是我常用的经验值采样时间大约是 32 乘以几百微秒。如果被测电流是转速较高的电机长时间平均会把真实的电流峰值抹平这时应该把 times 降到 8或者在代码里把同一时刻的多通道数据用 DMA 方式固定下来而不是在时间上平均。3.5 用串口输出AD值和电流值验证链路把换算结果打印出来是判断整个信号链路是否正常的最快方法。使用串口 1 输出波特率 115200格式化打印 ADC 原始值、分压点电压和还原电流。打印前先关闭中断或者加互斥避免与通信中断产生数据错位。如果没有电流表加载只用手触碰 ACS712 输出引脚都会引起数值跳动这从侧面说明测量链路是通的但也说明输入端阻抗偏高实际应用中要在模块 OUT 端加大电容或使用电压跟随器。4. 零偏校准、数字滤波与故障排查4.1 为什么必须做零偏校准手册给的 2.5V 偏置是理想值但模块上电后由于 5V 电源误差、芯片个体差异和温度影响实际零偏可能在 2.47V 到 2.53V 之间。按 2.5V 带入计算在 5A 量程上会产生约正负 160mA 的电流误差对 20A、30A 量程影响更大。解决办法是在没有负载电流时实测零偏。这个过程叫软件校准只需要在系统启动后确认负载未通电调用一次采样函数得到零电流时的 AD 值再把当时的 ADC 值换算成实际偏置电压。float g_zero_voltage; void ACS712_Calibrate_Zero(void) { uint16_t adc_zero; adc_zero ADC1_Read_Average(64); g_zero_voltage ((float)adc_zero * 3.30f / 4095.0f) * 2.0f; }逻辑说明64 次平均是为了消掉随机噪声。g_zero_voltage 是最终换算函数里要用的零电流偏置它覆盖了模块输出偏置和分压电阻误差中所有和信号直流点相关的成分。调用这个函数的时机很关键必须保证 ACS712 上电时间超过 10 秒因为上电瞬间芯片内部偏置还没有稳定直接校准会把一个偏移量永久写进系统。温度变化会导致零偏漂移机械结构或散热条件变化较大的场合最好每隔一段时间或者在继电器吸合前重新校准一次。如果负载无法完全断电可以使用差分测量或者软件记录漂移趋势但那是进阶话题。4.2 中值滤波和滑动平均怎么选ACS712 输出噪声呈现两种形态一种是高频白噪声来自芯片内部电路和外部电源另一种是尖峰脉冲来自 PWM 负载的快速电流变化。白噪声适合用滑动平均滤波脉冲尖峰适合用中值滤波。滑动平均实现简单只需要保留最近一组数据取均值但对单个强烈干扰的响应慢会把尖峰扩散到多个周期。中值滤波对孤立脉冲有很好的抑制作用但需要排序占用一定 CPU 时间。对于 240Hz 工频磁场干扰滑动平均窗口取 16 或 32 点效果不错。对于开关电源引起的毛刺5 点中值滤波足够。uint16_t ADC1_Median_Filter(void) { uint16_t buf[5]; uint8_t i, j; uint16_t tmp; for (i 0; i 5; i) { buf[i] ADC1_Read(); } for (i 0; i 5; i) { for (j i 1; j 5; j) { if (buf[i] buf[j]) { tmp buf[i]; buf[i] buf[j]; buf[j] tmp; } } } return buf[2]; }逻辑说明这组嵌套循环对 5 个样本做升序排列取中间位置第 3 个值作为输出。如果遇到一个尖峰它最多只会出现在排序数组的两端不会影响中位数。注意这段代码没有 DMA采样过程中会阻塞主循环如果主循环还有其他实时任务建议把采样次数从 5 降到 3或者把采样放在定时器中断里。实际项目中我常把中值滤波和滑动平均级联使用先用中值滤掉尖峰再对中值输出做 8 点平均整体响应仍然比单纯 32 点平均快。4.3 故障排查AD值、偏置和读数异常先列一个最典型的故障表按出现频率排序。现象可能原因检查方法ADC 读数恒为 0分压电阻虚焊、GND 不共地万用表测分压点对单片机 GND 电压ADC 读数恒为 4095输入电压超过 3.3V、参考地未连接测 OUT 电压和分压点电压无电流时读数跳动大电源纹波、模块靠近功率线用示波器看 OUT 波形加大采样时间读数随负载变化但换算电流偏小分压比不对实测 OUT 和分压点电压计算实际分压比读数先正确后漂移芯片发热导致零偏漂移让模块散热量更大重新校准零偏其中 GND 不共地是最隐蔽的问题。ACS712 输出是相对自身 GND 的电压如果模块 GND 和 STM32F103 GND 之间存在电势差ADC 测量结果会在这个电势差上叠加直流偏移且该偏移随负载电流变化。解决办法是所有模拟信号地单点汇聚不要让电流回流通路经过传感器地线。5. 进阶用ACS712做过流保护与调试技巧过流保护是电流传感器最常见的延伸功能但直接拿当前采样值做阈值比较会出现继电器在临界点反复吸合的问题。正确做法是加入滞回区间。滞回比较器比普通比较器多出的部分是阈值上下限之间的过渡区域电流超过上限时进入保护状态需要电流回落到比上限低一些的下限才解除保护。#define CURRENT_OVER 3.00f #define CURRENT_HYST 0.20f static uint8_t protect_flag 0; void Over_Current_Check(float current) { if (protect_flag 0) { if (current CURRENT_OVER CURRENT_HYST) { protect_flag 1; GPIO_SetBits(GPIOB, GPIO_Pin_12); } } else { if (current CURRENT_OVER - CURRENT_HYST) { protect_flag 0; GPIO_ResetBits(GPIOB, GPIO_Pin_12); } } }逻辑说明保护状态进入条件是 3.2A退出条件是 2.8A中间的 0.4A 区间就是滞回区。这样的好处是当负载在 3A 附近小幅波动时保护输出不会来回抖动。GPIOB 的 12 脚可以驱动一个 LED 做状态指示也可以接继电器控制主回路但驱动继电器时还要在单片机和继电器之间加光耦和续流二极管避免感性负载断开瞬间损坏引脚。在这个基础上还有一个我从实际调试中总结出的技巧在串口输出当前电流的同时每隔一帧输出一次 ADC 原始值、实测偏置电压和分压比。如果发现电流读数异常先看 ADC 原始值是否正常原始值正常但电流不对说明换算参数错了原始值都不正常则是硬件接线和信号链路的问题。没有电流表时可以用一节干电池串一个大功率电阻制造一个几百毫安的已知电流校准结果远比完全依赖手册可靠。选择 5A、20A 还是 30A 的模块也要在使用油之前先确认传感器自身发热因为大电流铜排附近的温度会直接改变霍尔芯片零偏热量造成的偏移无法通过简单的常量校准完全消除。本文还有配套的精品资源点击获取