ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

STM32F103C8T6交流电压检测:从互感器采样到RMS计算完整方案

STM32F103C8T6交流电压检测:从互感器采样到RMS计算完整方案 简介这套基于STM32F103C8T6的交流电压检测工程定位为单片机实战入门资源适合学习交流电压有效值测量、模拟数字转换器采样与直接内存访问传输的开发者也可作为相关电子竞赛的参考项目方案。压缩包共包含201个文件其中C语言源文件34个、头文件37个并带有大量编译中间文件、Keil工程配置文件、可直接烧录的固件文件、链接映射文件与调试配置文件整体约6.54兆字节目录结构清晰便于按模块检索学习。根据站内统计已有4034人浏览学习。通过该工程可以系统理解这款单片机的外设配置流程包括模拟数字转换器的工作模式、采样时间设定与中断传输机制还能追踪均方根算法在工程中的实际落地方式覆盖数据采集、平方累加、均值计算和开方输出等关键步骤。工程代码还涉及定时器、串口和系统时钟等相关外设配合数据手册与固件库学习可快速掌握嵌入式电压测量项目从硬件搭接到软件滤波的完整思路。 STM32F103C8T6这块板子玩嵌入式的基本都不陌生Cortex-M3内核、72MHz主频、20KB RAM便宜量大国产替代方案也成熟。但不少人第一次用它做交流电压检测时会在“硬件怎么接”“ADC怎么采”“算出来的数怎么不对”这几个环节反复卡壳。这篇文章我就把这套检测方案的完整思路、电路参数计算、HAL库程序实现和调试中踩过的坑一次性说清楚给准备做智能电表、电压监测器或工业数据采集的朋友一个可以直接抄作业的参考。先说清楚这个项目到底解决什么问题STM32的ADC只能测量0到3.3V的正向直流电压而市电是220V、50Hz的正弦交流电有正半周也有负半周峰值能达到310V左右。直接把交流电接进ADC引脚板子瞬间就会烧掉。所以“基于STM32F103C8T6检测交流电压”这个项目本质上要解决三件事把220V高压变成ADC能承受的低压信号把交流信号的负半周抬升到ADC量程内再用软件算法还原出真实的电压有效值。这套处理流程在工业现场和智能硬件里非常通用搞懂一次换其他单片机也能直接迁移。1. 整体设计思路为什么用F103C8T6做交流电压检测1.1 需求拆解从“测电压”到“测交流电压”很多人第一次拿到这个需求第一反应是“ADC采样不就行了”但实际上交流电压检测和直流电压检测完全是两码事。直流电压检测只需要分压电阻把电压降到ADC量程内然后直接采样换算即可整个过程是静态的。交流电压则是一个随时间变化的正弦波正负交替频率固定国内50Hz有效值和峰值之间还存在√2的倍数关系。这些特性决定了你不能像测直流那样简单粗暴地采样必须从硬件和软件两个维度重新设计。再往深一层说交流电压检测还分“只测有效值”和“既要有效值又要波形/相位/频率”两种需求。如果是前者可以用真有效值芯片比如AD736直接输出直流信号给ADC省事但增加物料成本如果是后者就必须保留完整的波形信息走“互感器偏置同步采样”的路线。我这次选的是后者因为只测有效值的话用F103C8T6的ADC就太浪费了而且后续扩展功率计算、谐波分析时还得重新设计硬件不如一步到位。1.2 方案选型互感器隔离采样与直接分压的取舍交流电压检测的硬件前端常见就三条路电阻分压、电压互感器、霍尔电压传感器。霍尔传感器精度高但成本也高民用电表里都很少用这里就不多说了重点对比前两个。电阻分压是最省钱的方式两个电阻串联把220V降到3.3V以内再接个运放做电压跟随和偏置就行。但它最大的问题是“没有电气隔离”——高压侧和低压侧共地一旦后级电路出故障高压会直接窜进单片机和调试电脑非常危险。而且分压电阻上的功耗也不小在220V下用两个1206封装的电阻长期运行发热明显。电压互感器则是把初级线圈和次级线圈用磁芯隔开实现电气隔离次级输出的是与初级电流成比例的电流信号再通过采样电阻转成电压。这样做的好处是高压侧故障不会殃及单片机安全性大大提升精度也能做到0.1%以上。价格也就几块钱综合性价比是工业产品里最常用的方案。我最终选的是精密电流型电压互感器额定输入2mA输出2mA具体参数计算后面详细讲。方案对比 | 方案 | 电气隔离 | 精度 | 成本 | 体积 | 适用场景 | |------|---------|------|------|------|----------| | 电阻分压 | 无 | 中等 | 极低 | 最小 | 低压电子制作、临时验证 | | 电压互感器 | 有 | 高 | 中等 | 中等 | 工业采集、智能电表 | | 霍尔传感器 | 有 | 最高 | 高 | 大 | 高精度计量、电力监测 |还有一个细节容易忽略互感器输出的是电流信号抗干扰能力比电压信号强很多特别适合工业现场这种电磁环境复杂的场景。信号在传输过程中受干扰幅度会变化但电流环的阻抗低不容易耦合噪声这是互感器方案的隐性优势。2. 硬件电路设计从互感器到ADC引脚的完整链路2.1 电压互感器选型与采样电阻计算选互感器时首先明确输入输出规格。我用的这款额定输入电流2mA额定输出电流2mA变比1:1。初级串联一个限流电阻接到电网火线上电阻取值根据欧姆定律计算220V交流的有效值对应2mA那么限流电阻就是220V / 0.002A 110kΩ。考虑到电阻的功率和耐压我用的是两个56kΩ的金属膜电阻串联总阻值112kΩ误差在可接受范围内。选串联而不选单个电阻是为了分摊耐压和功耗——110kΩ电阻在220V下功耗约0.44W单个电阻会发热严重两个电阻串联后每个只承担一半功耗可靠性更高。次级输出2mA电流经过采样电阻转换成电压。采样电阻的取值直接决定ADC看到的信号幅度我的计算思路是这样的STM32的ADC参考电压是3.3V但交流信号经过偏置后中心点通常在1.65V附近信号摆幅上下各1.65V也就是说采样电阻上的峰值电压不能超过1.65V。2mA是有效值对应峰值电流约为2.828mA那么采样电阻最大可取 1.65V / 0.002828A ≈ 583Ω。为了留出余量我选了470Ω这样ADC输入端的信号峰值大约2.66V既不会削顶也充分利用了ADC量程换算精度更高。2.2 偏置电路与ADC输入保护前面提过交流信号有负半周而STM32的ADC只能接受0到3.3V的正电压所以必须在信号进入ADC之前把整个波形往上抬升。最常用的办法是加法电路把互感器次级采样后的交流信号叠加一个1.65V的直流偏置使原本在-1.33V到1.33V之间摆动的信号变成0.32V到2.98V完全落在ADC量程内。偏置电路我用的是两只10kΩ电阻从3.3V和GND分压得到1.65V再经过一个运放构成的电压跟随器做缓冲避免后级电路拉低偏置电压。运放用的是LM358双电源供电太麻烦这里用的是单电源5V方案。需要注意的是LM358的输出电压范围不是轨到轨的实际最大输出约为VCC-1.5V3.5V最小约为0.1V左右输入信号接近上限的时候可能会轻微非线性但考虑到ADC采样值最大不超过3.3V余量还算充分。如果对精度要求更高可以换轨到轨运放比如MCP6002。ADC输入保护也很关键不是简单串个电阻就完事。我的做法是在ADC引脚和地之间并联一个3.3V的TVS管双向同时在信号路径上串联一个1kΩ电阻限流。TVS管能吸收瞬态尖峰串联电阻和ADC输入端的寄生电容还能组成一个低通滤波器对高频噪声有一定抑制作用。实测下来在继电器启停、电机启动这种恶劣工况下ADC输入端的波形依然干净没有烧过引脚。2.3 过零检测与频率测量电路如果只是测电压有效值过零检测电路并非必需但一旦涉及相位测量、功率计算、谐波分析过零检测就必不可少。它的作用是给单片机提供一个“正弦波从负到正的过零点”脉冲信号用来确定每个周期的起始位置。我的过零检测电路从互感器次级取信号经过一个LM393比较器和1.65V基准电压比较输出方波送给STM32的输入捕获引脚。LM393是开漏输出需要接上拉电阻到3.3V。这套电路测试下来有个经验比较器的滞回要加一点大概5到10mV否则正弦波在过零点附近斜率较小噪声容易引起输出抖动导致捕获的时间戳抖动几十微秒换算成相位误差就非常可观了。过零检测还有一个价值可以实时计算电网频率。通过测量两次上升沿之间的时间差就能算出频率值。标准市电是50Hz但实际运行中会在49.8到50.2Hz之间波动。频率值可以用来动态调整采样窗口、校准RMS计算的时间基准对整个系统的测量稳定性帮助很大。3. 软件实现HAL库下的采样、计算与校准3.1 定时器触发ADC采样确保窗口完整交流电压检测的程序架构最重要的一点是“同步采样”——采样的时间点必须和被测信号的周期严格对应。如果用一个固定的采样率去采而电网频率有微小漂移采样点数可能覆盖不完一个完整周期或者多采了半个周期计算出来的有效值就会有周期性波动。我的做法是用定时器触发ADC采样而不是在主循环里轮询。具体配置TIM2的更新事件触发ADC1的注入组转换每个PWM脉冲触发一次采样。采集N个点我用的64点然后关闭定时器对采样数据进行RMS计算计算完再开启下一轮。这样每个采样窗口都能覆盖约32个完整周期64点/2点每周期用窗口内的数据计算一次有效值刷新率大约每秒50次能满足绝大多数监控场景。代码里我用的HAL库配置流程是先初始化ADC和DMA然后配置TIM2的PWM模式把TIM2的TRGO事件连接到ADC的触发源。具体的初始化代码如下关键部分/* ADC1初始化 */ hadc1.Instance ADC1; hadc1.Init.ScanConvMode DISABLE; hadc1.Init.ContinuousConvMode DISABLE; hadc1.Init.DiscontinuousConvMode DISABLE; hadc1.Init.ExternalTrigConv ADC_EXTERNALTRIGCONV_T2_TRGO; hadc1.Init.DataAlign ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion 1; HAL_ADC_Init(hadc1); /* TIM2配置为PWM模式TRGO触发ADC */ htim2.Instance TIM2; htim2.Init.Prescaler 72 - 1; /* 1MHz计数频率 */ htim2.Init.Period 1000 - 1; /* 1kHz触发频率 */ HAL_TIM_PWM_Init(htim2);这里有个容易被坑的点ADC的采样时间必须足够长保证内部采样电容能充分充电。我配置的是ADC_SAMPLETIME_239CYCLES_5即约3.3μs采样时间在信号内阻1kΩ的情况下完全足够。如果采样时间太短采样值会有轻微衰减而且精度越高的ADC越明显。3.2 有效值RMS算法实现与代码细节交流电压的有效值数学定义是一个周期内瞬时值的平方再取平均再开方这就是RMSRoot Mean Square算法。在数字系统里离散化后的公式很简单对N个采样点先求平方和除以N再开根号。由于信号经过偏置电路抬升了1.65VADC采集到的值里包含了直流偏置分量。计算RMS之前必须先把直流分量减掉否则算出来的结果会偏大。我先把每个采样点的电压值转换成实际电压减去1.65V偏置再进行RMS计算。代码如下#define SAMPLE_NUM 64 #define ADC_REF 3.3f #define OFFSET 1.65f #define SCALE_FACTOR 220.0f / 1.33f /* 实际电压与采样电压的比例 */ float calc_rms(uint16_t *adc_buf, uint16_t len) { float sum_sq 0.0f; for (uint16_t i 0; i len; i) { float volt (adc_buf[i] * ADC_REF / 4095.0f) - OFFSET; sum_sq volt * volt; } float rms sqrtf(sum_sq / len); return rms * SCALE_FACTOR; }SCALE_FACTOR这个系数怎么来的采样电阻470Ω互感器变比1:1所以次级峰值电压为 2.828mA × 470Ω ≈ 1.329V。也就是说实际220V有效值对应ADC输入端1.33V有效值考虑了偏置后的峰峰值约3.76V稍大于设计的2.66V会不会削顶这个问题我在后面调试部分专门说。所以电压比例系数是220 / 1.33 ≈ 165.4。实际调试时这个系数不能只看计算必须用标准源和万用表校准一次后面再详细讲。3.3 过零检测与频率计算程序实现频率测量使用TIM3的输入捕获功能捕获上升沿。每捕获一次读取CCR寄存器的值与上一次捕获值做差得到两次过零之间经过的定时器计数个数换算成周期和频率。配置代码关键部分/* TIM3输入捕获配置 */ htim3.Instance TIM3; htim3.Init.Prescaler 72 - 1; /* 1MHz计数 */ htim3.Init.Period 0xFFFF; HAL_TIM_IC_Init(htim3); /* 捕获通道1上升沿 */ sConfig.ICPolarity TIM_INPUTCHANNELPOLARITY_RISING; sConfig.ICSelection TIM_ICSELECTION_DIRECTTI; sConfig.ICPrescaler TIM_ICPSC_DIV1; sConfig.ICFilter 0x0F; /* 数字滤波滤除毛刺 */ HAL_TIM_IC_ConfigChannel(htim3, sConfig, TIM_CHANNEL_1);注意ICFilter这个参数直接对应之前说的“滞回”需求。虽然硬件上比较器已经加了一点滞回但在电磁环境差的场合数字滤波能进一步防止误触发。实测在接触器吸合的瞬间如果不加数字滤波频率值会突然跳变好几个Hz加了滤波后稳定很多。频率计算方面读取到两次捕获的差值后用1MHz除以差值就是频率。这里要处理定时器溢出情况由于TIM3是16位1MHz计数下溢出周期只有65.535ms而50Hz信号周期是20ms差值的两倍才能覆盖一个周期所以我在中断里加了一个计数器处理溢出。这个细节如果没注意频率数值会偶尔跳变到错误范围排错时非常容易忽略。3.4 校准方法用标准源修正比例系数上面提到的SCALE_FACTOR理论计算用的元件参数是标称值实际电阻、互感器都有误差必须做一次校准。校准方法很简单用一台精度较高的万用表或者标准信号源测出当前电网实际电压同时让程序打印出当前计算值然后用“实际值/计算值”修正SCALE_FACTOR写进程序里的常量或者EEPROM中之后重启就会生效。我在校准过程中发现一个关键问题互感器在低压段比如几十伏存在非线性导致校准系数在小电压和大电压下不完全一致。所以建议至少校准两个点一个是接近满量程的电压比如220V一个是低电压段比如50V左右用两点拟合修正系数能明显改善非线性误差。如果对精度要求更高可以存一个校准表做分段线性插值不过对于大多数监控场景两点校准已经够用。校准时的另一个细节标准万用表测的是真有效值而程序计算也是真有效值两者原理一致才能对得上。如果用普通整流式万用表测非正弦波信号读数会偏大或偏小校准就没意义了。4. 踩坑记录与排查技巧4.1 采样窗口不同步导致的数值波动这是我调试过程中遇到最顽固的问题。程序刚跑起来的时候有效值计算结果是“稳定”的但观察一段时间发现数值有小幅周期波动大约每几秒钟波动一次幅度在±2V左右。起初以为是互感器精度问题后来用示波器查看ADC波形发现采样触发信号的频率和电网频率不是完全同步的两者之间存在缓慢的相位漂移。原因分析TIM2的触发频率固定为1kHz而电网频率不是严格的50Hz可能是50.03Hz或49.97Hz。每周期采样20个点但采样窗口可能覆盖20.01个周期或者19.98个周期导致计算的RMS值随相位漂移而波动。解决思路有两种一种是用过零检测电路输出的方波作为采样窗口的起点每次从过零点开始采样固定个数的点确保窗口和周期同步另一种是用频率测量值实时调整TIM2的溢出值让采样频率跟踪电网频率。我用的是第一种方案更简单可靠。实现上就是DMA采集完成后读取过零脉冲的计数标志判断是否已同步没同步则丢弃本帧数据重新等待过零信号到来再开启采样。4.2 峰值超过ADC参考电压导致的削顶失真前面计算采样电阻时我粗略估算了峰值电压约2.66V留有余量但实际装上后却发现大电压时数据异常偏小。排查后发现问题出在LM358运放的输出摆幅限制——信号经过运放缓冲后输出摆幅达不到理论计算值正半周接近3.0V左右时运放已经进入饱和区波形被削顶导致ADC采集到的信号峰值不足。这也验证了之前提到的“不要只看计算值”的经验。解决方法有两个方向要么把采样电阻从470Ω降到330Ω把信号摆幅减小留出更多余量要么换轨到轨运放。我最终换成了MCP6002输出摆幅接近电源轨问题彻底解决。这个案例给所有做模拟前端设计的同行提个醒运放的输出范围不是VCC和GND一定要查数据手册特别是单电源供电时。4.3 ADC采样值跳动、接地问题与参考电压稳定ADC采样值在排除信号链问题后依然有跳动尤其是采集的数据最后两位不稳定。这类问题的根源通常是地线处理和参考电压的稳定性。STM32的ADC模块内部有一个参考电压但默认情况下是以VDDA为参考的VDDA的稳定性直接决定ADC精度。我的板子上VDDA和VDD之间只接了一个0.1μF去耦电容高频噪声吸收不够ADC跳动明显。后来在VDDA引脚旁边增加了一个4.7μF的钽电容并联0.1μF陶瓷电容信号稳定了很多。另外模拟地和数字地在PCB上要单点连接不要把ADC的参考地跟继电器、电机等大电流回路共用地线。程序层面也可以做一些处理计算RMS时对原始ADC值做简单的滑动平均滤波或者多次采样取平均。我在每个采样点用了两次采样取平均牺牲了一点采样率换来了明显更稳定的结果。注意这里不能对整个RMS结果做低通滤波否则响应速度变慢实时性下降。4.4 常见问题速查表现象可能原因解决办法偶发测量值突跳采样窗口与周期未同步用过零检测同步采样窗口显示值偏大未扣除直流偏置计算RMS前减去偏置电压显示值偏小且大电压时严重运放输出削顶换轨到轨运放或降低采样电阻频率读数跳变过零检测噪声干扰增加比较器滞回开启输入捕获数字滤波ADC末位跳动VDDA不稳定增加去耦电容模拟地单点接地互感器发热限流电阻选小了计算并确认限流电阻阻值必要时加大功率调试时还有一个非常实用的技巧在程序里加一个“裸数据输出”模式把经过偏置处理后的瞬时采样值直接通过串口打印出来用串口绘图工具显示波形。这样能直观看到正弦波是否削顶、是否偏置正常、是否有抖动比只看一个有效值数字高效得多。我在开发阶段一直保持这个调试通道定位问题快了很多。5. 实用扩展从电压检测到更多应用场景这套“互感器偏置同步采样”的框架改一改就能用在好几个地方。最直接的是做单相交流电压监测仪LCD或OLED实时显示电压、频率超限报警。再加一个电流互感器和乘法运算就能实现功率计算得到有功功率、功率因数等参数。如果再进一步在零点和峰值处多采集几个点做FFT变换还能分析谐波含量这就是简易电能质量分析仪的雏形了。程序架构上建议把采样、计算、显示分成独立模块通过消息队列或者标志位通信。比如DMA采集完成中断里只负责把数据搬进环形缓冲区主循环里做RMS计算和结果显示避免在中断里做浮点运算。F103C8T6的72MHz主频做64点RMS计算加频率计算耗时大约几百微秒完全不是瓶颈。C8T6虽然只有64KB Flash和20KB RAM但跑这套程序连一半资源都用不到性价比确实高。如果想降低功耗做电池供电的便携式设备可以考虑用STM32的掉电模式和RTC唤醒每秒钟唤醒一次采集数据其余时间休眠。这个改动对硬件没有额外要求软件上增加低功耗管理即可实测下来电流可以从几十毫安降到几毫安以内。最后再分享一个实际经验做这类电力测量项目安全意识要放在第一位。互感器隔离方案虽然比电阻分压安全但上电调试时务必用隔离变压器给整个系统供电示波器要用隔离探头或者差分探头养成“先接保护、再上电”的习惯。我在实验室调试初期就因为偷懒没接隔离变压器示波器地线夹碰到高压侧结果烧坏了一个探头还好人没事。这种事故虽然不会发生在每个人身上但只要发生一次就足够让人长记性希望大家不要走这个弯路。本文还有配套的精品资源点击获取
返回列表