图2-1 频率测量原理图
在计数时会出现图2-1所示的丢失脉冲的情况。
第一个丢失的脉冲是由于开始检测时脉冲宽度已小于机器周期T;第二个丢失的脉冲的负跳变在定时之外。
定时时间内出现脉冲丢失,将引起测量精度降低。
脉冲频率越低,这种误差越大。
显然对于较低频率的脉冲测量不适合采用测量频率法。
而我们本次设计就是采用这种测量频率法对被测脉冲信号进行频率测量,为解决图一中脉冲的丢失这个问题,我们在程序设计中实现了计数开始与脉冲上升沿的同步控制。
2.1.2频率计的基本原理
频率计最基本的工作原理为:
当被测信号在特定时间段T内的周期个数为N时,则被测信号的频率f=N/T。
在一个测量周期过程中,被测周期信号在输入电路中经过放大、整形、微分操作之后形成特定周期的窄脉冲,送到主门的一个输入端。
主门另外一个输入端为时基电路产生电路产生的闸门脉冲。
在闸门脉冲开启主门的期间,特定周期的窄脉冲才能通过主门,从而进入计数器进行计数,计数器的显示电路则用来显示被测信号的频率值,内部控制电路则用来完成各种测量功能之间的切换并实现测量设置.
图2-2 频率计原理图
2.2总体设计思路
频率计是一种专门对被测信号频率进行测量的电子测量仪器,是我们经常会用到的实验仪器之一,频率的测量实际上就是在单位时间内对脉冲信号进行计数,计数值就是信号频率。
本文介绍了一种基于单片机的电子频率计的设计方法,此电子频率以AT89C51单片机为控制核心,可将外部的频率脉冲信号通过单片机计数端输入,由定时器/计数器T0负责定时,定时器/计数器T1负责对被测信号计数,一旦T0定时时间到,立刻终止T1的计数,此时T1的计数值便是单位时间内的脉冲个数,我们将T0的定时时间设为1s,当T0定时满1s后,立即停止T1计数,此时T1的计数值即为被测信号的频率。
该频率计的测量范围为1Hz~65534Hz,被测脉冲信号的频率可以随时进行调整,通过LCD液晶显示模块对被测信号的频率进行实时显示。
2.3具体模块
根据上述系统分析,该系统包括被测频率脉冲信号、单片机晶振电路、以AT89C51单片机为核心的频率测量模块、LCD液晶显示模块。
各模块作用如下:
1.脉冲信号:
就是被测信号,可以随时调整其频率,以便于单片机测量。
2.单片机晶振电路:
由于单片机的内部时钟方式是用芯片内部振荡电路,精度不高,温飘也较大,外部时钟,分RC振荡和石英晶振,RC精度不高,成本低,石英晶振,精度高,稳定性好,故我们采用单片机的晶振电路提供时钟信号。
3.AT89C51频率测量模块:
主要负责对脉冲信号的计数,并且驱动LCD显示模块实时显示测量值。
4.LCD液晶显示模块:
对单片机测量的频率进行实时显示。
综上所述频率计的系统设计由被测频率脉冲信号、单片机晶振电路、以AT89C51单片机为核心的频率测量模块、LCD液晶显示模块等组成,频率计的总体设计框图如图2-3所示。
图2-3 频率计总体设计框图
第3章硬件电路设计
3.1 AT89C51主控制器模块
电子频率计以AT89C51单片机为控制核心,可将外部的频率脉冲信号通过单片机计数端输入,由定时器/计数器T0负责定时,定时器/计数器T1(P3.5)负责对被测信号计数,一旦T0定时时间到,立刻终止T1的计数,此时T1的计数值便是单位时间内的脉冲个数,我们将T0的定时时间设为1s,当T0定时满1s后,立即停止T1计数,此时T1的计数值即为被测信号的频率。
图 3-1AT89C51主控模块
3.1.1 主要特性
AT89C51 提供以下标准功能:
4k 字节Flash 闪速存储器,128字节内部RAM,32 个I/O 口线,两个16位定时/计数器,一个5向量两级中断结构,一个全双工串行通信口,片内振荡器及时钟电路。
同时,AT89C51可降至0Hz的静态逻辑操作,并支持两种软件可选的节电工作模式。
空闲方式停止CPU的工作,但允许RAM,定时/计数器,串行通信口及中断系统继续工作。
掉电方式保存RAM中的内容,但振荡器停止工作并禁止其它所有部件工作直到下一个硬件复位。
3.1.2管脚说明
VCC:
供电电压。
GND:
接地。
P0口:
P0口为一个8位的漏级开路双向I/O口,每脚可吸收8TTL门电流。
当P0口的管脚第一次写1时,被定义为高阻输入。
在FIASH编程时,P0口作为原码输入口,当FIASH进行校验时,P0输出原码,此时P0外部必须接上拉电阻。
P1口:
P1口是一个内部提供上拉电阻的8位双向I/O口,P1口缓冲器能接收输出4TTL门电流。
P2口:
P2口为一个内部上拉电阻的8位双向I/O口,P2口缓冲器可接收,输出4个TTL门电流,当P2口被写“1”时,其管脚被内部上拉电阻拉高,且作为输入。
P2口在FLASH编程和校验时接收高八位地址信号和控制信号。
P3口:
P3口管脚是8个带内部上拉电阻的双向I/O口,可接收输出4个TTL门电流。
当P3口写入“1”后,它们被内部上拉为高电平,并用作输入。
作为输入,由于外部下拉为低电平,P3口将输出电流(ILL)这是由于上拉的缘故。
RST:
复位输入。
当振荡器复位器件时,要保持RST脚两个机器周期的高电平时间。
XTAL1:
反向振荡放大器的输入及内部时钟工作电路的输入。
XTAL2:
来自反向振荡器的输出。
振荡器特性:
XTAL1和XTAL2分别为反向放大器的输入和输出。
该反向放大器可以配置为片内振荡器。
石晶振荡和陶瓷振荡均可采用。
如采用外部时钟源驱动器件,XTAL2应不接。
有余输入至内部时钟信号要通过一个二分频触发器,因此对外部时钟信号的脉宽无任何要求,但必须保证脉冲的高低电平要求的宽度。
3.2 晶振电路
由于单片机的内部时钟方式是用芯片内部振荡电路,精度不高,温漂也较大,外部时钟,分RC振荡和石英晶振,RC精度不高,成本低,石英晶振,精度高,稳定性好,故我们采用单片机的晶振电路提供时钟信号。
图3-2 晶振电路
3.3频率脉冲信号
频率脉冲信号就是被测信号,可以随时调整其频率,以便于单片机测量,直接在protues左侧工具条内的一个GeneratorMode工具中选择DCLOCK放置频率脉冲信号(如图3-3)。
图3-3 频率脉冲信号
3.4 LCD液晶显示模块
LCD液晶显示器是一种被动式的显示器,与LED不同,液晶本身并不发光,而是利用液晶在电压作用下,能改变光线通过方向的特性而达到显示白底黑字或黑底白字的目的。
液晶显示器具有微功耗、体积小、重量轻、超薄型等诸多其他显示器件所无法比拟的优点,在袖珍式仪表和低功耗系统中,得到越来越广泛的应用,目前市场上液晶显示器种类繁多,按排列形状可分为字段型、点阵字符型、点阵图形型,在单片机应用系统中,常使用点阵字符型LCD显示器。
字符型液晶显示模块组件内部主要由LCD显示屏(LCDPanel)、控制器(Controller)、驱动器(Driver)、少量阻容原件、结构件等装配在PCB上构成。
第4章系统的软件设计
4.1 频率测量模块
将定时器T0设置在定时方式2,定时时间为250us,满4000次中断正好是1s,定时器T1工作于计数方式1,计数初值为0。
在启动定时器T0开始定时后,随即对送到T1(P3.5)引脚的被测脉冲进行计数,当T0定时满1s后,立即停止T1计数,关闭定时器T0,T1的计数值即为被测信号的频率,程序流程图如图4-1。
图4-1 频率测量
频率测量其中,中断服务子程序流程图如下:
图4-2中断服务子程序
频率测量主函数中,还进行了数据转换以及调用显示模块进行显示其程序流程框图如下:
图4-3 频率测量主函数
频率测量模块源程序:
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
voidinit_lcd(void);
voiddisp_str(ucharx,uchary,uchar*p);
sbitFS=P3^5;//被测信号FS输入端
bitRDY=0; //测量完成标志
uintmsn;//定时中断计数
uintcount(void) //测量FS的频率
{RDY=0;
TMOD=0x52; //T0:
定时方式2,T1:
计数方式1
TH0=TL0=6;//T0定时时间为250us
msn=4000; //4000次中断正好1s
TH1=TL1=0x00;//T1工作于计数方式,初值为0
ET0=1; //允许T0中断
EA=1; //开中断
while(FS==1); //等待被测信号变低
while(FS==0); //等待被测信号变高
TR0=1; //T0开始定时
TR1=1; //T1开始计数
while(RDY==0); //等待1s
TR1=0; //关闭T1、T0
TR0=0;
return(TH1*256+TL1);//返回计数值
}
voidtimer0(void)interrupt1using1
{msn--;
if(msn==0) //如果1s已到
RDY=1; //设置测量完成标志位
}
voidmain()
{uintf;
ucharstr[9]="f=Hz";
uchari;
init_lcd(); //液晶屏初始化
while
(1)
{f=count();//测量频率
_nop_();
for(i=6;i>=2;i--) //测量结果转换为5位ASCII码
{str[i]=f%10+0x30;
f=f/10;
}
disp_str(0,3,str);//显示测量结果
}
}
4.2 液晶显示模块
液晶显示模块是一个显示的子程序,主要供频率测量模块调用,以便在液晶屏上显示出实时的频率测量值,它的编程比较固定,无非就是按照LCD液晶显示屏的参数要求的指令系统来编写程序,其程序流程图4-4。
图4-4 液晶显示
液晶显示模块源程序:
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
sbitRS=P2^0;//数据/命令寄存器选择控制端
sbitRW=P2^1;//读写控制端
sbitE=P2^2; //使能控制端
sfrLCD=0x90;//P1口作为总线端口
sbitBF=LCD^7;//就绪线BF,低电平有效
voidlcd_cmd(ucharcmd)
{LCD=cmd;
RS=0;//选择命令寄存器
RW=0;//执行写数据操作
E=1;
_nop_();//延时
E=0; //使能信号有效
while
(1)
{LCD=0xff;//总线变高
RS=0; //选择命令寄存器
RW=1;//读操作
E=0;//使能信号有效
_nop_();
E=1;//撤销使能信号
if(BF==0)break;//如果就绪,返回
}
}
voidlcd_dat(uchardat)
{
LCD=dat;//显示数据总线
RS=1;//选择数据寄存器
RW=0;//执行写数据操作
E=1;
_nop_();//延时
E=0; //使能信号有效
while
(1)
{LCD=0xff;//总线变高
RS=0; //选择命令寄存器
RW=1;//读操作
E=0;//使能信号有效
_nop_();
E=1;//撤销使能信号
if(BF==0)break;//如果就绪,返回
dat=LCD;
}
}
voidinit_lcd(void)//初始化液晶屏
{
lcd_cmd(0x01);//清屏幕
lcd_cmd(0x3c);//设置双行显示,5*10点阵
lcd_cmd(0x0c);//开显示,关闭光标
}
voiddisp_str(ucharx,uchary,uchar*p)//在x行、y列显示字符串p
{
if(x==0)//如果在第一行显示
lcd_cmd(0x80+y);//设置写入地址
else //如果在第二行显示
lcd_cmd(0xc0+y);//设置写入地址
while(*p) //将字符依次发送到液晶屏
lcd_dat(*p++);
}
第5章频率计的系统调试与仿真
5.1KEIL中对程序的调试
德国的KEIL软件公司提供了一流的8051系列开发工具,将软件开发工具绑定到不同的套件或工具包中。
KEIL8051开发工具套件可用于编译C源程序、汇编源程序,链接和定位目标文件及库,创建HEX文件以及调试目标程序,我们进入到KEIL中的集成开发环境,对所编写的程序进行了调试,使其生成了目标文件(HEX文件),如图5-1所示
图5-1程序的调试
5.2 Protues中对系统的仿真
我们采用Protues软件对系统进行仿真,将KEIL生成的HEX文件下载入单片机中,点击OK开始进行系统的仿真,如图5-2所示
图5-2Protues中对hex文件的选择
在Protues中双击被测频率脉冲信号t1,在Frequency中将其频率设定为6443,如图5-3所示:
图5-3频率的设定
点击OK,然后在Protues中点击Play开始进行系统仿真,仿真结果如图5-4所示:
图5-4仿真结果
我们按照上面的方法,依次改变被测频率脉冲信号的频率,在Protues软件中进行反复的调试仿真,软件仿真结果如图5-5:
图5-5多次仿真数据结果
从记录的数据可以看出,系统软件仿真误差很小,在信号频率范围内测量出来的频率基本上就是输入信号的频率,在超出这个范围后,才出现很小的误差。
这可能是由于硬件电路信号传输延时,或者晶振电路产生的时钟信号误差造成的,也可能是由于软件中执行语句的延时造成的,在高频率下就会出现很小的误差,但是可以看出,误差在允许范围内,所设计的电路基本符合要求。
附录
源程序:
频率测量模块源程序
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
voidinit_lcd(void);
voiddisp_str(ucharx,uchary,uchar*p);
sbitFS=P3^5;//被测信号FS输入端
bitRDY=0; //测量完成标志
uintmsn;//定时中断计数
uintcount(void) //测量FS的频率
{RDY=0;
TMOD=0x52; //T0:
定时方式2,T1:
计数方式1
TH0=TL0=6;//T0定时时间为250us
msn=4000; //4000次中断正好1s
TH1=TL1=0x00;//T1工作于计数方式,初值为0
ET0=1; //允许T0中断
EA=1; //开中断
while(FS==1); //等待被测信号变低
while(FS==0); //等待被测信号变高
TR0=1; //T0开始定时
TR1=1; //T1开始计数
while(RDY==0); //等待1s
TR1=0; //关闭T1、T0
TR0=0;
return(TH1*256+TL1);//返回计数值
}
voidtimer0(void)interrupt1using1
{msn--;
if(msn==0) //如果1s已到
RDY=1; //设置测量完成标志位
}
voidmain()
{uintf;
ucharstr[9]="f=Hz";
uchari;
init_lcd(); //液晶屏初始化
while
(1)
{f=count();//测量频率
_nop_();
for(i=6;i>=2;i--) //测量结果转换为5位ASCII码
{str[i]=f%10+0x30;
f=f/10;
}
disp_str(0,3,str);//显示测量结果
}
}
液晶显示模块源程序:
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
sbitRS=P2^0;//数据/命令寄存器选择控制端
sbitRW=P2^1;//读写控制端
sbitE=P2^2; //使能控制端
sfrLCD=0x90;//P1口作为总线端口
sbitBF=LCD^7;//就绪线BF,低电平有效
voidlcd_cmd(ucharcmd)
{LCD=cmd;
RS=0;//选择命令寄存器
RW=0;//执行写数据操作
E=1;
_nop_();//延时
E=0; //使能信号有效
while
(1)
{LCD=0xff;//总线变高
RS=0; //选择命令寄存器
RW=1;//读操作
E=0;//使能信号有效
_nop_();
E=1;//撤销使能信号
if(BF==0)break;//如果就绪,返回
}
}
voidlcd_dat(uchardat)
{
LCD=dat;//显示数据总线
RS=1;//选择数据寄存器
RW=0;//执行写数据操作
E=1;
_nop_();//延时
E=0; //使能信号有效
while
(1)
{LCD=0xff;//总线变高
RS=0; //选择命令寄存器
RW=1;//读操作
E=0;//使能信号有效
_nop_();
E=1;//撤销使能信号
if(BF==0)break;//如果就绪,返回
dat=LCD;
}
}
voidinit_lcd(void)//初始化液晶屏
{
lcd_cmd(0x01);//清屏幕
lcd_cmd(0x3c);//设置双行显示,5*10点阵
lcd_cmd(0x0c);//开显示,关闭光标
}
voiddisp_str(ucharx,uchary,uchar*p)//在x行、y列显示字符串p
{
if(x==0)//如果在第一行显示
lcd_cmd(0x80+y);//设置写入地址
else //如果在第二行显示
lcd_cmd(0xc0+y);//设置写入地址
while(*p) //将字符依次发送到液晶屏
lcd_dat(*p++);
}
总结