任务35 定时器的初步认识.docx
- 文档编号:13750646
- 上传时间:2023-06-17
- 格式:DOCX
- 页数:16
- 大小:108.13KB
任务35 定时器的初步认识.docx
《任务35 定时器的初步认识.docx》由会员分享,可在线阅读,更多相关《任务35 定时器的初步认识.docx(16页珍藏版)》请在冰点文库上搜索。
任务35定时器的初步认识
任务五定时器的初步认识
3.5.1任务介绍
在动态数码管的学习中,我们已经用DelayMs()延时函数实现了数字钟的走时,但是这种方式下的数字钟走时不够准确。
生活中,我们需要精确计量时间时,通常会借助于走时准确的秒表,51单片机内部也有一个类似于秒表的装置,我们称之为定时器,借助于定时器,我们可以实现走时准确的数字钟。
本节的任务是:
利用单片机定时器完成走时准确的数字钟,另外在程序中,数码管扫描也由定时器来驱动。
3.5.2知识准备
1、定时器的引入
在讲述定时器的原理之前,我们先看一下图3.5.1中的水龙头向水盆滴水的画面。
在画面中,水龙头由于没有关紧,水一滴一滴地滴向脸盆,盆的容量是有限的,水会在某一个时刻从水盆中溢出。
假设一开始水盆没有水,65536个水滴恰好可以把水盆装满,恰好是计数了65536次。
如果我们计数1000次怎么办呢?
向水盆中预先装下了64536滴水,然后打开水龙头,开始滴水,等到水盆中的水溢出了,自然是计数了100次。
如果水滴的速度是恒定的,1滴/1秒,计数就变成了计时了。
实际古人计时装置“刻漏”的原理和水盆滴水的原理相似。
图3.5.1水盆滴水
51单片机的定时器/计数器的原理与上面讲述的水盆滴水的例子类似,如表3.5.1所示。
3.5.1定时/计数器和水盆滴水的类比
类比
水盆滴水
定时/计数器
类比1
水滴
脉冲
类比2
水盆
两个8位的RAM
类比3
预先装入水滴
设定RAM初值
类比4
打开水龙头
启动定时器
类比5
水滴向水盆滴水
每来一个脉冲,RAM加1
类比6
水盆的水溢出
RAM数值溢出
2、定时器的内部结构及工作原理
在51单片机内部有2个定时器,分别称为定时/计数器0、定时/计数器1,每个定时/计数器具有计数和定时两大功能,有4种工作方式。
定时/计数器0和定时/计数器1配置上完全相同,现用定时/计数器1的工作方式1来说明定时器内部结构与工作原理。
图3.5.2为定时/计数器1在工作方式1下的内部结构图。
(1)定时/计数器1的脉冲源有两种:
当定时/计数器1工作于定时方式时,加1脉冲由振荡器的12分频提供(12M晶振的12分频为1MHz)。
当定时/计数器1工作于计数方式时,加1脉冲由外部脉冲源提供,P3.4是定时/计数器0的外部脉冲源输入端,P3.5是定时/计数器1的外接脉冲输入端。
定时/计数器工作于定时还是计数方式,取决于选择开关C/
,当C/
=0时工作于定时方式,C/
=1时工作于计数方式。
(2)脉冲要经过启动开关才能到达RAM。
启动开关=TR1&(
|
),GATE为门控位,
为外部中断1。
当GATE=0时,
|
的结果为1,则启动开关仅有TR1决定。
当GATA=1,则启动开关的不仅由TR1决定,还要由来自于外部中断1的信号决定是否开启中断。
(3)两个8位的RAM,高八位的RAM称为TH1,低八位的RAM称为TL1,组合在一起共16位。
每来一个脉冲,RAM的值加1,当RAM的数值超过65535时,计数器会溢出,溢出标志位TF1会由0变成1,同时TF1的变化会引发中断事件的发生。
要设定不同的定时时间,在定时/计时器启动之前,向RAM预先装入初值。
图3.5.2定时/计数器1的内部结构(方式1)
3、定时器的方式控制字
51单片机的特殊功能寄存器(SFR)中有两个属于定时器的寄存器,分别为TMOD和TCON。
(1)TMOD寄存器
7
6
5
4
3
2
1
0
GATE
C/T
M1
M0
GATE
C/T
M1
M0
定时/计数器1
定时器/计数器0
表3.5.1TMOD寄存器
从表3.5.1中我们可以看到,TMOD被分成两份,每份四位,高四位用于定时/计数器1的控制,低四位用于定时/计数器0的控制。
结合图3.5.2定时/计数1的内部结构,GATE参与了启动开关的选择,称为门控位。
C/T是用来选择定时还是计数,M1M0是下面要介绍的4种工作方式的选择位。
(2)TCON寄存器
表3.5.2TCON寄存器
7
6
5
4
3
2
1
0
TF1
TR1
TF0
TR0
IE1
IT1
IE0
IT0
用于定时器
用于外部中断
TCON寄存器也被分成了两份,前四位用于定时/计数器,后四位用于外部中断,如表3.5.2所示。
前四位中又被分成了两份,分别属于定时/计数器1和定时/计数器0。
其中TF是定时器溢出标志,没有溢出时为0,溢出后为1。
TR参与了定时器的启动。
需要说明的是,TMOD寄存器中的每一个位不可以单独寻址,举例:
定时/计数器0,定时模式,门控位为0,方式0(M1M0=01),可以写成TMOD=0x05。
TCON寄存器中的每一个位可单独寻址,举例:
启动定时器0,TR0=1就可以了。
4、定时/计数器的四种工作方式
51单片机的定时器有四种工作方式,TMOD寄存器中M1和M0位决定了使用哪种工作方式。
M1M0为的配置和定时器的工作方式的对应关系如表3.5.3所示。
表3.5.3定时器工作方式
M1
M0
工作方式
描述
0
0
工作方式0
13位定时/计数器
0
1
工作方式1
16位定时/计数器
1
0
工作方式2
8位自动重装模式
1
1
工作方式3
2个独立8位定时/计数器
工作方式0和工作方式3很少使用,在这里不做专门的讲解,下面介绍工作在方式1和工作方式2的特点。
(1)工作方式1:
当定时/计数器处于工作方式1时,两个8位的RAM都参与计数,即计数的范围是:
0~65535。
需要注意,工作于方式1时,定时/计数器溢出后,需要手动装入初值。
(2)工作方式2
定时/计数器处于工作方式2时,相比于方式1,其计数范围缩小了,高8位的RAM不参与计数,低8位的RAM参与计数。
其计数范围是0~255。
高8位的RAM闲置不用吗?
实际上不是这样的,首先来分析一下定时/计数器在工作方式1下存在的问题。
开发板的晶振频率为12MHz,12分频后为1MHz,脉冲周期是1us,选择定时模式时,每隔1us,RAM的值加1,16位的定时/计数模式,最大定时范围为65.536ms。
我们要实现数字钟的1秒定时,可以先定时50ms(RAM初值=65536-50000=15536),然后连续定时20次,就可以实现1秒的定时。
在介绍工作方式1时,提到溢出后需要手动装入初值(15536),如果不手动装入初值,则溢出后,RAM从0开始计数。
RAM的初值重装是在中断服务函数中完成的。
定时/计数器溢出后产生中断,到进入中断服务函数后装入初值,是需要耗费时间的。
实际是,定时/计数器溢出后,从0开始计数,运行一段时间初值重装了,又从初值运行。
尽管这段时间很小(几十个us),但连续定时,日积月累,误差就大了,所以工作方式1下,定时的精度不高。
工作方式2下初值的重装不需要手动装入,一旦溢出,硬件自动完成初值的重装,中间没有时间耽搁,方式2比方式1定时要准确。
定时/计数器初始化时,高8位RAM也装入初值,当定时/计数器溢出后,硬件自动把高8位RAM存放的初值导入到低8位RAM,完成初值重装。
5、定时器/计数器的初始化和溢出标志位处理
定时器/计数器初始化设置如下:
(1)TMOD寄存器的配置:
定时/计数的选择,GATE门的设置,工作方式的选择。
(2)装入初值,根据任务的要求,给THn、TLn(n为0或1)装入初值。
(3)开总中断:
EA=1,开定时器中断:
ETn(n为0或1)=1。
(4)如果还有别的中断,根据任务的重要性,需要确定中断优先级,配置IP寄存器。
(5)启动定时器/计数器,TRn(n为0或1)=1。
定时器/计数器溢出可以采用查询和中断两种方式进行处理。
查询方式效率低(主程序中不停的查询溢出标志位TFn(n为0或1)是否置位),本书中采用中断方式。
6、定时/计数应用举例
例程1:
计数的应用
自动化生产线上,12瓶易拉罐饮料为一箱,光电传感器检测到一瓶易拉罐就给送出一个脉冲,控制器连续接收到12个脉冲,则作为一个包装。
本例用按键来模拟光电传感器发出的脉冲,按键接P3.4(定时/计数器0的外接脉冲输入端),用发光二极管的状态取反来模拟一个包装的动作,发光二极管接单片机的P0.0。
连续按下12次按键,发光二极管状态取反一次。
根据任务要求,定时/计数器的配置如下:
使用定时/计数器0,计数、工作方式1。
程序如下:
#include
#defineucharunsignedchar
#defineuintunsignedint
sbitLED=P0^0;//LED接口定义
//计数器0初始化
voidTimer0Init()
{
TMOD=0x05;//GATE=0,C/T=1,M1M0=01;
TH0=(65536-12)/256;//RAM高8位赋值
TL0=(65536-12)%12;//RAM低8位赋值
ET0=EA=1;//开定时器中断和总中断
TR0=1;//启动定时器
}
//主函数
voidmain(void)
{
Timer0Init();//定时/计数器初始化
while
(1)
{
//执行别的任务
}
}
//计数器0中断服务函数
voidTimer0Intr(void)interrupt1
{
TH0=(65536-12)/256;
TL0=(65536-12)%12;//溢出后重新赋初值
LED=!
LED;//中断每产生一次,发光二极管的状态取反一次
}
程序解释:
(1)Timer0Init()函数完成计数器的初始化。
外部中断不参与计数器的开启,门控位:
GATE=1;计数模式:
C/T=1;工作方式1,M1M0=01。
每12个脉冲到来,计数器溢出一次,初值为(65536-12),取出高8位和低8位,赋给TH0和TL0。
最后开中断,启动计数器。
(2)计数器溢出后,CPU响应中断服务函数,在中断服务函数中,重新赋予初值,并完成LED的状态取反。
例程2:
定时的应用
用定时的方式实现LED闪烁,LED闪烁的频率为1Hz。
LED接单片机P0.0引脚,0.5秒亮,0.5秒灭。
根据任务要求,定时/计数器的配置如下:
使用定时/计数器1,计时、工作方式2。
#include
#defineucharunsignedchar
#defineuintunsignedint
sbitLED=P1^0;//LED接口定义
bitFlag500Ms=0;//500ms标志位
//定时器1的初始化
voidTimer1Init()
{
TMOD=0x02;//GATE=0,C/T=0,M1M0=10;
TH1=256-200;//RAM高八位赋值
TL1=256-200;//RAM低八位赋值,200us的定时
ET1=EA=1;//开定时器中断和总中断
TR1=1;//启动定时器
}
//主函数
voidmain(void)
{
Timer1Init();//定时器的初始化
while
(1)
{
if(Flag500Ms==1)//500ms标志位置1
{
Flag500Ms=0;//清空500ms标志位
LED=!
LED;//LED状态取反
}
}
}
//定时器1的中断服务函数
voidtimer0_intr(void)interrupt1
{
staticuintCnt200us=0;//200us计数变量
if(++Cnt200us>=2500)//200us*2500=500ms
{
Cnt200us=0;//清空计数变量
Flag500Ms=1;//500ms标志位置1
}
}
程序解释:
(1)Timer0Init()函数完成定时器1的初始化。
门控位不参与定时:
GATE=0;定时:
C/T=0;工作方式2,自动重装模式,M1M0=10。
所以TMOD=0x20。
200us定时器溢出一次,8位自动重装模式,所以TH1=TL1=(256-200)=56。
(2)由于采用了自动重装模式,在中断服务函数中,不需要重新赋初值。
本任务的定时目标是500ms,程序中定时器的定时长度是200us,所以需要连续定时。
每进入一次中断服务函数,200us计数变量(Cnt200us)加1,Cnt200us计数到2500,整个计时的时间为200us×2500次=500ms。
(3)500ms到来后,程序中并没有像上一个计数例子一样,在中断服务函数中完成LED的闪烁,而是把500ms标志位(Flag500Ms)置1,在主程序中,检测到该标志位置1,则完成LED的闪烁。
(4)计数变量Cnt200us在定义的时候,uint前面多了个关键字static,其作用是声明变量Cnt200us为局部静态变量。
什么是局部静态变量呢?
C语言变量根据定义的位置不同分为全局变量、局部变量,
全局变量是在函数外部定义的变量,又称外部变量。
全局变量一般定义在程序的开始处,可以为多个函数共同使用,其有效的作用范围是从它定义的位置开始整个程序文件结束为止。
局部变量是在一个函数内部内部定义的变量,该变量只能在定义它的那个函数范围内有效,不同的函数可以使用相同的局部变量名。
由于它们的作用范围不同,不会互相干扰,函数的形式参数也属于局部变量。
局部变量根据存储空间分配的不同又分为自动变量和静态变量。
在函数内部定义的变量,如果不做专门说明,则为自动变量。
自动变量的特点是只在定义它们的时候才分配存储空间,在定义他们的函数返回时系统回收变量所占用的存储空间,对这些变量存储空间的分配和回收是系统自动完成的,
静态变量在定义是前面多了个关键字static,局部静态变量仍然保留局部变量的特性,只能在定义的函数内部使用,但与自动变量不同的是,再次调用定义它的函数时,它又可继续使用,而且保存了前次被调用后留下的值。
因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。
弄明白了什么是局部静态变量,再来看中断函数中计数变量Cnt200us的使用就简单多了。
首次调用中断函数时,计数变量Cnt200us定义且赋初值0,紧跟着在函数中Cnt200us的值加1,退出出中断服务函数后,Cnt200us的值不释放,仍然为1。
3.5.3任务实施
本节的任务程序实现如下:
#incldue
#defineucharunsignedchar
#defineuintunsignedint
//段码
ucharcodeSeg7Code[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
ucharcodeSeg7Posit[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//位码
ucharDispBuffer[6];//缓冲区
bitSystemFlag1Ms=1;//1ms标志位
//6位数码管显示子函数,在第二位和第四位上显示小数点
voidSeg7Display()
{
staticuchari=0;
P1=0xff;//消隐
P0=Seg7Code[disp_buff[i]];//送段码
if((i==1)||(i==3))P0&=~0x80;//在第二位和第四位数码管上加小数点
P1=Posit[i];//送位选
if(++i>=6)i=0;
}
//把时间送入缓冲区
voidTimerToBuffer(ucharnhour,nminute,nsecond)
{
DispBuffer[0]=nhour/10;
DispBuffer[1]=nhour%10;//小时的个位和十位的分解
DispBuffer[1]=nhour%10;//小时的个位和十位的分解
DispBuffer[2]=nsecond/10;
DispBuffer[3]=nsecond%10;//分的个位和十位的分解
DispBuffer[4]=nminute/10;
DispBuffer[5]=nminute%10;//秒的个位和十位的分解
}
//定时器0初始化
voidTimer0Init()
{
TMOD=0x02;//GATE=0,C/T=0,M1M0=02;
TH0=56;//高8位RAM赋值
TL0=56;//低8位RAM赋值,200us定时
ET0=EA=1;//开定时器中断和总中断
TR0=1;//开启定时器
}
//主函数
voidmain(void)
{
ucharHour=9;//小时
ucharMinute=23;//分
ucharSecond=37;//秒
uintCnt1Ms=0;//1ms计数器
Timer0Init();//定时器0初始化
while
(1)
{
if(FlagSystem1Ms==1)//定时器产生的1ms的时标信号到
{
SystemFlag1Ms=0;//时标信号清零
TimerToBuffer();//显示数据送往缓冲区
Seg7Display();//6位数码管动态显示
if(++Cnt1Ms>=1000)//1秒到,1ms*1000=1000ms
{
Cnt2Ms=0;//2ms计数变量清零
if(++Second>=60)//秒加1,到60秒,则分加1
{
Second=0;//秒清零
if(++Minute>=60)//分家1,到60分,则小时加1
{
Second=0;//秒清零
{
Minute=0;//分清零
if(++Hour>=24)//小时加1,到24小时
Hour=0;//小时清零
}
}
}
}
}
}
//定时器0中断服务函数,提供1ms的时标信号
voidtimer0_intr(void)interrupt1
{
staticucharCnt200us=0;//200us计数变量
if(++Cnt200us>=5)//0.2ms*5=1ms
{
Cnt200us=0;//清空计数变量
SystemFlag1Ms=1;//1ms标志位置1
}
}
程序解释:
(1)程序中第一次提出了时标信号这个概念,主程序中的任务是在时标信号的驱动下完成的。
本程序的“时标信号”是标志位(Flag1ms),每隔1ms,时标信号被置1,主程序中检测到时标信号置1后,再清零。
1ms的是通过定时器定时来完成,定时器200us溢出一次,连续5次,置位1次时标信号。
(2)主程序中的任务有3个:
缓冲区数据刷新(TimerToBuffer())、6位数码管的扫描(Seg7Display())和数字钟计时。
缓冲区数据刷新和之前的函数一样,不需解释。
数码管扫描的程序中和之前的程序相比,做了一些改动。
在之前的Seg7Display()函数中,数码管的逐位扫描是通过延时1ms来实现的,6位数码管延时需要6ms的延时,在扫描数码管这段时间内,别的任务只能等待。
扫描完数码管后,再执行别的任务,数码管的最后一位点亮的时间并非是1ms,而是1ms+别的任务的执行时间,这将导致数码管最后一位和其余5位分配时间不一致,最后1位的亮度高于其它5位。
本程序中,利用定时器产生的1ms时标信号,每间隔1ms执行一次Seg7Display()函数,
执行一次Seg7Dislplay()只完成一位数码管的扫描,根据静态变量i的值送入段码和位码,变量i加1,下一个1ms的时标信号到来后,送入下一位数码管的段码和位码,依次循环。
(3)时间的计时也是通过1ms的时标信号来完成的。
1ms时标信号每到来一次,计数变量Cnt2Ms加1,计数500次,则为1秒,其余的和以前的程序一样。
总结:
在本节之前,程序中任务的时间分配是通过延时函数来实现的,单个任务中有延时函数看不出问题,但当程序中的任务多了,任务之间会相互堵塞,运行效率较低。
本节的例程给出了LED闪烁、数码管扫描等任务采用定时器时标信号轮询的方法,没有延时语句,任务之间不会堵塞,提高了运行效率。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 任务35 定时器的初步认识 任务 35 定时器 初步 认识