15.1 项目分析
频率计又称为频率计数器,是一种专门对被测信号频率进行测量的电子测量仪器。频率计主要由四个部分构成:时基(T)电路、输入电路、计数显示电路以及控制电路。对于单片机来说无法直接测量正弦波,所以需要将正弦波变换成单片机可以测量的数字信号,频率计测量信号常用的方法是通过检测信号的单位时间内峰值次数来计算频率和周期,通常利用运放或者是三极管等电子元件采集信号的过零点信息,然后可以采用两种方法进行测量。
方法1:检测1秒之内脉冲的个数,这个次数就是信号的频率,但是这种方式检测过程太长,并且对单片机的计数频率要求很高,且无法测量信号的占空比,只适用于占空比为50%的方波;
方法2:检测一个信号的高低电平时间,然后将高低电平时间累加,最后计算出周期,利用周期计算频率,这种方式对单片机计数频率要求不高,并且可以完整的测量出一个信号的周期,占空比和频率,测量速度较快,一般普遍使用这种方法。
下面我们来利用方法2来实现测量一个方波信号的占空比,周期和频率。
15.2 原理图
15.3 源代码
/********************************************************************************************************* 头 文 件 引 用*********************************************************************************************************/#include //导入51单片机头文件/********************************************************************************************************* 数 据 类 型 定 义*********************************************************************************************************/#define u8 unsigned char //定义无符号字符型数据(0~255)#define u16 unsigned int //定义无符号整型数据(0~65535)#define u32 unsigned long/********************************************************************************************************* 硬 件 端 口 定 义*********************************************************************************************************///LCD1602控制端口#define LCD_DB P0 //LCD数据口sbit LCD_RS = P2^0 ; //数据命令选择sbit LCD_RW = P2^1 ; //读写控制sbit LCD_EN = P2^2 ; //使能控制//DS1302控制端口sbit Check = P1^0 ; //检测端口/********************************************************************************************************* 数 据 结 构 定 义*********************************************************************************************************/typedef struct{ u8 Count ; //溢出次数 u8 Duty ; //占空比 u32 Time_H ; //高电平时间 u32 Time_L ; //低电平时间 u16 Fosc ; //频率 u32 Cycle ; //周期 u8 Fosc_State; //频率单位 u8 Time_H_State ; //时间单位 u8 Time_L_State ; //时间单位 u8 Cycle_State ; //周期单位}PWMData;PWMData PWM_Data;u8 UI_Page ; //翻页/********************************************************Name :delay_msFunction :毫秒延时函数Paramater : ms:延时的时间Return :None********************************************************/void delay_ms( u16 ms ){ u8 i ; while( --ms ) for( i=0; i<110; i++ ) ;}/********************************************************************************************************* LCD1602 显 示 程 序*********************************************************************************************************//********************************************************Name :LCD_Write_CommandFunction :LCD写入命令Paramater : Command:命令代码Return :None********************************************************/void LCD_Write_Command( u8 Command ){ LCD_RS = 0 ; //命令模式 LCD_RW = 0 ; //写模式 LCD_EN = 0 ; //使能复位 LCD_DB = Command ; //发送数据到P0总线 delay_ms( 5 ) ; LCD_EN = 1 ; //使能拉高 delay_ms( 1 ) ; LCD_EN = 0 ; //下降沿数据写入 delay_ms( 1 ) ;}/********************************************************Name :LCD_Write_DataFunction :LCD写入数据Paramater : Data:数据Return :None********************************************************/void LCD_Write_Data( u8 Data ){ LCD_RS = 1 ; //数据模式 LCD_RW = 0 ; //写模式 LCD_EN = 0 ; //使能复位 LCD_DB = Data ; //发送数据到P0总线 delay_ms( 5 ) ; LCD_EN = 1 ; //使能拉高 delay_ms( 1 ) ; LCD_EN = 0 ; //下降沿数据写入 delay_ms( 1 ) ;}/********************************************************Name :LCD_InitFunction :LCD初始化Paramater :NoneReturn :None********************************************************/void LCD_Init(){ LCD_Write_Command( 0x38 ) ; //8位总线宽度+显示2行+每个字符占用5×10的点阵 LCD_Write_Command( 0x0C ) ; //开启显示+关闭光标+关闭光标显示 LCD_Write_Command( 0x06 ) ; //光标右移+写入数据后显示屏不移动 LCD_Write_Command( 0x01 ) ; //清屏}/********************************************************Name :LCD_Show_StringFunction :LCD显示字符串Paramater :NoneReturn :None********************************************************/void LCD_Show_String( u8 x, u8 y, u8 *str ){ u8 Address ; //计算坐标 switch( y ) { case 0: Address=0x80+x ; //第一行数据地址 break; case 1: Address=0xC0+x ; //第二行数据地址 break; default: break; } //写入数据 LCD_Write_Command( Address ) ; //设置写入地址 while( *str!='\0' ) { LCD_Write_Data( *str ) ; //写入数据 str ++ ; //指针地址累加 }}/********************************************************************************************************* 频 率 测 量 程 序*********************************************************************************************************/void TIM0_IRQHandler() interrupt 1{ TH0 = 0 ; TL0 = 0 ; PWM_Data.Count ++ ;}void EXIT0_IRQHandler() interrupt 0{ delay_ms( 10 ) ; UI_Page ++ ; if( UI_Page==1 ) { LCD_Show_String( 0, 0, "Time_H:000.000 s" ) ; LCD_Show_String( 0, 1, "Time_L:000.000 s" ) ; } if( UI_Page==2 ) { LCD_Show_String( 0, 0, "Cycle: 000.000 s" ) ; LCD_Show_String( 0, 1, " " ) ; } if( UI_Page==3 ) { LCD_Show_String( 0, 0, "Fosc:000.000 Hz " ) ; LCD_Show_String( 0, 1, "Duty:000% " ) ; UI_Page = 0 ; }}/********************************************************Name :Interrupt_InitFunction :初始化TIM0+EXTI0Paramater :NoneReturn :None********************************************************/void Interrupt_Init(){ IE = 0x83 ; IT0 = 1 ; TMOD = 0x01 ; TH0 = 0 ; TL0 = 0 ; PWM_Data.Count = 1 ; UI_Page = 0 ;}/********************************************************Name :DispFunction :计算参数Paramater :NoneReturn :None********************************************************/void Disp(){ PWM_Data.Cycle = 0 ; //采集高电平时间 while( Check==0 ) ; //等待端口产生高电平 TR0 = 1 ; TH0 = 0 ; TL0 = 0 ; PWM_Data.Count = 1 ; while( Check ) ; //等待高电平结束 PWM_Data.Time_H = PWM_Data.Count*( TH0*256+TL0 ); //获取高电平时间 PWM_Data.Cycle += PWM_Data.Time_H ; //采集低电平时间 TH0 = 0 ; TL0 = 0 ; PWM_Data.Count = 1 ; while( Check==0 ) ; //等待端口产生高电平 TR0 = 0 ; PWM_Data.Time_L = PWM_Data.Count*( TH0*256+TL0 ); //获取低电平时间 PWM_Data.Cycle += PWM_Data.Time_L ; //计算数据 PWM_Data.Fosc = 1000000/PWM_Data.Cycle ; PWM_Data.Duty = ( ( float )PWM_Data.Time_H/PWM_Data.Cycle )*100 ; if( PWM_Data.Fosc<999 ) { PWM_Data.Fosc_State = 2 ; PWM_Data.Fosc *= 1000 ; } else if( PWM_Data.Cycle>=1000 ) PWM_Data.Fosc_State = 1 ; if( PWM_Data.Cycle<999 ) PWM_Data.Cycle_State = 2 ; else if( ( PWM_Data.Cycle>=1000 )&&( PWM_Data.Cycle<999999 ) ) PWM_Data.Cycle_State = 1 ; else if( PWM_Data.Cycle>=1000000 ) { PWM_Data.Cycle_State = 1 ; PWM_Data.Cycle /= 1000 ; } if( PWM_Data.Time_L<999 ) PWM_Data.Time_L_State = 2 ; else if( ( PWM_Data.Time_L>=1000 )&&( PWM_Data.Time_L<999999 ) ) PWM_Data.Time_L_State = 1 ; else if( PWM_Data.Time_L>=1000000 ) { PWM_Data.Time_L_State = 1 ; PWM_Data.Time_L /= 1000 ; } if( PWM_Data.Time_H<999 ) PWM_Data.Time_H_State = 2 ; else if( ( PWM_Data.Time_H>=1000 )&&( PWM_Data.Time_H<999999 ) ) PWM_Data.Time_H_State = 1 ; else if( PWM_Data.Time_H>=1000000 ) { PWM_Data.Time_H_State = 1 ; PWM_Data.Time_H /= 1000 ; } delay_ms( 100 ) ;}/********************************************************************************************************* 主 函 数*********************************************************************************************************/void main(){ Interrupt_Init() ; LCD_Init() ; LCD_Show_String( 0, 0, "Fosc:000.000 Hz " ) ; LCD_Show_String( 0, 1, "Duty:000% " ) ; while( 1 ) { Disp() ; if( UI_Page==0 ) { //显示频率 LCD_Write_Command( 0x80+5 ) ; LCD_Write_Data( 0x30+PWM_Data.Fosc/100000 ) ; LCD_Write_Data( 0x30+PWM_Data.Fosc%100000/10000 ) ; LCD_Write_Data( 0x30+PWM_Data.Fosc%10000/1000 ) ; LCD_Write_Data( '.' ) ; LCD_Write_Data( 0x30+PWM_Data.Fosc%1000/100 ) ; LCD_Write_Data( 0x30+PWM_Data.Fosc%100/10 ) ; LCD_Write_Data( 0x30+PWM_Data.Fosc%10 ) ; if( PWM_Data.Fosc_State==0 ) LCD_Write_Data( ' ' ) ; else if( PWM_Data.Fosc_State==1 ) LCD_Write_Data( 'k' ) ; //显示占空比 LCD_Write_Command( 0xC0+5 ) ; LCD_Write_Data( 0x30+PWM_Data.Duty/100 ) ; LCD_Write_Data( 0x30+PWM_Data.Duty%100/10 ) ; LCD_Write_Data( 0x30+PWM_Data.Duty%10 ) ; } else if( UI_Page==1 ) { //显示高电平时间 LCD_Write_Command( 0x80+7 ) ; LCD_Write_Data( 0x30+PWM_Data.Time_H/100000 ) ; LCD_Write_Data( 0x30+PWM_Data.Time_H%100000/10000 ) ; LCD_Write_Data( 0x30+PWM_Data.Time_H%10000/1000 ) ; LCD_Write_Data( '.' ) ; LCD_Write_Data( 0x30+PWM_Data.Time_H%1000/100 ) ; LCD_Write_Data( 0x30+PWM_Data.Time_H%100/10 ) ; LCD_Write_Data( 0x30+PWM_Data.Time_H%10 ) ; if( PWM_Data.Time_H_State==0 ) LCD_Write_Data( ' ' ) ; else if( PWM_Data.Time_H_State==1 ) LCD_Write_Data( 'm' ) ; else if( PWM_Data.Time_H_State==2 ) LCD_Write_Data( 'u' ) ; //显示低电平时间 LCD_Write_Command( 0xC0+7 ) ; LCD_Write_Data( 0x30+PWM_Data.Time_L/100000 ) ; LCD_Write_Data( 0x30+PWM_Data.Time_L%100000/10000 ) ; LCD_Write_Data( 0x30+PWM_Data.Time_L%10000/1000 ) ; LCD_Write_Data( '.' ) ; LCD_Write_Data( 0x30+PWM_Data.Time_L%1000/100 ) ; LCD_Write_Data( 0x30+PWM_Data.Time_L%100/10 ) ; LCD_Write_Data( 0x30+PWM_Data.Time_L%10 ) ; if( PWM_Data.Time_L_State==0 ) LCD_Write_Data( ' ' ) ; else if( PWM_Data.Time_L_State==1 ) LCD_Write_Data( 'm' ) ; else if( PWM_Data.Time_L_State==2 ) LCD_Write_Data( 'u' ) ; } else if( UI_Page==2 ) { LCD_Write_Command( 0x80+7 ) ; LCD_Write_Data( 0x30+PWM_Data.Cycle/100000 ) ; LCD_Write_Data( 0x30+PWM_Data.Cycle%100000/10000 ) ; LCD_Write_Data( 0x30+PWM_Data.Cycle%10000/1000 ) ; LCD_Write_Data( '.' ) ; LCD_Write_Data( 0x30+PWM_Data.Cycle%1000/100 ) ; LCD_Write_Data( 0x30+PWM_Data.Cycle%100/10 ) ; LCD_Write_Data( 0x30+PWM_Data.Cycle%10 ) ; if( PWM_Data.Cycle_State==0 ) LCD_Write_Data( ' ' ) ; else if( PWM_Data.Cycle_State==1 ) LCD_Write_Data( 'm' ) ; else if( PWM_Data.Cycle_State==2 ) LCD_Write_Data( 'u' ) ; } }}
15.4 仿真截图