1. 硬件选型与连接:从零搭建测量系统
第一次用STM32F103配ACS712测交流电流时,我对着淘宝五花八门的电流互感器发了半小时呆。后来实测发现,硬件选型不当会导致测量误差直接翻倍。先说核心配置:我用的正点原子MiniSTM32开发板(MCU为STM32F103RCT6),搭配ACS712-30A模块(量程±30A)。这里有个坑——ACS712分5A/20A/30A三个版本,30A版本的灵敏度最低(66mV/A),但测量笔记本充电这种场景完全够用。
电流互感器选型更有讲究。我买的10A/5A穿心式互感器,关键参数是匝数比。包装上标着"10A:5A 3匝",意思是火线绕3圈时,10A初级电流对应5A次级电流。实际接线时要把插座线剥开(注意安全!),单独让火线穿过互感器中心孔绕3圈。有次偷懒只绕了1圈,测得电流值直接缩水三分之二。
硬件连接示意图:
- ACS712的VCC接5V,GND接地
- OUT引脚接STM32的PA1(ADC1通道1)
- 电流互感器次级两根线随便接ACS712的输入端子(不分极性)
- 务必在ACS712输出端加0.1μF滤波电容!这个细节能减少50%以上的高频噪声
2. STM32CubeMX配置:ADC+DMA+TIM三重奏
用STM32CubeMX配置时,这三个外设的联动就像交响乐配合。TIM定时触发ADC采样,DMA负责搬运数据到内存,CPU全程不参与,实测采样率轻松跑到10kHz。具体操作:
打开CubeMX后先设置时钟树:
- HCLK设为72MHz(STM32F103的满血状态)
- APB2时钟设为72MHz(ADC所在总线)
接着配置ADC1:
- 选择通道1(对应PA1)
- 分辨率设为12位(4096级)
- 扫描模式禁用(单通道)
- 连续转换模式禁用(由TIM触发)
- DMA设置成Circular模式(循环缓冲)
TIM3的配置最易出错:
- 时钟源选内部时钟
- 分频系数设为7200-1(72MHz/(7200*1Hz)=10kHz)
- 计数模式Up
- 在Trigger Output里把TRGO选为Update Event
最后生成代码时,记得勾选"Generate peripheral initialization as a pair of .c/.h files"。这样ADC、DMA、TIM的配置代码会分开,后期调试更方便。
3. 代码实战:从采集到校准
生成的代码需要三处关键修改。首先在main.c定义全局变量:
uint16_t adc_buff[1000]; // 存放1000个采样点 volatile uint8_t AdcConvEnd = 0; // DMA完成标志然后在main()函数启动ADC:
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buff, 1000);重点来了——校准公式。ACS712空载时输出2.5V,但实际测量我的模块是2.48V(个体差异)。转换公式要这样写:
float current = (adc_buff[i] * 3.3f / 4095 - 2.48f) / 0.066f;其中0.066是30A版本的灵敏度(66mV/A)。打印数据时建议用科学格式:
printf("%.3e %.3e\n", i*0.0001f, current);遇到过最头疼的问题是零点漂移。解决方法是在设备断电时采集100次ADC值取平均,作为动态零点补偿。实测可将静态误差从±0.3A降到±0.05A。
4. Python可视化:让数据会说话
串口数据用Python处理时,pySerial+Matplotlib组合比LabVIEW更轻量。先安装依赖:
pip install pyserial matplotlib numpy完整的Python脚本示例:
import serial import matplotlib.pyplot as plt import numpy as np ser = serial.Serial('COM3', 115200, timeout=1) data = [] for _ in range(1000): line = ser.readline().decode().strip() if line: t, i = map(float, line.split()) data.append([t, i]) data = np.array(data) plt.figure(figsize=(12, 4)) plt.plot(data[:,0], data[:,1], lw=1) plt.title("Laptop Charging Current (10kHz Sampling)") plt.xlabel("Time (s)") plt.ylabel("Current (A)") plt.grid(True) plt.tight_layout() plt.show()进阶技巧:添加移动平均滤波
window_size = 50 weights = np.ones(window_size) / window_size smoothed = np.convolve(data[:,1], weights, mode='valid')5. 避坑指南:血泪经验总结
电源隔离问题:有次测量电钻电流时,STM32突然重启。后来发现是电机启停导致地线干扰,用光耦隔离后解决。
采样率陷阱:最初设1kHz采样率测充电器,完全看不到高频纹波。后来改到10kHz才发现电流波形有100us级的尖峰。
量程选择:测小电流(<1A)建议换ACS712-5A版本(灵敏度185mV/A),30A版本测0.1A以下基本没精度。
导线发热:连续测10A以上电流时,电流互感器次级导线会明显发热,建议换AWG18以上线径。
Python内存泄漏:长时间连续采集时,记得定期清空serial缓冲区,否则内存会缓慢增长直到崩溃。