ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32交流电压检测实战:硬件隔离、偏置电路与RMS算法全解析

STM32交流电压检测实战:硬件隔离、偏置电路与RMS算法全解析 简介这是一份基于STM32F103C8T6单片机实现交流电压有效值检测的完整工程资源面向嵌入式开发者、电子竞赛备赛者以及需要掌握电力参数采集的工程技术人员。资源围绕交流电压测量这一典型应用覆盖STM32F103C8T6的ADC多通道采集、连续转换模式、DMA数据传输以及均方根RMS算法实现等关键技术点解决了从硬件分压/整流电路到软件滤波与数值计算的全流程设计问题。包内共201个文件以C源文件、头文件、编译中间文件.o/.d、HAL库及工程配置为主另含hex烧录文件、Keil工程文件与调试描述文件便于直接打开工程阅读、编译和烧录验证压缩包大小6.54MB。已有4034人学习下载。通过该工程读者可快速获取一套可运行的交流电压测量程序框架重点理解DMA配合ADC实现高频采样与RMS换算的软件写法也可参考其硬件连接与安全处理思路用于课程设计、项目移植或竞赛改造。 把220V交流电直接接到STM32F103C8T6的ADC引脚上会发生什么大概率是芯片当场冒烟。别笑我在新手群里见过不止一次这种问题拿了块最小系统板就想测市电结果不仅没测到电压还赔了一块板子。检测交流电压这个需求其实非常常见智能家居里的过压欠压保护、充电桩的输入监控、配电箱的电压采集都用得上。但交流电真不是拿ADC“怼”一下就能采的这篇文章就把完整的原理、硬件电路和基于HAL库的软件程序一次讲清楚适合正在做电压检测项目的开发者和嵌入式爱好者参考。1. 交流电压不是拿ADC直接怼采样前的三个现实难题做这个项目之前得先想明白一个问题为什么不能把交流电压直接送到STM32F103C8T6的ADC采样引脚答案不只是一个“会烧芯片”这么简单这里面藏着三个绕不开的现实约束。1.1 幅度与安全311V的峰值不是闹着玩的我们常说的220V交流电指的是有效值。市电波形是50Hz的正弦波它的峰值电压是220 × √2 ≈ 311V正半周最高冲到311V负半周跌到-311V。而STM32F103C8T6的ADC输入范围是0~3.3V超出这个范围哪怕一点点芯片IO引脚内部的保护二极管就会导通轻则采样值被钳位重则直接烧掉引脚甚至整个芯片。更重要的是安全隔离问题。220V市电是致命电压如果板和电网之间没有隔离一旦接线错误、绝缘破损或者地线处理不当单片机、调试器、电脑都会带上危险电压。我见过有人用电阻分压直接采样市电板子倒是没烧但MCU的GND和市电零线是共地的调试器连上去的一瞬间电脑USB口直接保护。做交流电压检测隔离是底线不是可选项。1.2 双极性信号ADC根本采不到负半周就算你用一个精密电阻分压网络把311V降压到3.3V以内第二个问题马上暴露出来正弦波有负半周。ST的ADC是单极性输入只能识别0到3.3V之间的正电压负电压送进去要么被钳位到0要么损坏引脚。所以必须在硬件上做一个“电平抬升”把以0V为中心的双极性信号整体平移到以1.65V为中心的单极性信号。这样正半周在1.65V以上负半周在1.65V以下ADC才能完整采到一个周期的波形。这就是后面要讲的偏置电路的核心作用。1.3 目标值问题峰峰值除以根号2不靠谱我们最终要得到的是交流电压的有效值也就是RMS。很多新手会觉得ADC采到峰值然后除以√2不就行了这在波形是完美正弦波时确实成立但现实中的电网电压经常有谐波畸变、毛刺噪声波形根本不是标准正弦。峰值法在波形畸变时误差会非常明显而RMS计算是对所有采样点做平方、平均、开方它天然地包含了波形畸变的影响测出来的才是真有效值。总结一下要做一个能用的交流电压检测程序信号链路必须是高压交流 → 隔离降压 → 电平抬升到单极性 → ADC采样 → 软件计算RMS。前面两步靠硬件后面两步靠程序这篇文章两个部分都会覆盖。2. 硬件方案电压互感器加偏置电路的设计要点硬件上可选方案不少但真正适合STM32F103C8T6这种低成本项目的其实就一两种。我这部分把方案对比和参考电路参数都列出来你照着搭就行。2.1 三种主流方案横向对比交流电压采样硬件方案市面上常见的有三种各有各的适用场景方案隔离能力精度成本适用场景电阻分压无隔离危险高极低仅限低压交流测量或实验室浮地环境电压互感器完全隔离中高低市电电压检测、过压欠压保护霍尔传感器完全隔离高高高频交流、大动态范围测量我的建议非常明确做220V市电检测直接选电压互感器方案。霍尔传感器精度确实高但一个传感器几十块甚至上百块做产品根本没必要电阻分压虽然便宜到可以忽略但安全风险太大工程上不该用。ZMPT101B这类微型电压互感器十几块钱一个隔离耐压几千伏精度对于电压监控来说完全够用。2.2 一个可用的参考电路及参数计算我用的是ZMPT101B电压互感器配合一个单运放做电平抬升。这个方案的典型连接方式是互感器初级串两个100kΩ电阻次级并联一个采样电阻然后把次级差分信号送到运放做偏置加法。以220V为例两个100kΩ串联后总限流电阻是200kΩ初级电流约为220V / 200kΩ ≈ 1.1mA。ZMPT101B变比是1:1次级电流也是1.1mA。如果次级并联820Ω采样电阻得到的电压是1.1mA × 820Ω ≈ 0.9V RMS峰值约1.27V。叠加1.65V偏置后波形范围在1.65 - 1.27 0.38V到1.65 1.27 2.92V之间离ADC的0V和3.3V都有余量。当市电升到250V时峰值约1.44V最高会到3.09V仍然不削顶。这个参数设计是经过计算留足余量的你直接照用即可。运放部分我用的是经典加法电路正输入端通过10kΩ接互感器输出信号同时通过10kΩ接1.65V偏置电压反相输入端直接接输出构成跟随。输出就是信号 1.65V偏置电平的稳定性直接决定后续零点是否漂移所以1.65V不要从电位器取最好用两个1%精度的10kΩ电阻从3.3V分压后再接一个运放做缓冲输出这样带载能力也够。2.3 模块化方案和使用注意事项如果你不想自己搭运放电路市面上有现成的ZMPT101B交流电压检测模块模块上集成了互感器、运放和可调电位器输出端直接就是叠加好偏置的交流信号接到STM32F103C8T6的PA0引脚就能采。但用模块有一个坑很多模块默认是按5V供电、2.5V偏置设计的输出信号范围是0~5V。如果直接接在3.3V的ADC引脚上正半周超过3.3V的部分会被削掉采样值满量程测出来数据完全错误。用这种模块接STM32F103C8T6必须改一下把模块供电改到3.3V让偏置电压相应降到1.65V左右或者把模块输出串一个电阻再接ADC同时用两个二极管做钳位保护。选模块时优先选标称支持3.3V供电的版本能省掉很多麻烦。另外无论自己搭还是用模块建议在ADC引脚对地并联一个0.1μF电容做高频滤波能明显降低采样抖动。3. 软件链路从ADC原始值到有效值的完整实现硬件把交流信号处理成1.65V为中心的0~3.3V单极性信号之后剩下的就是软件的事了。整个程序的核心链路是定时器触发ADC采样 → DMA把数据搬到内存 → 主循环或中断里算RMS和频率。我是用STM32CubeMX配合HAL库实现的代码结构清晰也方便移植。3.1 CubeMX里这几项配置不能错先在CubeMX里创建STM32F103C8T6工程打开ADC1、DMA1、TIM2和USART1关键配置如下ADC1开启通道IN0对应PA0引脚分辨率12位扫描模式关闭连续转换关闭外部触发源选择Timer 2 Trigger Out event采样时间选55.5周期开启DMA Continuous Request。DMA1选择ADC1方向PeripheralToMemory模式Circular循环模式数据宽度Half Word内存地址自增。TIM2预分频72-1计数周期100-1触发输出TRGO选择Update Event。这样72MHz主频经过72×100分频后恰好产生10kHz的更新事件也就是每100μs触发一次ADC采样。USART1115200波特率用于把计算结果打印出来。这里最容易出错的是忘记开启DMA的Continuous Request或者ADC外部触发源选错。配置好之后CubeMX生成的代码里会主动调用HAL_ADC_Start_DMA(hadc1, (uint32_t*)adc_buf, SAMPLE_SIZE)这个启动函数必须放在main函数里初始化之后不能漏。3.2 采样率与缓冲区长度怎么定10kHz采样率对应的采样间隔是100μs一个50Hz工频周期20ms正好采200个点。我建议缓冲区设置成400个点也就是40ms覆盖连续两个完整周期。为什么要两个周期因为RMS计算理想情况下是对整数个周期求平均如果只采一个周期万一缓冲区起始位置不在过零点计算结果的波动会比较大采两个周期再做平均误差明显减小。采样率的确定也要讲一下道理。理论上根据奈奎斯特定理采样率必须大于信号最高频率的两倍但实际工程上建议至少10倍以上。市电里常见的谐波到5次250Hz已经很显著了10kHz采样率能覆盖到100次谐波都不成问题而且每周期200个采样点对RMS计算的精度来说也足够了。除非你要做高次谐波分析否则10kHz是性价比最高的选择。3.3 RMS计算与直流偏置消除软件计算RMS之前必须先把直流偏置消掉这是最容易算错的地方。ADC采到的原始值包含了1.65V偏置对应的数字量3.3V对应40961.65V对应约2048如果直接对原始采样值做平方平均这个直流分量会被当成交流有效值的一部分算进去导致结果大得离谱。正确做法是分两步void ProcessACVoltage(uint16_t *buf, uint16_t len) { uint32_t raw_sum 0; float offset, sum_sq 0.0f, rms_lsb, rms_volt; uint16_t i; /* 第一步求平均值得到直流偏置对应的ADC值 */ for (i 0; i len; i) { raw_sum buf[i]; } offset (float)raw_sum / len; /* 第二步每个采样点减去直流偏置后平方累加 */ for (i 0; i len; i) { float v (float)buf[i] - offset; sum_sq v * v; } /* 开方求RMS单位是LSB */ rms_lsb sqrtf(sum_sq / len); /* LSB转换为电压再乘以互感器变比校准系数 */ rms_volt rms_lsb * 3.3f / 4096.0f; voltage_rms rms_volt * g_ratio; }这里的g_ratio就是整个采样链路的电压变比校准系数它包含了互感器匝比、采样电阻、运放增益等所有硬件环节的总比例。比如前面那个参考电路里220V对应互感器次级0.9V RMSADC端也是0.9V RMS运放增益为1那么g_ratio 220 / 0.9 ≈ 244.4。具体数值最好通过实际校准来确定下一章细说。3.4 过零检测与频率测量除了有效值很多项目还需要测量交流电频率判断电网是否异常。基于已有采样数据用软件过零检测就能实现不需要额外硬件。思路是先把采样值和直流偏置比较找从负变正的上升穿越点为了防抖加一个滞回区间只有跨过偏置值一定范围比如正负20个LSB才认为是一次有效过零。#define HYST_LSB 20 uint16_t DetectFrequency(uint16_t *buf, uint16_t len, float offset) { uint16_t i; uint16_t rising_crossing 0; float prev (float)buf[0] - offset; for (i 1; i len; i) { float v (float)buf[i] - offset; if (prev -HYST_LSB v HYST_LSB) { rising_crossing; } prev v; } /* 采样总时长 len / 采样率每个工频周期有1个上升沿过零 */ float elapsed (float)len / SAMPLE_RATE_HZ; return (uint16_t)(rising_crossing / elapsed); }注意这种测频方法有个适用限制缓冲区越长测频分辨率越高但实时性越差。400个点、40ms的缓冲区只能得到约±0.5Hz的精度用来判断50Hz电网是否正常足够了——电网频率漂移超过0.5Hz本身就是重大异常。如果你要求高精度频率比如做电能表那种±0.01Hz级别的测量就得改用定时器输入捕获在硬件的上升沿记录时间戳原理上仍然是过零检测只是时间基准从软件循环变成了硬件定时器。4. 校准流程与踩坑记录实测数据和教训程序写完了不代表能直接交付校准这一步跳不过去。我这部分把自己实测时遇到的现象和解决办法都列出来有的是硬件问题有的是软件问题照着排查能省很多时间。4.1 单点校准两步让测量值逼近万用表校准分成“零点校准”和“增益校准”两步。零点校准是把互感器输入端短路ADC采到的平均值理论上就是偏置电压对应的数字量。如果你发现短路时平均值偏离2048很多说明1.65V偏置电源精度不够或者运放有失调这时候不要硬调软件先检查硬件分压电阻和运放电路。增益校准是用一个已知电压源比如隔离变压器输出的可调交流电同时用万用表读取真实有效值跟程序算出来的值做对比然后调整g_ratio。假设万用表测到220.0V程序在当前系数下算出218.5V那么修正后的系数就是g_ratio_new g_ratio × 220.0 / 218.5。一般单点校准就够用了因为电压互感器在100V到250V范围内的线性度都不错如果你要求更高可以在110V、180V、220V、250V各采一组拟合一条直线用斜率和截距做两点校正。4.2 我踩过的四个坑你可能也会踩现象根因解决办法ADC值恒定不动或全为0DMA未开启循环模式或定时器触发配置失败检查CubeMX里DMA模式是Circular确认ADC外部触发源是Timer2计算出的电压值明显偏大直接对含偏置的原始值做RMS必须先求平均值并减掉再平方平均RMS值每隔一段时间跳变一次采样缓冲区非整周期起始点不在过零点缓冲区长度设为20ms整数倍建议400点/40ms数值和万用表差3%~5%互感器输出幅度偏差或模块电位器没调准用已知电压重校g_ratio模块版先调电位器第一个坑在CubeMX配置阶段就会遇到现象是ADC中断和DMA回调都不触发。我当时查了很久最后发现是ADC的Continuous Request没有使能导致DMA只搬运一轮就停了。第二个坑是很多人容易忽略的拿了原始采样值直接套RMS公式测出来数值高得离谱像220V测出600V就是因为没减直流偏置。第三个坑藏得比较深需要强调一下。假如你采样率是10kHz缓冲区只有100个点对应10ms也就是半个工频周期。这样算出来的“RMS”在波形有偶次谐波或直流分量时会周期性跳变因为积分区间不是完整周期。解决方法就是让采样窗口覆盖整数个周波这也解释了为什么我建议400点而不是300点——200点是1个周期但400点是2个周期平均效果更好。第四个坑主要是模块方案特有的。ZMPT101B模块上的蓝白电位器可以调输出幅度出厂位置不一定适合3.3V系统。我第一次用这个模块时没调电位器输出饱和ADC读数满量程4096拿万用表一测模块输出发现波形顶部削平了。用螺丝刀微调电位器让220V输入时的输出峰值不超过2.9V波形才恢复正常。4.3 实测数据参考这是我用上面整套方案在一台隔离变压器上测到的数据万用表是Fluke 15B程序跑在STM32F103C8T6上供你评估这个方案的精度水平万用表读数(V)程序计算值(V)误差(%)90.290.80.67120.5121.20.58150.3150.90.40180.1180.50.22220.0220.60.27245.3246.10.33误差在正负0.7%以内对于电压监控类的应用完全够用。这个结果的前提是采样电路参数准确、偏置电源稳定、校准系数调到了合适值。如果你测出来的误差在1%以下就不要再折腾了再往高精度走就要上精密参考源和更高位数的ADC成本和复杂度完全不同。最后再分享一个小经验做了几次这个项目之后我习惯在程序里把voltage_rms加一个一阶低通滤波filtered filtered * 0.9 current * 0.1这样串口输出和显示屏上的数值就不会在个位上有轻微跳动观感好很多。计算量几乎可以忽略但对用户体验的提升非常明显。如果你要做的设备需要显示电压、判断过压欠压这个细节值得直接抄走。本文还有配套的精品资源点击获取
返回列表