三相交流电压表完整程序解析:从MCU采样到RMS算法实战
2026/9/2 9:59:28 网站建设 项目流程

简介:整套三相交流电压表程序设计方案,面向嵌入式开发、电力监控及电子测量学习者,解决三相电压信号采集、计算、显示与报警等完整链路。压缩包内共15个文件,以C语言源码、头文件、Keil工程文件、编译目标文件、HEX烧录文件及DSK仿真磁盘为主,另含列表文件与备份文件,便于阅读、编译和烧录调试,整体仅46KB,轻量易用。已有706人学习浏览。程序覆盖ADC数据采集、同步采样、有效值与线/相电压计算、相位分析、过欠压报警、数据显示与记录等关键模块,适合以51单片机或类似平台为载体的实验室项目开发。源码结构清晰,函数划分明确,可直接移植到现有监控板卡;通过研读可快速掌握三相电压测量的工程实现思路,并二次改造用于配电柜监测或教学演示,对提升电力系统自动化监控能力有直接参考价值。 做电气设备维护和配电柜项目的朋友,应该都跟三相交流电压表打过交道。这种仪表看着不起眼,但没有它,整个配电系统的电压状态就跟睁眼瞎一样。我这次拿到的是一份“电测仪表(三相交流电压表完整程序).rar”,压缩包里是一套可以直接拿去用的三相交流电压表程序,配合常见的单片机开发板就能跑起来。别小看这个题目,三相电压的采集、有效值计算、相序判断、显示刷新,每一块单独拎出来都够新手琢磨一阵子,而这份完整程序恰好把整条链路串通了。

这篇东西适合两类人看:一类是做电力仪表、配电监控相关项目的单片机开发者,想参考一套能实际跑通的程序框架;另一类是刚接触交流采样,被真有效值算法和隔离方案搞得一头雾水的同学。我会把这套程序背后的设计思路、关键代码逻辑、硬件接线要点和实际调试中容易踩的坑挨个拆开讲清楚,保证你拿到压缩包之后不仅能编译通过,还能在硬件上稳定运行。

1. 项目概述与整体设计思路

1.1 三相交流电压表解决什么问题

三相交流电压表是电测仪表里最基础的设备之一,专门用来实时测量三相供电系统的线电压和相电压。现场的三相电通常是A、B、C三根火线加一根零线,相与相之间是线电压(标准380V),相与零线之间是相电压(标准220V),三相互相之间差120度相位角。配电柜里装了它,值班人员才能随时知道哪一相电压偏高、哪一相欠压、是不是缺相了。

这个项目把“完整程序”放在压缩包里,意味着它是个“拿到就能改、改完就能烧”的样板工程。程序里通常包含三相电压采样、真有效值计算、相序判断、按键切换显示模式和OLED/LCD显示驱动这几个核心模块。相比买一个成品电压表,自己做一套的好处是能灵活接入自己的系统,比如把电压数据通过串口发给上位机,或者加上继电器做超限断电控制。

1.2 方案选型:为什么用MCU直接采样而不是专用计量芯片

做三相电压测量,行业里常见两条路线:一条是专用的电能计量芯片(比如HLW8032、BL0940这类),内部集成了ADC和功率计算逻辑,开发者不用管算法细节,直接读寄存器拿数据;另一条就是这套程序采用的方案——MCU内置ADC直接采样,用软件算法计算有效值。

两条路我都走过,说实话专用芯片省事,但有个前提是你的需求跟芯片内置的逻辑完全匹配。可一旦你想做的事情超出了芯片的固定功能,比如要自定义显示界面、要加相序判断、要根据电压波动做保护逻辑,就得另想办法。这套程序走的是MCU方案,用一颗常见的ARM Cortex-M内核单片机(STM32F103或者国产GD32系列都行)自带多通道ADC,配合电压互感器或电阻分压,把380V交流电降到ADC可采集的低压信号,再用软件算RMS值。优势很明显:折腾空间大、代码完全可控、成本低。

1.3 程序整体架构与模块划分

打开这个工程,代码整体是“主循环+定时中断”的经典结构。ADC采样放在定时器触发的中断里,这样可以保证每周波采样的点数固定,不会因为主循环里显示刷新或按键扫描而抖动。主循环负责干活,中断负责按时采集,两者通过标志位通信,这套架构是单片机处理连续数据流的标准姿势,也是整个程序跑得稳的关键。

