51单片机的延时及定时器

       经常看些东西,有时在书上,有时在网上,还有的是突然醒悟,也该做一些总结,最近想总结一下单片机的定时以及延时问题。

单片机主要是两种延时方式:

1.硬件延时:要用到定时器/计数器,这种方法可以提高CPU的工作效率,也能做到精确延时;

2.软件延时:软件延时有时候不能够做到非常精确地延时,主要靠循环体或是一些无意义的指令来完成。

单片机都有一个属于自己的晶振频率:11.0592Mhz(主要是为了设置波特率的方便),12Mhz6Mhz等(后面的例子全都用12M晶振)。对于12Mhz的晶振频率,一个机器周期为1us,对于51单片机的库函数就有nop()这个函数(调用时需要#include<intrins.h>),实现延时一个机器周期。那么就有了简单的软件延时程序。可以有delay5us,delay10us等程序,只需要在程序里用nop()就可以了,但是要注意调用该函数需要有一个调用指令(2us),结束后也有个结束指令(2us),而且在函数里调用该函数,只有最内层的函数有结束指令。

例如:

void Delay5us( ) {

_NOP_( );

}

void Delay10us()

{

_NOP_( );

_NOP_( );

_NOP_( );

_NOP_( );

_NOP_( );

_NOP_( );

}

分别是两个不同的延时函数。


再就是我们会经常使用的for循环延时程序了,我现在也是在学单片机,在郭天祥老师的程序里经常会有

void delay(unsigned int i)

{

while(i--);

}

在这个程序里,如果没有中断完全可以用仿真模拟的方法并自己调整,直到自己想要的延时时间,因为在后面中断,串口,模拟时序的时候并没有那么精确的延时,都是一个比较大的时间段,但是学了就尽量弄得精确一些。(如果是大神完全可以抠汇编,用示波器,当然我现在都不会。),因为这是c语言编程,不是汇编,汇编的一条指令(也就是机器指令)机器周期是一定的,也就是说可以很精确,但是c不行,需要取决于很多东西(如编译器,cpu等等)。

继续说这个程序,他就是用不断循环的做一些无意义的事,达到延时的目的。因为你不能准确的知道一条c指令确是多少时间(或者说会有误差),在上面的程序里,当i=1时,大约延时10us。下面再给出几个延时函数,仅供参考。

200ms延时子程序

程序:

void delay200ms(void)

{

unsigned char i,j,k;

for(i=5;i>0;i--)

for(j=132;j>0;j--)

for(k=150;k>0;k--);

}

10ms延时子程序

程序:

void delay10ms(void)

{

unsigned char i,j,k;

for(i=5;i>0;i--)

for(j=4;j>0;j--)

for(k=248;k>0;k--);

}

关于函数延时以及一些基本的程序,我也只了解这些,再来说说定时器实现精确延时。

定时器有4种模式,一般较为常用的为方式1,因为一旦溢出就会申请中断,因此一次溢出共需要65536us,约等于63.5ms,若定时器工作在方式2,则可实现极短时间的精确延时;如使用其他定时方式,则要考虑重装定时初值的时间(重装定时器初值占用2个机器周期)。

在实际应用中,定时常采用中断方式,如进行适当的循环可实现几秒甚至更长时间的延时。使用定时器/计数器延时从程序的执行效率和稳定性两方面考虑都是最佳的方案。

在这里,用定时器中断服务程序中,需要给定时器重装初值,完成定时器中断服务程序就回到主程序,但是要注意,若是没有关闭中断,在运行中断服务程序(进入中断服务程序需要时间)而且没有到给它重新赋值的语句前,定时器也在计数中,只有在重新赋初值后的瞬间,又开始从新的值处开始计时,因此,这是一个误差,解决误差的办法就是赋值初值的时候加上它当前的值。TH0=TH0+初值,TL0=TL0+初值。

另外,中断服务程序不要过长,或者有一个或多个延时程序(不是说不能,是不建议),否则中断服务程序还没结束就又进入中断,会造成崩溃。

下面给出一个程序,实现数码管每隔1s循环显示0-F,实现准确延时,当然不是绝对的准确。(我就直接把我学习单片机开发板上面的程序拷过来了)。这个程序重新赋值的时候没有向我上面说的那样,中断服务程序也略显赘余,可以把if放在主函数while中。

/**************************************************************************************
*		              定时器1实验												  *
实现现象:下载程序后数码管最后一位间隔一秒循环显示0-F。使用单片机内部定时器可以实现准确延时。																				  
***************************************************************************************/

#include "reg52.h"			 //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;

sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
					0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
u8 n=0;
/*******************************************************************************
* 函 数 名         : Timer1Init
* 函数功能		   : 定时器1初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer1Init()
{
	TMOD|=0X10;//选择为定时器1模式,工作方式1,仅用TR1打开启动。

	TH1=0XFC;	//给定时器赋初值,定时1ms
	TL1=0X18;	
	ET1=1;//打开定时器1中断允许
	EA=1;//打开总中断
	TR1=1;//打开定时器			
}

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	LSA=0;
	LSB=0;
	LSC=0;
	Timer1Init();  //定时器1初始化
	while(1);		
}

/*******************************************************************************
* 函 数 名         : void Timer1() interrupt 3
* 函数功能		   : 定时器0中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer1() interrupt 3
{
	static u16 i;
	TH1=0XFC;	//给定时器赋初值,定时1ms
	TL1=0X18;
	i++;
	if(i==1000)
	{
		i=0;
		P0=smgduan[n++];
		if(n==16)n=0;	
	}	
}

注:这里数码管用的是138译码器。


这就是我的一些理解,我现在也在学习中,想着花点时间总结会有更好的脉络。之后会再来完善。有错欢迎指出。





评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没想好叫什么名字

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值