基于单片机正弦波有效值的测量
基于单片机正弦波有效值的测量
一.简介
本作品以单片机STC12C5A60S2为主控芯片并以此为基础,通过二极管1N5819实现半波整流,使用单片机内部自带10位AD对整流后的输入信号进行采样,从而实现对峰值的检测;同时通过运放LM837对输入信号进行放大,之后通过施密特触发器,将原始信号整形成可被单片机识别的标准脉冲波形,之后配合内部计数器(定时器)达到测量其频率的目的;这样,整流和AD采样实现对输入信号峰值的检测;通过放大、整形实现对输入信号频率的检测。 二.基本功能与技术指标要求
(1)输入交流电压:1mV~50V,分五档:
① 1mV~20mV,② 20mV~200mV,③ 200mV~2V,④ 2V~20V,⑤ 20v~50V。
(2)正弦频率;1Hz~100kHz;
(3)检测误差:≤2%;
(4)具有检测启动按钮和停止按钮,按下启动按钮开始检测,按下停止按钮停止检测;
(5)显示方式:数字显示当前检测的有效是,在停止检测状态下,显示最后一次检测到的有效值;
(6)显示:LCD,显示分辨率:每档满量程的0.1%;
三.理论分析
本文要求输入交流信号,通过电路测量其峰值,频率,有效值以
及平均值,因为输入的交流信号为模拟信号,而一般处理数据使用的主控芯片单片机处理的是数字信号,所以我们选择使用数模转换器AD(Analog to Digital Converter)将输入的模拟信号转换为数字信号,并进行采样;由于要求输入交流信号电压峰峰值Vpp为50mV~10V,所以如果我们采用AD为8
位,则最小采样精度为
,因此会产生78.4%的误差,并且题目要求输入交流
信号的频率范围为40Hz~50kHz,所以为了保证对高频率信号的单周期内采样个数,我们需要选择尽量高速度的AD;
因此我们选用使用单片机STC12C5A60S2,其内部自带AD为8路10位最高速度可达到250KHz
,所以我们可以将最小采样精度缩小到
,并且在输入交流信号频率最大时(50KHz)在单个
周期内可采集5个点,因此可保证测量精度。
由于该AD只能接受0~5V的模拟信号输入,所以当我们直接输入一个双极性信号时可能损坏AD,因此当信号进入AD之前我们要进行半波整流,为此我们设计了整流电路,在交流信号通过整流电路输入AD后,由AD实时输出对应模拟信号大小的二进制数,并存入变量MAX中,随着信号的不断输入MAX中只保存AD输出过的最大值,这样既可测出输入信号的峰值;由交流信号有效值表达式
可知检波器应当首先把输入的瞬时电压平方, 然后在一定平均时间内取平均值再开方。即可得到交流信号的有效值,然后通过比较峰值
和有效值的关系即可知道该交流信号的波形;由于要检测交流信号的频率,所以我们使用单片机内部定时器和计数器,而计数器只能由上升沿或是下降沿触发,所以我们想通过施密特触发器将输入交流信号变为高低电平,以便检测其频率,而施密特触发器CD40106在常温5V供电下门限电压=1.4V,而输入交流信号的峰值最小时为25mV,所以不能保证输入信号为正的时候施密特触发器就能发生跳变,因此我们要对输入交流信号进行放大,为此我们设计了放大电路。
四.电路设计
1.电源电路的设计
本次实验我们采用+5V,—5V,+12V,—12V的电源,故采用了
7805,7905,7812,7912这四块芯片组成电源电路对作品进行供电。如
图一所示:
图一
经过测量实际输出电压为:+5.12V -5.06V +12.67V -12.02V
2.分档设计
由于直接对信号测量时,对小信号的测量产生很大的误差,为了更有效的利用单片机的资源,故要对待测信号进行分档处理,但在实际运行中,由于进入放大器之前存在大量电阻产生了很多的噪声干扰,使输出波形严重失真,不能有效的利用,故我们舍弃了该部分的电路。
3.整流电路的设计
我们使用整流二极管5819实现半波整流,但通过此电路输出波形会出现一个负电压,因此在此基础上,我们在二极管后加了一个上拉电阻,实现最大程度的消除输入波形的负电压,使其在单片机STC12C5A60S2内部AD的可接收信号范围内(0-5V)。输入信号经过整流后进入AD,由AD输出10位数字信号,再有单片机进行处理,得出信号的有效值。电路如图二所示:
图二
在实际的测试中,证明该方案可行,能够实现对信号的半波整流处理。
4.正弦波转方波电路设计
在该电路的设计中,我们一开始采用555芯片把正弦波转换为方波,但在实际的测试中发现555只能对高于6V的信号才能使正弦波转换为放波,与我们实习要求不符合,故舍弃了该方案。在老师的指导下,我们采用了滞回比较器来产生方波。本方案检测输入信号频率的核心电路,信号通过由三个运放LM324组成的运放电路后放大100倍,然后输入施密特触发器使其波形变为高低电平,将此波形输入到单片机主控芯片,利用单片机内部的计数器和定时器完成对频率的测量。大部分采用运放LM324构成放大100倍电路,位于图下方由5V电源供电的运放可以产生一个2.5V的直流电压,将此2.5V的直流电压输入到第一级运放后,会使输入的交流信号上偏2.5V,变为0-5V,再经过100000pF的电容C3滤掉被放大的噪声以及2.5V直流后,变为-2.5—+2.5的交流信号,之后进入二级放大;
如图所示第二级运放在加入+2.5V直流电压后输出为0—5V的交流信号,至此外加交流信号已被放大100倍;在外加交流信号被放大100倍之后进入施密特触发器CD40106,该触发器在常温5V供电下门限电压 =1.4V ,输出高电平 ,低电平 , 此部分电路输出即为这个高低电平。电路如图三所示:
图三
在实际的检测中发现放大器并不能将信号放大100倍,故幅值较小的信号不能正常显示频率,只要当信号的峰峰值大于1.5V时才能使单片机正常显示信号的频率。
5.单片机电路
采用STC12C5A60S2单片机,作为整个系统的控制器,控制各个模块协调工作。运用其内部集成的一个AD
作为数模转换器。如图四所示
图四
6.
系统整体电路图如图五所示
图五
四.软件设计
AD流程图如图六 图七所示
图六
图七
1.正弦波有效值的测量方法
工频正弦电压信号
u(t)=Umsin(2πft+ϕ)=2Usin(2πft+ϕ)
t
频率f和初相位角ψ已知,且ψ=0,现以t=0时可作为基准时间,并在t=0时刻开始进行等间隔采样,共得到正弦波u(t)的N个测量值(采样数据)u(0),u(1),……,u(N-1),希望利用这N个测量值来计算正弦波的幅值Um(有效值U)。
设采样周期为TS,如果不存在噪音,且测量无偏差,则有
u(0)=Umsin(0)=0
u(0)=Umsin(2π⋅f⋅TS)
……
u(k)=Umsin(2π⋅f⋅K⋅TS)
……
u(N-1)=Umsin(2π⋅f⋅(N-1)⋅TS)
我们可以设法利用测量值求解出正弦波参数。
实际上,我们的测量存在误差,同时信号存在噪音,因此,对每一个测量信号u(k),它与对应时刻t=k·TS的正弦波u(t)=Umsin(2πft)的值u(t)=Umsin(2π⋅f⋅k⋅Ts)存在一个误差e(k),其中k=0,1,…,N-1。定义
e(k)=Umsin(2π⋅f⋅k⋅TS)-u(k)
为了利用N个测量值来尽可能准确的计算正弦波幅值Um,我们定义指标函数为:
N-11N-12ε(Um)=∑e(k)=∑[Um⋅sin(2π⋅f⋅k⋅Ts)-u(k)]2 2k=0k=0
它是频率f,幅值Um,初相位角ψ的函数。我们的目的就是求频率f,幅值Um,初相位角ψ使得目标函数ε最小。为此,令
∂ε(Um)N-1
=∑{[Um⋅sin(2π⋅f⋅k⋅TS)-u(k)]⋅sin(2π⋅f⋅k⋅Ts)}∂Umk=0
=Um⋅∑sin(2π⋅f⋅k⋅Ts)-∑u(k)⋅sin(2π⋅f⋅k⋅Ts)=02
k=0k=0N-1N-1
-------------------------------------------------(1)可以得到正弦波的幅值
Um=∑u(k)⋅sin(2π⋅f⋅k⋅T)s
k=0
N-1
k=0N-1 ∑sin2(2π⋅f⋅k⋅Ts)
为了与正弦波的真正幅值Um表示上的区别,用UmC表示利用N个采样数据获得的正弦波幅值计算值,既
UmC=∑u(k)⋅sin(2π⋅f⋅k⋅T)s
k=0
N-1
k=0N-1(2) ∑sin2(2π⋅f⋅k⋅Ts)
对于f≈50Hz(以前面的测量值为准,这里仅为举例),选择采样周期TS ≈20/200=0.1ms=100us,既每个周期采样200点(每半个周波100次采样),
sin(2π⋅f⋅k⋅TS)
=sin(10π⋅k)k=0,1,...,199
是确定的(可以预先求出,存于sin表中),sin2(2π·f·k·TS)同样作为常数表,而且
∑sin
k=0N-12(2π⋅f⋅k⋅Ts)=C
本身是一个常数,因此公式(2)变为
UmC=∑u(k)⋅sin(2π⋅f⋅k⋅T)s
k=0N-1
C(3)
当采样周期很小时,数值积分系数C可以用
1C=⋅⎰sin2(ωt)d(ωt)TS02π(4)
来替代(可以认为C是一个周期类的数值积分,当采样周期很小时,就用积分项来表示)。
就可利用N个等间隔测量值计算出正弦信号的峰值Um。
正弦波的有效值与峰值的关系
UC UmC
2(5)
半波绝对值电路:
在待测交流信号的正半波,输出待测信号送AD变换电路,在待测信号的负半波,输出0.
正半波时,Px.y=0,单片机利用该信号识别是正半波信号,进行AD转换,获得N个采样值。在Px.y=1时,单片机暂停AD变换,利用正半波获得的N个检测信号计算正弦波的有效值U,频率f。
2.数据测量
幅值的测量
表1
频率的测量
表2
3.数据分析
由上图可知在输入信号vpp在0.7v到5v的区域内,实验作品的误差较小能够实现有效值的测量,当频率在100Hz到40KHz时,在误差允许范围内,作品基本能完成对该信号的准确测量。
4.软件部分代码:
#include
#include
#define FOSC 12000000L
#define BAUD 9600
typedef unsigned char BYTE;
typedef unsigned int WORD;
sbit LCM_E=P2^5;//定义接口
sbit LCM_RW=P2^6;
sbit LCM_RS=P2^7;
sfr ADC_CONTR = 0xBC;
sfr ADC_RES = 0xBD;
sfr ADC_LOW2 = 0xBE;
sfr P1ASF = 0x9D;
sfr AUXR = 0x8e;
#define ADC_POWER 0x80
#define ADC_FLAG 0x10
#define ADC_START 0x08
#define ADC_SPEEDLL 0x00
#define ADC_SPEEDL 0x20
#define ADC_SPEEDH 0x40
#define ADC_SPEEDHH 0x60
#define LCM_Data P0//数据接口
void InitUart();
void SendData(BYTE dat);
void Delay(unsigned int n);
void IO_ADinit() ;
void ADC_Power_On() ;
void get_ad_result() ;
void ADCONVERT() ;
void WriteDataLCM(BYTE WDLCM);
void WriteCommandLCM(BYTE WCLCM,BuysC);
BYTE ReadDataLCM(void);
BYTE ReadStatusLCM(void);
unsigned int GetADCResult(BYTE ch);
void LCMInit(void);
void DisplayOneChar(BYTE X, BYTE Y, BYTE DData);
void DisplayListChar(BYTE X, BYTE Y, BYTE code *DData); void Delay5Ms(void);
void Delay400Ms(void);
void Disp_number(unsigned int num,BYTE n);
void ShowResult(BYTE ch);
unsigned int GetADCResult(BYTE a);
unsigned int data_max = 0;
unsigned long data_max1,count =0;
unsigned int data_change = 150;//0.7V
unsigned char code VP[4]={"Amp:"},Freq[5]={"Freq:"};
unsigned int Counter = 0;
unsigned int data_last = 0;BYTE ch = 0;
unsigned int Frequency = 0;
unsigned int nn =0,flag = 0,flag1 = 0,nn1=0;
unsigned int PrintFre[5] ;
unsigned int Print[4];
unsigned int number[4];
unsigned int D;
void main()
{
unsigned int n=0;
Delay400Ms();//启动等待,等LCM讲入工作状态
LCMInit();//LCM初始化
Delay5Ms();//延时片刻(可不要)
ET1 = 1;
EA=1;
InitUart();
TMOD = 0x15;//均为16位计数器
TH0 = 0x00;
TL0 = 0x00;
TH1 = 0x3C;
TL1 = 0xAF;
TCON = 0x50;//T1,T0均工作,t1做定时器,t0做计数器;
while(1)
{
ADCONVERT();
n++;
if(n>10000){data_max=0;n=0;}
}
}
void To_interrupt(void)interrupt 3 using 1
{
int i ,j;
unsigned int Change1,Print[4];
unsigned long int Change2;
TH1 = 0x3C;
TL1 = 0xAF;
Counter = Counter++;
if(Counter == 20)
{
Frequency = (TH0=2000)Change1 = (Frequency/100)*92;
else if (Frequency>=200)Change1 = ((Frequency/10)*92)/10; else Change1 = (Frequency*97)/100;
for( i=0;i
{
PrintFre[i] = Change1 % 10;
Change1 = Change1 / 10;
}
for(i=4;i>=0;i--)
{
if(i>0)DisplayOneChar(4-i,0,PrintFre[i]+48); else{DisplayOneChar(4-i,0,PrintFre[i]+48); DisplayListChar(5-i,0,"Hz");
}
}
Change2 = data_max*50;
Change2 = Change2/10/1.414;
for(j=0;j
{
Print[j] = Change2 % 10;
Change2 = Change2 / 10;
}
for(j=3;j>=0;j--)
{
if(j>0) DisplayOneChar(10-j,0,Print[j]+48); else { DisplayOneChar(10-j,0,Print[j]+48); DisplayListChar(11-j,0,"mV");
}
}
TH1 = 0x3C;
TL1 = 0xAF;
TH0 = 0xFF;
TL0 = 0xFC;
Counter = 0;
}
}
void IO_ADinit()
{
ADC_CONTR=0xe0; //设置P1.0为输入AD转换口
_nop_(); //ADC_CONTR需要四个指令延时
_nop_();
_nop_();
_nop_();
}
void ADC_Power_On()
{
ADC_CONTR|=0x80;
_nop_();
_nop_();
_nop_();
_nop_();
}
void get_ad_result()
{
unsigned int q=0,ad_average_result;
ADC_RES=0;
ADC_LOW2=0;
ADC_CONTR|=0x08;
while(!(ADC_FLAG&ADC_CONTR)) ;
ADC_CONTR&=0xE7;
ad_average_result=(ADC_RES
if(data_max
void ADCONVERT()
{
ADC_Power_On();
IO_ADinit();
get_ad_result();
}
void InitUART()
{
SCON = 0x5a;
PCON|= 0x80;
}
void SendData(BYTE dat)
{
while(!TI);
TI = 0; SBUF = dat;
}
void Delay(unsigned int i)
{
unsigned char j;
for(i; i > 0; i--)
for(j = 200; j > 0; j--) ;
}
void WriteDataLCM(BYTE WDLCM)
{
ReadStatusLCM(); //检测忙
LCM_Data = WDLCM;
LCM_E = 0;
LCM_RS = 1;
LCM_RW = 0;
LCM_E = 0; //若晶振速度太高可以在这后加小的延时
LCM_E = 1;
LCM_E = 0;
}
void WriteCommandLCM(BYTE WCLCM,BuysC) //BuysC为0时忽略忙检测 {
if (BuysC) ReadStatusLCM(); //根据需要检测忙
LCM_Data = WCLCM;
LCM_E = 0;
LCM_RS = 0;
LCM_RW = 0;
LCM_E = 1;
LCM_E = 0;
}
BYTE ReadDataLCM(void)
{
LCM_E = 0;
LCM_RS = 1;
LCM_RW = 1;
LCM_E = 1;
LCM_E = 0;
return(LCM_Data);
}
BYTE ReadStatusLCM(void)
{
LCM_Data = 0xFF;
LCM_E = 0;
LCM_RS = 0;
LCM_RW = 1;
LCM_E = 1;
LCM_E = 0;
while (LCM_Data & 0x80) //检测忙信号
{ LCM_E=0;
LCM_E=1;
}
return(LCM_Data);
}
void LCMInit(void) //LCM初始化
{
LCM_Data = 0;
WriteCommandLCM(0x38,0); //三次显示模式设置,不检测忙信号 Delay5Ms();
WriteCommandLCM(0x38,0);
Delay5Ms();
WriteCommandLCM(0x38,0);
Delay5Ms();
WriteCommandLCM(0x38,1); //显示模式设置,开始要求每次检测忙信号 WriteCommandLCM(0x08,1); //关闭显示
WriteCommandLCM(0x01,1); //显示清屏
WriteCommandLCM(0x06,1); // 显示光标移动设置
WriteCommandLCM(0x0C,1); // 显示开及光标设置
}
void DisplayOneChar(BYTE X, BYTE Y, BYTE DData)
{
Y &= 0x1;
X &= 0xF; //限制X不能大于15,Y不能大于1
if (Y) X |= 0x40; //当要显示第二行时地址码 0x40;
X += 0x80; // 算出指令码
WriteCommandLCM(X, 0); //这里不检测忙信号,发送地址码
WriteDataLCM(DData);
}
void DisplayListChar(BYTE X, BYTE Y, BYTE code *DData)
{
BYTE ListLength;
ListLength = 0;
Y &= 0x1;
X &= 0xF; //限制X不能大于15,Y不能大于1
while (DData[ListLength]) //若到达字串尾则退出
{
if (X
{
DisplayOneChar(X, Y, DData[ListLength]); //显示单个字符
}
ListLength++ ;
X++ ;
}
}
void Disp_number(unsigned int num,BYTE n)
{
BYTE a; //个位
BYTE b; //十位
BYTE c; //百位
BYTE d; //千位
// BYTE e; //万位
switch(n)
{
case 1:WriteDataLCM(num);
break;
case 2:b=num/10;
a=num%10;
WriteDataLCM(number[b]);
WriteDataLCM(number[a]);
break;
case 3:c=num/100;
b=num%100/10;
a=num%10;
WriteDataLCM(number[c]);
WriteDataLCM(number[b]);
WriteDataLCM(number[a]);
break;
case 4:d=num/1000;
c=num%1000/100;
b=num%1000%100/10;
a=num%10;
WriteDataLCM(number[d]);
WriteDataLCM(number[c]);
WriteDataLCM(number[b]);
WriteDataLCM(number[a]);
break;
}
}
//5ms延时
void Delay5Ms(void)
{
unsigned int TempCyc = 5552;
while(TempCyc--);
}
//400ms延时
void Delay400Ms(void)
{
BYTE TempCycA = 5;
unsigned int TempCycB;
while(TempCycA--)
{
TempCycB=7269;
while(TempCycB--);
};
}
五.实习总结:
匆匆的4周过去了,我们的实习也将步入结束。4周的实习让我感受良多;在这4周中我们从一开始的原理图方案的设计到实习实物的制作再到对作品的调试到最后的验收,经历了种种挫折与挑战。但在同伴的不懈努力以及老师的细心指导下,我们的作品在不断的辩证下产生了,虽然它简陋而粗糙,但它却依旧是我们汗水与努力的结晶。经过此次实习使我们学到许多,我们拥有了能够独立完成作品的能力,同时加强了我们的动手能力,增进了同学间的友谊。当在实习中也出现了种种问题,比如由于设计方案的不合理使我们一度陷入了进退两难的境地,最终只能浪费一块板,重新做了块新板。又例如由于
焊接的粗心导致了多次数据的检测错误等等。不管怎么说这都是一次有意义的实习。最后感谢我们可敬的老师,感谢他们辛苦的付出。
六.参考文献
《单片微机原理及应用》 清华大学 主编: 方方
《模拟电子电路》 北京大学出版社 主编 :宋树祥
《电子系统设计创新与实践实习指导书》
《模拟电子技术基础简明教程》清华大学 主编: 杨素行