程序模块大致分成:ADC驱动(配置采样通道和触发方式)、采样数据处理(均值滤波、RMS计算)、参数标定(比例系数、零点校准)、显示驱动(OLED屏或LCD1602)、按键输入(切换显示相序/线电压/相电压)。有的版本还会加过压欠压报警输出。理解了模块划分再去看代码,就不会觉得乱,改起来也知道去哪找。

2. 核心算法与关键代码实现

2.1 交流采样的原理:从380V到ADC可读的小电压

单片机ADC很娇气,参考电压基本都是3.3V或者5V,你要是把380V直接怼进去,板子瞬间冒烟。所以前端必须做降压处理。工程里常用的做法是电压互感器(PT)降压或者高阻值电阻分压。电阻分压简单便宜,但跟电网之间没有电气隔离,安全性差一些;电压互感器把强电和弱电隔离开,抗干扰能力强,做仪表类产品基本都选互感器方案。

互感器一般输出的是毫安级电流信号,比如2mA/2mA规格,初级串一个电阻接380V,次级并一个采样电阻转成电压,最后再经过运放调理电路,把信号偏移到ADC输入范围的正中间。因为交流信号有正半周和负半周,如果不加直流偏置,负半周会被ADC截掉,程序里需要把采集到的ADC值减去这个直流偏置,还原出真实的交流波形。这个“减去零偏”的细节,很多新手第一次做交流采样都会遗漏,导致计算结果完全不对。

2.2 真有效值(RMS)算法:核心采样逻辑

交流电压表的“灵魂”在于真有效值计算。因为电网波形不一定是纯净的正弦波,可能是带畸变的,用峰值除以根号2去估算有效值误差会比较大。标准做法是RMS算法,对信号在整数个周期内进行离散采样,把每个采样点的值平方求和,再取平均再开方,公式是:

Vrms = sqrt( (1/N) * Σ(vi^2) )

其中N是一个周期内的采样总点数,vi是第i个采样点的电压值。从数学原理上说,这个算法对任意波形都成立,所以叫“真有效值”。程序实现时要注意两点:一是N必须覆盖整数个信号周期,否则会引入截断误差;二是v必须先用浮点数或定点数计算出实际电压值,再参与平方和运算。

下面是核心计算代码的简化版本,逻辑跟工程里的实际代码是一致的:

#define SAMPLE_NUM 128 // 每个周期采样点数 #define ADC_REF_VOLT 3.3f // ADC参考电压 #define ADC_RESOLUTION 4096 // 12位ADC float calc_rms(uint16_t *adc_buf, uint16_t zero_offset) { uint64_t sum_sq = 0; for (uint16_t i = 0; i < SAMPLE_NUM; i++) { float voltage = (adc_buf[i] - zero_offset) * ADC_REF_VOLT / ADC_RESOLUTION; sum_sq += (uint64_t)(voltage * voltage * 1000.0f); } float rms = sqrtf((float)sum_sq / SAMPLE_NUM / 1000.0f); return rms; }

这里我故意把平方和用了整数累加再转浮点,避免每次循环都调用浮点乘法导致速度下降。实际工程里如果要追求极致效率,还能用arm_dsp库里的函数,比如arm_rms_f32(),一个函数调用就能算完。但在小型单片机上,直接用数学库的sqrtf就够了,注意算完后获得的RMS值对应的是互感器次级或分压后的电压,还得再乘回变比才是真实的三相电压值。

2.3 采样时序与每周波点数选择

1个周期的采样次数,决定了测量精度和MCU的负载。50Hz工频信号的周期是20ms,如果每周波采128个点,那么每个点之间的时间间隔是20ms/128≈156.25us,换算成采样频率就是6400Hz。程序里用定时器中断来触发ADC采样,设好定时器的重装载值就能精确控制这个采样间隔。

选128点是根据经验来的,既保证了足够高的精度,又不会让中断太频繁影响主循环干活。如果你把点数提高到256,精度提升并不明显,但MCU每秒钟要进256*50=12800次中断,对主循环的拖累就大了。反过来,如果每周波只采32个点,对于有谐波畸变的波形,RMS算法误差会明显增大。

注意:定时器中断跟ADC采样触发最好用硬件联动,也就是定时器更新事件直接触发ADC启动转换,转换完成再进DMA中断,这样采样间隔完全不受CPU执行其它代码的影响,能确保等间隔采样。这也是程序里推荐的做法,纯靠软件延时去凑采样间隔的方案,精度不理想。

