RYMCU

51单片机第6章--定时器实验

Hugh 5 年前
# 定时器 # 51单片机

所属作品集

概述

在程序设计中我们经常会用到延时,对于精度要求不高的应用来说我们一般采用插入语句等待的方式来实现,对于精度高的应用来说我们一般采用定时器中断来实现。本章主要内容包括:

  1. 延时时间的调试
  2. 定时器功能介绍
  3. 定时器应用实例介绍

6.1 延时调试

前面程序中用到的延时通过执行多条语句来实现的,那么我们会问执行一条语句需要多长时间,延时 1ms 需要又多少条语句,延时的精度高吗?下面讲解的内容将包含以上内容。众所周知,晶振为单片机提供了时间基准,在晶振的节拍下程序按时间顺序往下执行,在单片机应用中有三个重要的时间概念,分别为时钟周期、机器周期和指令周期。

时钟周期: 单片机晶振频率的倒数,例如开发板的板载晶振为 11.0592MHz,时钟周期为 1/11.0592us,是 51 单片机的最基本,最小的时间单位。

机器周期: 单片机完成一条最基本指令操作所需要的时间,51 单片机的一个机器周期等于 12 个时钟周期,RY-51 开发板机器周期:12/11.0592us=1.09us。

指令周期: 执行一条指令所需的时间,一般由若干个机器周期组成。当然根据指令的不同所需的机器周期也不同,只需一个机器周期的简单指令,称之为单周期指令,包含两个机器周期的指令称之为双周期指令。
以 RY-51 单片机开发板为例,执行一条最简单的语句需要至少 1.09us,复杂的语句需要多个 1.09us 来实现。因此,我们常常在程序中通过添加数目不等的简单语句来实现延时,但是延时的精度往往不会太高,我们可以通过 keil 软件的仿真功能来确定延时的大概数值。接下来讲解延时函数仿真功能,我们以延时 10ms 为例进行介绍。

#include<reg52.h>

sbit led0 = P1^0;//LED小灯管脚定义
sbit FM = P2^4;//蜂鸣器管脚位定义
sbit Key17 = P3^0;//独立按键管脚定义

void delayms(unsigned int z)//延时函数
{
   unsigned int x,y;
   for(x=z;x>0;x--)
   	for(y=70;y>0;y--);
}

void main()
{
   while(1)
   {
   	delayms(10);//延时10ms
   	delayms(10);//延时10ms
   	delayms(10);//延时10ms
   }
}

首先,根据前面介绍的步骤建立一个工程,并编辑好如上所示的代码。这个代码和前面讲解的代码有点不同,我们先把延时的语句写成一个函数 delayms(),将其放在主程序之外。在主程序中调用函数来实现延时。改变延时函数的输入参数便可改变延时语句的条数,因此延时变得非常的灵活。主程序中我们对延时函数进行了三次调用,接下来我们看看执行一次函数调用需要多长时间。

在 keil 软件快捷按钮菜单栏中找到配置,点击进入如下图所示的子菜单:
图6-2-仿真晶振频率设置.png
如上图所示,在”Target”目录先将仿真晶振设置为“11.0592”。
图6-3-优化等级设置.png
如图所示,在“C51”目录下将代码优化等级设置为“0:Constant folding”。
设置好如上规则后,点击菜单栏的 debug 快捷键如下图所示,进入调试模式,可以看到程序进程已经执行到了第 17 条代码。
图6-3-进入调试模式.png
接下来给 17,18,19 三条语句设置断点,将鼠标移到语句处点击右键选择“insert/Remove Breakpoint”,设置好之后语句左边会出现红色的小方块。当程序执行到断点时,程序便会停止下来。
图6-4-断点设置.png
如图 6-5 所示,当程序顺序执行的黄色箭头停留在第 17 行时,所使用的时间为:0.00042209s,如图左侧所示。
图6-5-执行语句准备.png
点击如图 6-6 左上角的程序执行快捷键,程序进程黄色箭头停留在第 18 行,即执行完了一条延时函数语句 delayms(10),观察左下角的进程时间为:0.01057943s 将执行语句前后时间相减,结果约等于 10.1ms,因此与我们延迟 10ms 的要求相符。
图6-6-执行延时语句.png
通过调用函数实现延时的方法在单片机编程中是非常常见的,经过上面的延时调试可见这种方法的精度并不是很高,对应特殊应用场合我们一般采用定时器的方式实现。

6.2 定时器介绍

