哈喽哈喽大家好,我是阿飞的小蝴蝶,大家可以叫我阿飞或者小飞,我又回来啦
恰逢正月初一,在这里先祝大家新年快乐呀! 嘻~
前一段时间呢也是回了老家,结果走的时候忘了带上电脑的充电器,让电脑变成了摆设,也正是因为如此,这几天一直没有更新,昨天我爸爸才把充电器给我带回来,今天终于又跟大家见面了,大家有没有想我呢?
呃。。话不多说啦,今天我们开始讲解定时/计数器的基础知识
从名称上就可以看出来,这部分内容有两个主要功能模式:定时模式和计数模式
工作在定时模式时,每经过一个机械周期,内部的16位计数寄存器数值就会加1,寄存器装满时就会溢出,对应的标志位就会被置高,计时会从我们存放的初值开始计时,一直到16位的寄存器存满时产生溢出,因此在最大计时范围内,我们只要计算出需要存放的初值,就可以准确的产生任意时间的定时。
工作在计数模式时,对应的计数器输入引脚每产生一个脉冲,计数寄存器就会加1。
使用定时/计数器步骤主要分为以下几步:
1、启动定时/计数器(通过TCON寄存器控制);
2、设置定时/计数器工作模式(通过TMOD寄存器控制);
3、查询定时/计数器是否溢出(查看TCON中TF标志位);
这节课我们主要讲解一下定时器的基础知识:
我们先来看TCON寄存器高四位的使用:以定时器0为例,图中可以看到,TR0是定时器0的运行控制位,在TOMD.3 = 0的情况下(这也是我们常用的):当TR0 = 1时,定时器0会开始计时,TR0 = 0时,定时器0会禁止计时,因此,在启用定时器时,需要将TR0置高,“TR0 = 1”。TF0是定时器0的溢出标志,图中也可以看到,定时器0计满时,此标志位会被置高,在打开了定时器0中断的情况下,标志位会在响应中断后被硬件清0,也可以在程序的循环扫描中通过程序查询标志位并清0。(定时器1也是类似的)
再来看一下TMOD寄存器的使用:
从图中可以看到,TMOD寄存器是不可位寻址的,因此在使用时只能对此寄存器进行整体赋值(例:TMOD = 0x01;)图中的高四位(4~7)是定时器1的控制位,低四位(0~3)是定时器0的控制位,以下介绍仍然以定时器0为例(定时器1也是类似的):
GATE(TMOD.3)控制定时器0的打开方式(前边讲过的,这里我们一般配置为0,不做太多讲解)
C/T(TMOD.2)用来控制定时/计数器是工作在定时模式还是计数模式(当TMOD.2 = 1时工作在计数模式,TMOD.2 = 0时工作在定时模式)
M1(TMOD.1)与M0(TMOD.0)共同控制定时器0的定时模式,共有4中工作模式(0、1、2、3),这里我们常用的是模式1(M1 = 0、M0 = 1时),此时定时器的高8位与低8位全用,溢出值为65535,因此,当使用定时器0时,TMOD寄存器应配置为:“TMOD = 0x01; ”。(其它三个模式如果用到了会再讲,大家也可以去问一下百度)
现在就只剩下了最后一步:存放定时器初值,因为51单片机定时器默认为12分频的,因此,单片机的机械频率 = 晶振频率 / 12,机械周期 = 12 / 晶振频率,外接的晶振是11.0592MHz的频率,因此可计算出机械周期 = 12 / 11.0592 = 1.085(us)。也就是说,每隔1.085us,计数值会加1,我们只要用目标时间除以1.085us就是需要计数多少次,再用溢出值(65535)减去计数次数就得到了我们的需要存放的初值,我举个栗子:假设计时5ms, 5ms / 1.085us = 4608 ;65535 - 4608 = 60927;这个60927就是我们需要存放的初值,把高8位存放到TH0中,低8位存放到TL0中就可以啦,因此:TH0 = 0xed; TL0 = 0x14; 每次计时结束后进行下次计时时,初值会被清零,因此我们需要在查询TF标志位时重装初值。
好的,现在用程序来实现以下以上讲到的内容(led间隔500ms闪烁,假设LED直接由P1端口控制):
//这里我们使用定时器0 #include “reg52.h” #define u8 unsigned char void main() { u8 time; TR0 = 1; //打开定时器0 TMOD = 0x01; //配置定时器0工作模式 TH0 = 0xed; TL0 = 0x14; //存放定时器0初值 while(1) { if(TF0 == 1) //查询溢出标志位(计时是否完成) { TF0 = 0; //清零溢出标志位 TH0 = 0xed; TL0 = 0x14; //重发初值 time++; if(time == 100) //计时满500ms { time = 0; P1 = ~P1; //取反P1端口 } } } }
接下来我们把定时器与中断配合起来使用(依然以定时器0为例):
结合这两张图与上次讲的内容我们知道,打开定时器0中断时需要配置:“ EA = 1; ET0 = 1; ” (这里不明白的同学可以返回仔细看一下第五节内容)
配置好以后,每次定时器计满溢出时,程序会响应对应的中断,前边讲的,当打开了定时器对应的中断时,每次定时满响应了溢出中断以后,溢出标志位TF会被硬件清零,因此,我们就不需要在程序中编写 “扫描TF状态” 与 “清零TF标志” 的操作。
编写中断服务函数时需要在函数名后边加上中断编号:interrupt 1(定时器0),别忘了在中断服务函数中重放初值哦
在这里要强调一点:在中断服务函数里边不要放延时函数和死循环,也很好理解的,假如说我定时2ms,定时时间到了以后会执行中断服务函数,同时定时器会重新计时,当这里有延时函数与死循环时很容易造成这次中断服务函数还没有执行完,下一次又该开始了,程序很容易会跑飞的,因此,加了松手检测与按键消抖的按键扫描函数一定不能放里边
好的,接下来我们用程序来实现一下(与上一程序功能相同的):
#include "reg52.h" #define u8 unsigned char u8 time; void time0_init() { EA = 1; ET0 = 1; TH0 = 0xed; TL0 = 0x14; TMOD = 0x01; TR0 = 1; } void main() { time0_init(); while(1); } void time0() interrupt 1 { TH0 = 0xed; TL0 = 0x14; time++; if(time == 100) { time = 0; P1 = ~P1; } }
最后留给大家两个小练习吧:
1、使用定时器控制数码管任意一位显示数值0~9(每隔1秒数值加1)
2、把我们讲机械按键时写的那个用延时函数实现的秒表程序改为定时器计时
今天的内容就先到这里啦,码字不易,希望大家多多支持哦~