2.4 相序判断的实现思路

三相电最怕的故障之一是相序接反,如果电机带了相序保护功能还好,有些老设备没有保护,一反转就把机械结构给憋坏。这套程序里也带了相序判断逻辑,本质是在检测三相电压的过零点顺序。正常正序情况下,A相过零点先出现,经过120度相位角后B相过零,再过120度C相过零;如果顺序反了,说明相序错误,程序就会在屏幕上打出FAULT提示。

实现手段有两条路:硬件过零检测电路(比较器输出方波,单片机捕获上升沿),或者软件过零判断(在采样数组里找跨过偏置值的点)。硬件方式准确、抗干扰好,但多占电路;软件方式省成本,但实时性差一些。工程里两种方式都有人用,你要是拿这套程序改产品,建议优先用硬件捕获,代码逻辑会简单得多。

3. 完整工程结构与实操部署

3.1 压缩包解压与工程文件组织

拿到“完整程序.rar”第一件事当然是解压。这里提醒一下,很多下载站会给压缩包加密或者附带广告程序,解压前最好先用杀毒软件扫一遍,别让文件里夹带的乱七八糟的dll和exe混进你电脑。用WinRAR、7-Zip或者Bandizip都能解压,如果提示密码,一般下载页会注明,密码通常是网址或者固定的分享码,别信那种让你关注公众号才能获取密码的套路,单纯程序分享没必要这样。

解压后的典型目录长这样:项目根目录下有USER/(存放main.c、stm32f1xx_it.c等主程序文件)、HARDWARE/(每块硬件的驱动,比如OLED、ADC、按键)、CORE/(启动文件、内核相关代码)、SYSTEM/(串口、延时、中断分组等系统级封装)、OBJ/(编译生成的hex文件)。看到这个结构基本就能确定是正点原子或类似风格的工程模板,比较规整。

3.2 从源码到能跑:编译环境配置

这个工程默认是Keil MDK环境。打开后缀为.uvprojx的工程文件之前,先确认你的Keil版本别太旧,Keil MDK5打开是老工程会自动提示迁移,一般选“OK”让它迁移就行。如果工程提示找不到芯片型号,需要去Pack Installer里装对应的器件支持包,比如STM32F1系列就装Keil.STM32F1xx_DFP。

编译前有几点要检查:C/C++编译器选项里的宏定义,比如STM32F10X_HD是否跟你手上的芯片容量匹配;Include Paths里有没有漏掉HARDWARE、SYSTEM这些头文件目录。这些地方配不对,一编译就会爆出一堆“头文件找不到”的错。把工程里的路径一个个加上去,基本上就能编译通过了。

3.3 硬件接线与程序参数的对应关系

程序跑起来之前,硬件接线必须跟代码里的引脚定义对上。拿常见的OLED显示方案来说,用I2C接口的话,SDA和SCL要接到代码里oled.h定义的GPIO上;三相电压采样板来的三路信号,分别接入ADC的三个通道引脚。每个引脚对应到哪个ADC通道,在adc.c里会写死,比如PA0对应ADC1_IN0、PA1对应ADC1_IN1、PA2对应ADC1_IN2,接线的时候得照着这个表来。

接线完成后,还要检查互感器变比和分压电阻的数值跟程序里的标定系数是否一致。这个很关键,程序里通常会有一个电压比例系数VOLT_RATIO,比如Vr = Vrms * RATIO。如果你换了一个变比的互感器,或者改了采样电阻,这个系数必须按新的硬件参数重新算一遍。

计算公式很简单:假设互感器初级串了120kΩ限流电阻,输入380V时初级电流I=380/120000≈3.17mA,次级如果在10Ω采样电阻上输出31.7mV。MCU算出的电压是31.7mV,真实电压是380V,那么RATIO = 380 / 0.0317 ≈ 11987。代码里填上这个数,显示的电压值就准了。

3.4 烧录与上电调试的完整流程

程序编译通过后会生成hex文件,位于OBJ目录下。烧录方式看你的调试器,ST-Link用STM32 ST-LINK Utility或者Keil自带的下载功能,J-Link用J-Flash。Keil里配置好调试器型号,点击下载按钮就能把程序写进单片机。注意下载前要把板子的BOOT0引脚设置成从Flash启动,不然程序跑不起来。

