摘要: 本简易电能质量检测装置由单片机控制模块,电源模块,信号变换与处理模块等构成。c8051F020为主控单片机,它能准确的完成同时对一路工频交流电的频率、电压有效值、电流有效值、有功功率、无功功率、功率因素等进行测量。通过软件对输入电压信号进行实时采样。系统调试时,用函数发生器输出正弦电压信号作为交流信号的电压信号输入,此电压信号经自制的移向电路相后代表同一路信号的电路信号输入。
关键词:电能质量 单片机 工频交流电
一、系统设计方案及原理图
1.1 设计要求
1、测量交流输入电压有效值
频率:50Hz;测量范围:100~500V;准确度:±0.5%。
2、测量交流输入电流有效值
频率:50Hz;测量范围:10~50A;准确度:±0.5%。
3、测量有功功率P(单位为W)、无功功率Q(单位为var)、视在功率S(单位为VA)及功率因数PF(功率因数为有功功率与视在功率之比)。有功功率、无功功率、视在功率准确度:±2%;功率因数显示格式:0.00~0.99。
4、在交流电压、交流电流、有功功率、无功功率、视在功率的测试过程中,能够记录它们的最大值和最小值。
1.2设计思想
通过分析题目,本检测装置主要有主控制器模块、显示模块、按键模块和信号变换与处理模块等组成。
方案一、分别测量电压信号和电流信号,但是,电流信号不易测量,需用到大量元器件,硬件电路会变得很复杂,成本也高。
方案二、直接测量电压信号,通过模拟测量电流信号,即对电压信号移相测量电流信号,
图表 1
显示模块中通过按键1、2、3、4分别在lcd1602显示频率,相位差、电压有效值,最大电压,最小电压、电流有效值,最大电流,最小电流、 有功功率,无功功率,视在功率,功率因素。及在lcd128显示电压和电流各自的信号。
二、系统硬件设计
2.1信号波一周期采样点数的确定
在一个周期内,每次都从同一起点开始通过定时来采样个点。将每采集的点,通过AD转换。
2.2 电路设计图
2.2.1 移相电路
图1
通过硬件模拟来达到电能质量监测的。考虑到电压信号所相对的电流信号与电压信号之间存在90度的相位差,电流信号滞后于电压信号。所以通过移相电路即对输入电压信号移相来模拟电流信号。
2.2.2 整形电路
图2
整形模块利用比较器LM393,将正弦波转换成方波,再由IN4148整流二极管来方波的幅值,将输出的方波幅值限定在 -1V~+4.3V内。
2.2.3 采样电路
图3
采样保持模块我们采用LF398采样/保持器来设计采样电路。
LF398是一种高性能单片采样/保持器。它通过1k的电位器来实现调零的作用,8引角的采样控制信号可以由单片机来设定实现,也可利用函数信号发生器的产生脉冲信号来实现控制LF398的采样的点数。
2.2.4 总电路图
2.3电路分析
对电压进行移相得到模拟电流,再通过采样保持电路实现对同一时刻电压和电流两路信号的分别保持,将采样得到的电压电流进行模数转换后,以得到实际电压电流。再对电路进行整形,通过捕获上升沿来测量相位及频率。测量相位时,通过D触发器来判断相位超前还是滞后,
如果输出为0则相位滞后,如果输出为1则相位超前。
3 软件设计
3.1 主程序流程图
3.2各子程序流程图
(1)电压、电流测量
(2)频率测量
(3)功率测量
(4)定时器0中断
(5)PCA0中断
4 系统测试
4.1测试仪器及测量方法
测试仪表:函数信号发生器,数字万用表,示波器
测试方法:用函数信号发生器产生正弦信号波作为电压信号波输入,此电压信号经移相电路移相后作为同一路的电流信号输入,经采样送单片机处理后显示,将显示的各数值同示波器观察值作比较,计算出各误差值。
4.2测试结果及分析
电压有效值:U=
电流有效值:I=
有功功率:P=
视在功率:S=U*I
无功功率:Q=S-P
功率因素:
输入信号峰峰值 | 3V | 2V | 1V |
最大电压 | 1.49 | 1 | 0.52 |
最小电压 | -1.49 | -1 | -0.52 |
相对误差 | 0.006667 | 0 | -0.04 |
理论电压有效值 | 1.0608 | 0.7072 | 0.3536 |
电压有效值 | 1.0526 | 0.7102 | 0.3746 |
相对误差 | 0.00773 | -0.00424 | -0.05939 |
最大电流 | 1.5 | 1.01 | 0.54 |
最小电流 | -1.5 | -1.01 | -0.54 |
相对误差 | 0 | -0.01 | -0.08 |
理论电流有效值 | 1.0608 | 0.7072 | 0.3536 |
电流有效值 | 1.053 | 0.7132 | 0.3787 |
相对误差 | 0.007353 | -0.00848 | -0.07098 |
理论有功功率 | 0.974507 | 0.433114 | 0.108279 |
有功功率 | 0.84 | 0.38 | 0.1 |
相对误差 | 0.138026 | 0.122633 | 0.0756 |
理论视在功率 | 1.125297 | 0.500132 | 0.125033 |
视在功率 | 1.05 | 0.47 | 0.12 |
相对误差 | 0.071711 | 0.011 | 0.041941 |
理率因素 | 0.866 | 0.866 | 0.866 |
功率因素 | 0.8 | 0.81 | 0.8 |
相对误差 | 0.076212 | 0.0665 | 0.076212 |
通过此次实验对于软件和硬件方面的调试能力有所提高。
在软件编程方面,对于单片机中的一些芯片的接口的定义和调用接口的数据。根据单片机编程需要对硬件的一些指标有所了解。
在按电路图焊接完,进行硬件调试是发现LM393的2脚和6脚的输入信号中有一些毛刺信号的干扰,为了消除这些毛刺信号的干扰,于是在这两个引脚上加了两个对地小电容。
测相位的引脚也存在同样的问题,就用了同样的方法来解决。
在软件调试过程中发现,用定时器2、3来启动ADC0,虽然理论上是可以的,但是,由于ADC0启动和完成转换,需要一定时间,而我们是在启动ADC0转换的下一个时刻就直接读取转换结果,所以,中间出现了一些差错。于是,改用了置一AD0BUSY的方式来启动,并且使用while语句等到ADC0转换完成时才读取转换结果。
这些天实验中发现目前所做的效率显然高于之前的每一个实验,原因很多,一是因为没有其他旁事,这样会更专注。二是学习氛围,学习氛围很浓厚,每天都是12小时都在编程。更多的是得到了太多的帮助,学长一次次解决了我们看似无解的麻烦,并教会我们一些无从得知的基础知识,让我们在单片机这条路上走得更长。这些天学会了很多,但是最重要的还是学会自主学习,问题学会自己解决,这样才能永无止尽地学下去。
参考文献
1.黄志伟主编,全国大学生电子设计竞赛训练教程 北京:电子工业出版社,2004年
2.赵佩华,眭碧霞主编,单片机原理及接口技术 北京:机械工业出版社,2008年
3.张友德主编,单片机微型机原理应用于实验 上海:复旦大学出版社,2000年
附录
程序附录1:
#include "c8051F020.h"
#include "sysinit.h"
#include "lcd1602.h"
#include "keyscan.h"
#include "adc.h"
#include "lcd128.h"
#include "math.h"
#include extern unsigned char xdata lcd_buff0[16];//1602显示缓冲区 extern unsigned char xdata lcd_buff1[16]; unsigned char xdata tab10[]; unsigned char xdata tab20[]; unsigned char xdata tab30[]; unsigned char xdata tab40[]; unsigned char code zuobiao[]; unsigned int zhouqi,jiange; unsigned char j=0,ccf0_overflow,key,key_value; float freqvalue,xiangweicha,U,Umax,Umin,U0,I,Imax,Imin,I0,power,S,pf,t; float powerl=0; float xdata adc0_buff0[]; float xdata adc0_buff1[]; sbit control=P0^3; sbit flag=P0^5; void pl(void); void dy(void); void dl(void); void gl(void); void caiji(unsigned char adc0_buff0[],unsigned char adc0_buff1[]); void tuxing(float adc0_buff0[],float adc0_buff1[]); void PrintString(unsigned char *str); void pho_disp(unsigned char* tab); void main(void) { WDTCN=0xDE; WDTCN=0xAD; SysClkInit(); //配置系统时钟,使用外部晶振,系统上电默认使用内部2M时钟 PortInit(); //I/O端口配置 LCD1602_Init(); //lcd1602初始化 LCD128_Init(); ADC0_Init (); PCA0CPM0=0x21; PCA0CPM1=0x21; PCA0MD=0x01; EA=1; ET0=1; EIE1=0x08; PCA0CN = 0x40; //启动PCA0计数器工作 TMOD=0x01; TH0=0xFD; TL0=0x8F; AMX0CF = 0x00; while(1) { key_value=Get_Key(); if(key_value!=0xff) key=key_value; while(key==1) { pl(); key_value=Get_Key(); if(key_value!=0xff) key=key_value; } while(key==2) { dy(); key_value=Get_Key(); if(key_value!=0xff) key=key_value; } while(key==3) { dl(); key_value=Get_Key(); if(key_value!=0xff) key=key_value; } while(key==4) { gl(); key_value=Get_Key(); if(key_value!=0xff) key=key_value; } } } void pl(void) { ccf0_overflow = 0; //清零捕获次数计数器,开始测量频率信号 while(ccf0_overflow<2); //等待第二次捕获 freqvalue = 2000000.0/zhouqi;//计算频率值 xiangweicha=jiange*1.0/(zhouqi*1.0)*360-0.14; if(xiangweicha>180) xiangweicha=360-xiangweicha; if(flag==0) sprintf(lcd_buff0,"pd:+%-6.3f ",xiangweicha); if(flag==1) sprintf(lcd_buff0,"pd:-%-6.3f ",xiangweicha); LCD_SET_CURSOR(1,1); PrintString(lcd_buff0); sprintf(lcd_buff1,"freq:%-8.4f ",freqvalue); LCD_SET_CURSOR(2,1); PrintString(lcd_buff1); Delay_Ms(100); } void dy(void) { ccf0_overflow = 0; //清零捕获次数计数器,开始测量频率信号 while(ccf0_overflow<2); //等待第二次捕获 caiji(adc0_buff0,adc0_buff1); U=0; Umax=adc0_buff0[0]; Umin=adc0_buff0[0]; for(j=0;j<;j++) { if(adc0_buff0[j]>Umax) Umax=adc0_buff0[j]; if(adc0_buff0[j] } for(j=0;j<;j++) { U=U+pow(adc0_buff0[j]-(Umax+Umin)/2,2); } U=sqrt(U/); U = U/4095.0*3.0+0.025; //将电压数字量转换为实际电压值 U0=Umax; Umax = (Umax-(Umax+Umin)/2)/4095.0*3.0+0.025; Umin = (Umin-(U0+Umin)/2)/4095.0*3.0-0.025; sprintf(lcd_buff0,"l:%-6.2fs:%-6.2f",Umax,Umin); LCD_SET_CURSOR(1,1); PrintString(lcd_buff0); sprintf(lcd_buff1,"Urms:%-6.4f ",U); LCD_SET_CURSOR(2,1); PrintString(lcd_buff1); tuxing(adc0_buff0,adc0_buff1); Delay_Ms(100); } void dl(void) //电流测量 { ccf0_overflow = 0; //清零捕获次数计数器,开始测量频率信号 while(ccf0_overflow<2); caiji(adc0_buff0,adc0_buff1); I=0; Imax=adc0_buff1[0]; Imin=adc0_buff1[0]; for(j=0;j<;j++) { if(adc0_buff1[j]>Imax) Imax=adc0_buff1[j]; if(adc0_buff1[j] } for(j=0;j<;j++) { I=I+pow(adc0_buff1[j]-(Imax+Imin)/2,2); } I=sqrt(I/); I = I/4095.0*3.0+0.035; //将电压数字量转换为实际电压值 I0=Imax; Imax = (Imax-(Imax+Imin)/2)/4095.0*3.0+0.05; Imin = (Imin-(I0+Imin)/2)/4095.0*3.0-0.05; sprintf(lcd_buff0,"l:%-6.2fs:%-6.2f",Imax,Imin); LCD_SET_CURSOR(1,1); PrintString(lcd_buff0); sprintf(lcd_buff1,"Irms:%-6.4f ",I); LCD_SET_CURSOR(2,1); PrintString(lcd_buff1); tuxing(adc0_buff0,adc0_buff1); Delay_Ms(100); } void gl(void) { ccf0_overflow = 0; while(ccf0_overflow<2); caiji(adc0_buff0,adc0_buff1); Umax=adc0_buff0[0]; Umin=adc0_buff0[0]; Imax=adc0_buff1[0]; Imin=adc0_buff1[0]; for(j=0;j<;j++) { if(adc0_buff0[j]>Umax) Umax=adc0_buff0[j]; if(adc0_buff0[j] if(adc0_buff1[j]>Imax) Imax=adc0_buff1[j]; if(adc0_buff1[j] } power=0; for(j=0;j<;j++) { U=(adc0_buff0[j]-(Umax+Umin)/2)/4095.0*3.0; I=(adc0_buff1[j]-(Imax+Imin)/2)/4095.0*3.0; power=U*I+power; } U=0; I=0; for(j=0;j<;j++) { U=U+pow(adc0_buff0[j]-(Umax+Umin)/2,2); I=I+pow(adc0_buff1[j]-(Imax+Imin)/2,2); } U=sqrt(U/); U = U/4095.0*3.0; I=sqrt(I/); I = I/4095.0*3.0; power=power/.0; if(abs(t-Umax)>136) powerl=0; if(power>powerl) powerl=power; t=Umax; sprintf(lcd_buff0,"P:%-5.2fPL:%-5.2f ",power,powerl); LCD_SET_CURSOR(1,1); PrintString(lcd_buff0); S=U*I; pf=power/S; sprintf(lcd_buff1,"S:%-5.2fpf:%-3.2f ",S,pf); LCD_SET_CURSOR(2,1); PrintString(lcd_buff1); Delay_Ms(100); } void PCA0_0int(void) interrupt 9 { if(CF == 1) { CF = 0; //计数器溢出标志清零,必须用软件清零,硬件不能自动清零 } if(CCF0 == 1) //捕获中断处理 { CCF0 = 0; if(ccf0_overflow == 0) //第一次捕获发生时候清零计数器 { PCA0L = 0; PCA0H = 0; } if(ccf0_overflow == 1) //第二次捕获发生后,就可以计算周期 zhouqi = PCA0CPH0*256 + PCA0CPL0; ccf0_overflow ++; //捕获次数加调整 if(ccf0_overflow>=2) ccf0_overflow = 2; } if(CCF1 == 1) //捕获中断处理 { CCF1 = 0; if(ccf0_overflow ==1) jiange = PCA0CPH1*256 + PCA0CPL1; } } void caiji(float adc0_buff0[],float adc0_buff1[]) { j=0; TR0=1; while(j<); TR0=0; } void tuxing(float adc0_buff0[],float adc0_buff1[]) { clrgdram(); //将GDRAM中的数据全部置为0 set_coord(0,0); lcd128_show_str(tab10,16); set_coord(0,1); lcd128_show_str(tab20,16); set_coord(0,2); lcd128_show_str(tab30,16); set_coord(0,3); lcd128_show_str(tab40,16); pho_disp(zuobiao); //显示坐标 for(j=0;j<;j++) //打点显示电压和电流的波形 { U=adc0_buff0[j]/4095.0*3.0; I=adc0_buff1[j]/4095.0*3.0; draw_point(31-U*10,j+47,1); draw_point(63-I*10,j+47,1); } } void timer0_isr(void) interrupt 1 { TF0=0;//清除中断标志位 TH0=0xFD; TL0=0x8F; control=0; AMX0SL = 0x00; AD0INT = 0; // 清除ADC转换完成标志 AD0BUSY = 1; //启动ADC0 while (!AD0INT); adc0_buff0[j] = ADC0H; adc0_buff0[j] = adc0_buff0[j]*256 + ADC0L; //读取ADC0的值 AMX0SL = 0x01; AD0INT = 0; // 清除ADC转换完成标志 AD0BUSY = 1; //启动ADC0 while (!AD0INT); adc0_buff1[j] = ADC0H; adc0_buff1[j] = adc0_buff1[j]*256 + ADC0L; //读取ADC0的值 j++; control=1; }