定时器的功能很容易理解,就是到了某个指定的时间会提示设定者,我们平常使用的闹钟实际上可看作是一个定时器。定时器是单片机的重要资源,那么我们什么时候会使用到单片机的定时器功能呢?正常情况下我们一直在运行单片机的主程序,在主程序中假设我们需要 1s 之后去执行某个操作,这时我们只好在主程序中进行延时,直到 1s 时间到了再去执行相应的操作,那么在这个延时的过程中主程序别的事情干不了了,这样就很浪费系统的资源。

如果我们这个时候使用定时器功能就可以很好的解决这个问题。首先,我们在主程序中设定定时器定时 1s,并启动定时器开始计时,因为定时器的运行和单片机的主程序执行是分开的,不会相互影响,因此主程序继续往下执行。当 1s 的时间到了,定时器告诉主程序,这个时候主程序停下当前正在干的活而去响应定时器。

STC89C52 系列单片机内置 2 个 16 位的定时器,同时也可以当作计数器来用,分别为定时器 0(T0),定时器 1(T1),每个定时器有 4 种工作方式。那么定时器是如何计时的呢?我们以定时器 T0 工作在 16 位模式下进行介绍。16 位的二进制的最大值为:0b1111,1111,1111,1111=65535。首先,我们需要给这个 16 位的寄存器赋初始值,假设为 1000,当我们在主程序中启动定时器 T0,此时这个寄存器会每 12 个时钟周期从 1000 开始往上加 1,直到加到 65535,当再加 1 后,定时器 T0 就溢出了,寄存器值从 65535 变成 0,当溢出后定时器会告诉主程序,定时时间到了。所以我们只要改变这个初始值 1000 就可以得到不同的定时时间了。细心的同学可能会发现,当初始值为 0 的时候可以定时的时间最长。那么我们来看看定时器 T0 工作在 16 位模式下最长能定时多久。我们开发采用的时钟频率为 11.0592MHz,因此,每 12 个时钟周期时间为:12/11.0592us=1.09us。启动定时器后,从初始值 0 累加到 65535 在到溢出为 0,总共累加了 65536 次。因此,最大定时长度为:1.09us*65536=71.4ms。

6.3 定时器使用

前面介绍了 51 系列单片机中定时器的工作原理,这节将重介绍定时器的使用。前面介绍过单片机的某个功能的实现都有特殊功能寄存器 SFR 有关,当然定时器的使用也不例外,特殊功能寄存器列表如下表所示:
表 6-1 定时器/计数器特殊功能寄存器列表
表6-1-定时器-计数器特殊功能寄存器列表.png
TCON 是一个 8 位定时器/计数器中断控制寄存器,可位寻址,即每一位可单独赋值。B7、B6 为定时器 T1 控制位,B5、B4 为定时器 T0 控制位,如下表所示:
表 6-2 TCON 寄存器
表6-2-TCON寄存器.png
TF0:定时器/计数器 0 中断溢出标志位。T1 被允许计数后,从初始值开始加 1 计数。当最高位产生溢出时由硬件置“1”TF0,向 CPU 请求中断,一直保持到 CPU 响应中断时,才由硬件清零“0”TF0,另外,TF0 也可由程序查询清零“0”。
TF1:定时器/计数器 1 中断溢出标志位。功能与 TF0 类似。
TR0:定时器 T0 的运行控制位。该位由软件置位和清零。当 TR0=1 时就允许 T0 开始计数。
TR1:定时器 T1 的运行控制位。该位由软件置位和清零。当 TR1=1 时就允许 T1 开始计数。
其它位为外部中断相关内容,与定时器功能无关,这里暂时不做介绍。
前面讲的这几个位是定时器中断控制位,TR0 赋值为 1 后,定时器 T0 开始运行,当定时器 T0 溢出时,单片机硬件会将 TF0 位置 1。我们可以在程序中通过查询这个位是否为 1 来确定定时是否到达。另外,我们如果设置了定时器中断函数,当定时到达后,单片机程序会跳转到定时器中断函数,并且由硬件将 TF0 清零。TCON 是一个可位寻址的寄存器,在单片机程序中可以直接对 TCON 赋值,或者对其中的位进行直接赋值。
图6-7-定时器模式寄存器.png
图 6-7 定时器模式寄存器
TMOD 为定时器模式寄存器,寄存器的高 4 位为定时器 1 模式位,低四位为定时器 0 模式位,高低位的功能类似,下面以定时器 0 为例:
C/T:定时器、计数器功能选择位。清零作为定时器,置 1 作为计数器。
表 6-3 模式选择
表6-3-模式选择.png
定时器/计数器功能原理图如下图所示:
图6-8-定时器-计数器功能原理图.png

