使用51单片机制作频率计-测脉冲数法详解

b43078ef18484e4f8717df5f4170a459.png

本文为LED-执棋困局,csdn原创首发。
> 发布人:LED-执棋困局
> 欢迎大家与LED-执棋困局相互点赞+关注+收藏+评论,也祝大家顺顺利利,平平安安。
> 我的格言是:“尽最大努力,做最好的自己!
版权声明:本文为CSDN博主「LCD-执棋困局」的原创文章。

目录

一、频率计简介

1.1频率计概述

1.2频率计组成

1.3频率计原理

1.4测量原理

二、仿真设计

2.1频率计仿真

2.2仿真展示

三、软件程序设计

3.1主程序程序设计

3.2定时器初始化程序设计

3.3中断程序设计

3.4计数程序设计

3.5数码管程序设计

四、提高测量精度

4.1结果展现

4.2测量误差

4.3提高精度


一、频率计简介

1.1频率计概述

数字频率计是计算机、通讯设备、音频视频等各领域不可缺少的测量仪器。它是一种用十进制数字显示被测信号频率的数字测量仪器。它的基本功能是测量方波信号及其他各种单位时间内变化的物理量。在进行模拟、数字电路的设计、安装、调试过程中,由于其使用十进制数显示,测量迅速,精确度高等因素,经常要用到频率计。

1.2频率计组成

本文频率计为简易基础版,由定时器1中断服务模块、计数器0计数模块、数码管显示模块和主程序模块四个部分组成。

1.3频率计原理

本文频率计时钟频率为12MHZ,测量的脉冲频率可以自调。频率计以at89c51为核心,利用内部自带的定时器和计数器。每个at89c51内部自带2个定时器/计数器,可以通过编程方法来完成定时器和计数器的工作,让定时器每1ms中断一次,时间累加到1s时,暂停计数,TR0=0,每次定时器进入中断时,暂时关闭定时器1,能有效防止中断还没结束,下一个中断就来执行的情况,提高测量的精度,同时计数器T0计数,利用高8位TH0和低8位TL0计算出1s内的脉冲数/频率。

1.4测量原理

测量频率有两种方法,一个叫计数测量法(测量1s内脉冲数),另一种叫测量周期法(利用定时器测量周期,再利用f=1/T求出频率)。

前者适用于高频测量,后者适用于低频测量。

本文采用计数测量法

计数测量法如何计算频率?

频率f定义:单位时间内的脉冲数。

在t时间内测量到N个脉冲,则频率f=N/t,特殊情况下(t=1s),f=N。

如下图(定时器T0定时累加1s),f=50hz,则频率f=脉冲数N=1s/0.02s=50。

93b415de5e424832871e56b61ecefe70.png

二、仿真设计

2.1频率计仿真

如图所示,是在proteus软件频率计的仿真。由于要测量1s内的脉冲数,所以要接计数器T0的引脚INT0。

db7d7a6892d14affa128ca818aa3a374.png

d5a91eb8501e42779ec8a04dd63fa161.png

255c3834b93548dca34154515d0d2ead.png

2.2仿真展示

数码管线码a、b、c、d、e、f、g、DP接P0,由于P0口电流过小,驱动能力不足,所以一定要接排阻respack-8提高驱动能力,至于74HC245芯片也是提高驱动能力的,可以不用。而段码接到P2低4位。频率计接到计数器T0引脚INT0。

以上是我自己接的,大家也可以接其他引脚。

218ec6b184764237a31dd25896fda84a.png

三、软件程序设计

3.1主程序程序设计

主程序调用定时器T1和计数器T0初始化,在while(1)调用数码管显示服务模块。

/******************************************************************
功能:1s内脉冲数及频率的测量
******************************************************************/
#include <reg51.h>
#include "display.h"

void Timer0_Init();
void Timer1_Init();
void dis_service();

unsigned int cnd=0;//控制测量脉冲数时间变量
unsigned int num;//脉冲数变量

void main()
{
	Timer0_Init();
	Timer1_Init();
	EA=1;
	while(1)
	{
		dis_service();
	}
}

3.2定时器初始化程序设计

void Timer1_Init(void)		//1毫秒@12.000MHz
{
	TMOD &= 0x0F;			//设置定时器模式
	TMOD |= 0x10;			//设置定时器模式
	TL1 = 0x18;				//设置定时初始值
	TH1 = 0xFC;				//设置定时初始值
	ET1=1;           //定时器1中断打开
	TF1 = 0;				//清除TF1标志
	TR1 = 1;				//定时器1开始计时
}

3.3中断程序设计

1ms中断一次,要求测量时间为1s,所以定义变量cnd,让其累加到1000(中断1000次)后,开始计算脉冲数。计算脉冲数后,要将cnd、TH0、TL0清0,确保下次计算的正确性。

void T1_timer() interrupt 3 //1s测量脉冲数及频率
{
	TR1=0;
	cnd++;
	if(cnd>=1000)//1s
	{
		TR0=0;
		cnd=0;
		num=TH0*256+TL0;//脉冲数
		TH0=TL0=0;//重新赋予计数初值
		TR0=1;
	}
	display();
	TL1 = 0x18;				//设置定时初始值
	TH1 = 0xFC;				//设置定时初始值
	TR1=1;
}