第一次上电,别急着接三相电。先把程序单独烧进板子,用万用表量一下OLED屏有没有供电,看屏幕有没有显示初始界面。确认基本功能正常后,用可调变压器从低压开始慢慢升压,比如先从100V开始,对比程序显示值和标准万用表读数,看看偏差有多大。这个过程就是标定,也是整个项目里最花时间的环节。

4. 常见问题排查与实测经验

4.1 显示值一直跳变,不稳定

这个现象很常见,多半是采样端有干扰或者滤波没有做好。先检查互感器次级输出端的滤波电容,一般会接一个0.1uF的瓷片电容,如果没接,高频噪声会被ADC原样采进来。另一个原因是采样没有做软件滤波,比如中值滤波或滑动平均滤波,程序里实现也不难,连续采5次去掉最大最小值再取平均,数据会稳很多。

还有一点特别容易忽略:DMA采集三相数据时,如果三路通道的采样顺序跟代码里期望的不一致,或者DMA传输半完成中断和完成中断的处理写反了,数据就会错位,显示值自然乱跳。建议先把三路ADC的原始值直接打印到串口,用串口助手观察波形,看是不是周期性的正弦样子,是的话再谈后续算法,不是的话先修硬件和DMA配置。

4.2 显示电压比实际电压偏低,且随电压升高偏差越大

这种“比例性偏差”基本可以判定是分压系数问题。最常见的原因是你以为用的是120kΩ电阻,实际焊接的时候用错了或者电阻精度不高。另一个原因是ADC参考电压不是精确的3.3V,很多板子上的3.3V实际可能只有3.25V,这1.5%的误差在分压比放大的情况下会被放大。解决办法是拿高精度万用表实测板子的参考电压,然后把程序里的ADC_REF_VOLT改成实际值。

如果偏低的情况只出现在某一相,那大概率是那一相的前端运放偏置电压和其他两路不一致。这就要检查运放电路里的偏置电阻,保证三路零偏值一致。程序标定的时候,可以把每路的零点单独记录,不要用一个统一值。

4.3 程序编译报错,开发环境各种“命令找不到”

很多第一次打开工程的同学,会在电脑上遇到一些环境问题。比如在命令行里敲个命令提示“不是内部或外部命令”,多半是环境变量Path没配好。Keil本身倒不需要命令行,但如果你后续要配合脚本自动化编译,就得把C:\Keil_v5\ARM\ARMCC\bin加到系统Path里面去。另外,如果下载的时候提示找不到ST-Link设备或者连接失败,多半是驱动没装好,去ST官网装个ST-Link驱动就好。

这类问题看似跟项目无关,但实际上卡住不少人。我的建议是先把环境理干净,再折腾代码,否则环境问题跟代码问题混在一起,排查起来非常痛苦。

4.4 实际项目中很容易忽视的几个隐患

最后聊几点不写在教程里但实际项目会吃亏的细节。

一是接线端子一定要做隔离和绝缘处理。三相电压表要直接接380V强电,强电端和弱电端之间的距离要保持好,PCB上要开隔离槽,防止爬电。测试的时候就算是用小变压器供电,也千万别用普通的杜邦线去戳强电,安全第一。

二是校准的时候一定要多取几个测试点。我习惯分别用100V、220V、380V三档校准,最少测三个点,再画一条校准曲线,看是不是线性的。如果只有单点校准,在电压范围两头误差会比较大。

三是程序里的保护逻辑建议提前加。完整程序可能只做了测量显示,没做超限报警。自己加也很简单:在RMS计算结果出来之后,跟预设的上限下限比较,超过就点亮报警LED或控制继电器,实测下来在电压不稳的场合很实用。

5. 结束语:一套能打底子的完整工程

这套三相交流电压表程序,我从解压到编译、烧录、标定走了一遍,整体工程质量很扎实。它最大的价值不是那些漂亮的OLED界面,而是把交流采样从硬件调理到软件算法这条完整的链路打通了。新手照着工程抄一遍,理解ADC中断采样、RMS计算、互感器变比换算这些关键思路,比单纯看教材上的公式管用得多。

最后再分享一个小技巧:如果你打算在这个基础上做自己的产品,先把标定系数的存储位置放到EEPROM或者Flash末尾,做成可以通过串口指令修改。这样出厂校准的时候就不用反复重新编译烧录程序,生产线上能省下大量时间。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询