图 6-8 定时器/计数器功能原理图
如上图所示,当作为定时器或计数器时唯一的区别为输入的时钟不一样。当作为定时器时,输入的时钟为系统时钟,而当作定时器时,系统输入时钟为外部引脚。

6.4 定时器应用实例

本节我们介绍定时器的两种应用实例进行介绍,第一种为程序查询方式响应定时器的溢出,第二种为中断函数处理的方式响应定时器溢出。应用定时器实现的功能为每隔 50ms 使 led0 小灯闪烁一次,并使用定时器 T0,工作在模式 1,即 16 位定时的模式下。

6.4.1 程序查询方式实例

定时器使用步骤:
1) 配置定时器模式控制寄存器 TMOD;
2) 装载定时器初始值 TH0,T0;
3) 置位 TR0,启动定时器开始计时;
4) 主程序查询定时器中断标志位 TF0。

假设设置定时器 0 为每 1ms 溢出 1 次,因此在主程序中累计查询到 50 次便使 led 小灯闪烁一次。根据前面介绍的方法计算出定时器 0 初始值 65536-FOSC/12 * 1 * 10-3,程序设计如下图所示:

#include<reg52.h>

#define FOSC 11059200 //单片机晶振频率
#define T_1ms (65536 - FOSC/12/1000)  //定时器初始值计算

sbit led0 = P1^0;
unsigned char count = 0;
void main()
{
	TMOD = 0x01;	 //定时器工作模式配置
	TL0  = T_1ms;	//装载初始值
	TH0  = T_1ms>>8;

	TR0  = 1;		 //启动定时器
	while(1)
	{
		if(TF0==1)
		{
			TF0 = 0; //软件清零

			TL0 = T_1ms;//重装初始值
			TH0 = T_1ms>>8;	

			count++;
			if(count>=50)//	每一毫秒进入一次中断,达到50次则为50ms,翻转小灯。
			{
				count = 0;
				led0 = ~led0;
			}								
		}
	}
}  

如上图所示,在主程序开始阶段,对 TMOD 进行赋值来配置定时器 T0 为工作模式 1,然后对 TL0,TH0 寄存器进行初始化赋值,紧接着启动定时器 T0 开始计数。完成上述步骤后,进入主程序循环,在循环中不断的检测 TF0,当检测到定时器溢出后将 TF0 清零,重新转载定时器初始值,当溢出达到 50 次后翻转 led 小灯的值。这里需要注意的地方是,当判断到溢出后需要通过软件对 TF0 进行软件清零。结合我们前面学习的知识,大家可以根据自己的需求来改变程序的功能,加深对定时器功能的理解。

6.4.2 中断响应方式实例

中断响应方式与程序查询方式略有不同,在程序初始化处需要打开定时器中断,当定时器溢出后程序跳转到中断入口程序,并由硬件自动清理 TF0,可在中断程序中实现 led 小灯闪烁的功能。
定时器使用步骤:
1) 配置定时器模式控制寄存器 TMOD;
2) 装载定时器初始值 TH0,T0;
3) 置位 TR0,启动定时器开始计时;
4) 允许定时器中断,并开启总中断;
5) 进入中断程序。
6) 在中断程序中重载定时器初始值,并闪烁 led 小灯。
程序设计如下图所示:

#include<reg52.h>

#define FOSC 11059200 //单片机晶振频率
#define T_1ms (65536 - FOSC/12/1000)  //定时器初始值计算

sbit led1 = P1^1;
unsigned char count = 0;
void main()
{
	TMOD = 0x01;	 //定时器工作模式配置
	TL0  = T_1ms;	//装载初始值
	TH0  = T_1ms>>8;

	TR0  = 1;		 //启动定时器
	ET0  = 1;		 //允许定时器中断
	EA   = 1;		 //开总中断

	while(1);	  //循环
}

void timer0() interrupt 1
{
	TL0 = T_1ms;//重装初始值
	TH0 = T_1ms>>8;

	count++;
	if(count>=50)//	每一毫秒进入一次中断,达到50次则为50ms,翻转小灯。
	{
		count = 0;
		led1 = ~led1;
	}
}

6.4 本章小结

本章介绍了延时函数的调试,定时器基础知识的介绍以及定时器功能的应用实例。结合我们我们程序的介绍,多多练习下载试验逐步的熟练掌握延时函数、定时器功能的应用。

所属作品集

后发布评论