3.4计数程序设计

脉冲数从0开始测量,所以TL0、TH0初值为0。

void Timer0_Init(void)		//计数器T0
{
	TMOD &= 0xf0;			//设置计数器模式 0000 0101
	TMOD |= 0x05;			//设置计数器模式
	TL0 = 0;				//设置计数初始值
	TH0 = 0;				//设置计数初始值
	TF0 = 0;				//清除TF1标志
	ET0=1;          //计数器0中断打开
	TR0 = 1;				//计数器1开始计时
}

3.5数码管程序设计

用于显示数码管的每一位。

void dis_service()
{
	LEDBuf[0]=num/1000;
	LEDBuf[1]=num/100%10;
	LEDBuf[2]=num/10%10;
	LEDBuf[3]=num%10;
}

display.c

#include "display.h"

unsigned char code leddata[]={
	              0x3F, //0
	              0x06, //1
                0x5B, //2
	              0x4F, //3
	              0x66, //4																																																																		
	              0x6D, //5
	              0x7D, //6
	              0x07, //7
	              0x7F, //8
	              0x6F, //9
	              0x77, //A																																																																							
	              0x7C, //B
	              0x39, //C
	              0x5E, //D
	              0x79, //E
	              0x71, //F
	              0x76, //H
	              0x38, //L
	              0x37, //n
                0x3E, //u	
	              0x73, //P																																																																	
	              0x5C, //o
	              0x40, //-
	              0x00  //熄灭
                             };//数码管段码表

unsigned char LEDBuf[]={0,0,0,0};//缓冲区
unsigned char code PLACE_COOE[]={0xfe,0xfd,0xfb,0xf7};//位码
/******************************************************************************************
函数名:display
功能:数码管显示函数
参数:无
返回值:无
******************************************************************************************/
void display()
{
	static unsigned char i=0;
	switch(i)
	{
		case 0:
			IO_DIG=0x00;//消隐
			IO_DIG=leddata[LEDBuf[0]];//段码
	    IO_PLACE=PLACE_COOE[0];//位码
      i++;
		  break;
		case 1:
			IO_DIG=0x00;//消隐
			IO_DIG=leddata[LEDBuf[1]];//段码
	    IO_PLACE=PLACE_COOE[1];//位码
      i++;
		  break;
		case 2:
			IO_DIG=0x00;//消隐
			IO_DIG=leddata[LEDBuf[2]];//段码
	    IO_PLACE=PLACE_COOE[2];//位码
      i++;
		  break;
		case 3:
			IO_DIG=0x00;//消隐
			IO_DIG=leddata[LEDBuf[3]];//段码
	    IO_PLACE=PLACE_COOE[3];//位码
      i=0;
		  break;
	}
}

display.h

#ifndef __DISPLAY_H__
#define __DISPLAY_H__

#include <reg51.h>

#define IO_DIG   P0 //段码IO
#define IO_PLACE P2 //位码IO

#define N 4 //数码管个数

unsigned char code leddata[];//变量声明
extern unsigned char LEDBuf[];

void display();//数码管显示函数声明

#endif

四、提高测量精度

4.1结果展现

50hz:

5873e54f317840779fb7a3ab628a3ce8.png

100hz:

e2a4bcfd7b854e46a7a94f38b95e20e1.png

500hz:

184be1d169924ab1b73e269664cbb4ff.png

1000hz:

cb77f2995eb344e28ba9130f1fa615d8.png

4.2测量误差

4.2.1误差公式

本文测的是1s内的脉冲数,即为频率。

那频率计误差怎么求呢?其实很简单,就是先计算出实际值与理论值的差的绝对值,然后除于理论值,最后乘于100%。

误差=( |实际值-理论值| /实际值)×100%

4.2.2误差计算&结果

频率计测量50hz:

[(54-50)/54 ]×100%≈7.4%

频率计测量100hz:

[(108-100)/108]×100%≈7.4%

频率计测量500hz:

[(539-500)/539]×100%≈7.24%

频率计测量1000hz:

[(1077-1000)/1077]×100%≈7.15%

随着测量频率提高,误差有所降低。

4.3提高精度

1.选择更加稳定的信号,例如使用石英晶体振荡器产生的信号。

2.本文测量时间为1s,可以提高测量时间,在较长的时间,能测量更多的信号周期,从而减小误差。

3.在软件编程,在执行中断时,可以暂时关闭定时器T1,即TR1=0,在本次中断结束时再打开定时器T1,即TR1=1。

五、总结

本文讲述了关于频率计的测量方法之一的计数测量法,就是利用频率的定义(单位时间内的脉冲数)。还学会了频率计的相关原理,利用at89c51内部的定时器T0定时累加到1s,同时利用内部的计数器T1的高8位和低8位测量脉冲数,从而借助数码管模块来显示到数码管屏幕上。此外,我们还学会了如何计算误差和提高测量精度的方法。

hello!我是博主LED-执棋困局,下一文章—基于51单片机制作门铃,我们下期再见!你们的关注支持就是我的动力!

物联沃分享整理
物联沃-IOTWORD物联网 » 使用51单片机制作频率计-测脉冲数法详解

发表评论