使用单片机时,编程会高频率用到延时,如led灯闪烁,蜂鸣器长短鸣,秒表应用等等。首先考虑软件延时,但这个时间不精确,占用硬件资源。使用延时函数是,其他函数不能运行。这个方案cut掉。硬件延时,嗯,误差非常小。但成本较高,且参数调节不便。这个也不行。选择采用定时器调节时间,不占用cpu时间,能与CPU并行工作,实现精确的定时和计数。又可以通过编程设置其他工作方式和其他参数,因此使用非常方便。下面介绍定时器的使用。
概述
定时器系统是单片机内部一个独立的硬件部分,它与cpu和晶振通过内部某些控制线连接并相互作用,cpu一旦设置开启定时功能后,定时器便在晶振的作用下自动计时,但定时器的计数器计满后,会产生中断。计数时间一次为12/晶振频率。在晶振频率为11.0595mhz时,计数一次时间约等于1.09us。
89c52单片机定时器系统有三个定时器/计数器,分别是定时器T0,定时器T1,T2定时器。他们既有定时器功能,也有计数器功能。T0,T1有四种工作方式,T2有三种工作模式。
内部结构
定时器结构
由上图可知,定时器系统有两个寄存器组成,分别是TCON,TMOD。还可看出tcon控制外部中断,tmod控制定时器工作方式。tmod寄存器分为两部分,高四位为t1定时器控制位,低四位为t0定时器控制位。t0定时器与th0,tl0两个8位计数器有关,。t1定时器与th1,tl1两个8位计数器有关。上图信息就这么多,接下来看看两个寄存器相关数据。
寄存器TCON
寄存器TCON
TF1:定时器 1 溢出标志。当定时器/计数器 1 溢出时,由 硬件置位;当主机响应中断,
转向中断服务程序时,由硬件清零。
TR1:定时器 1 运行控制位, 由软件置位/ 复位来开启或关闭定时器/计数器 1。
TF0:定时器 0 溢出标志。当定时器/计数器 0 溢出时,由 硬件置位;当主机响应中断,
转向中断服务程序时,由硬件清零。
TR0:定时器 0 运行控制位,由 软件置位/ 复位来开启或关闭定时器/计数器 0。
IE1:外部中断 1 跳变中断请求标志,当检测到 INT1 发生 1 到 0 的跳变时,由硬件置位;当主机响应中断, 转向中断服务程序时,由硬件清零。
IT1:外部中断 1 触发方式控制位,由 软件置位或清零来选择外部中断 1 的跳变/电平触发中断请求。IT1=0 时,外部中断 1 为电平触发方式,当 INT1 输入低电平时,置位 IE1。
采用电平触发方式时,外部中断源必须保持低电平有效,直到该中断被 CPU 响应,同时在该中断服务程序执行完之前,外部中断源必须被清除,否则将产生另一次中断。IT1=1 时,外部中断 1 为边沿触发方式,在对 INT1 的相邻两次采样中,如果一个周期中为高电平,接下来的周期为低电平,则置位 IE1,表示外部中断 1 正在向 CPU 申请中断。直到该中断被CPU 响应时,才被 硬件清零。
IE0:外部中断 0 跳变中断请求标志,当检测到 INT1 发生 1 到 0 的跳变时,由硬件置位;当主机响应中断, 转向中断服务程序时,由硬件清零。
IT0:外部中断 0 触发方式控制位,应用同 IT1。
这个寄存器与中断有关,支持位寻址,就是可以对其每一位进行单独操作。定时器工作就是在一个特定的间隔(与晶振有关)加1,等到加到定时器溢满时,会触发外部中断。这两个定时器都是16位可编程定时器/计数器。最大可装2的16次方。,就是65535.,定时器在晶振为11.0592MHZ时,间隔约等于1.09us。
(关于中断的知识在上一篇文章有详细介绍,在这里就不累赘。)
寄存器TMOD
寄存器TMOD
GATE:门控制位,当 GATEx=1 时,控制寄存器 TCON 的 TRx=1(x=0 或 1)。当 GATEx=0 时,定时器启动与停止仅受寄存器中的TRx来控制(x=0 或 1)。
C / :定时器、计数器方式选择位,该位为 1 时为计数器,为 0 时为定时器。
M0:定时器/计数器工作模式选择位。
M1:定时器/计数器工作模式选择位。
注:高四位是T1定时器控制位,低四位是T0定时器控制位
工作方式如下图所示
4种工作方式
这个寄存器是控制定时器的工作方式与哪个定时器工作。tmod寄存器支持位寻址,编程时只可以是总线式,不可以单个控制。因为两个定时器命名一样,单个控制会弄混。下面示范写法:
TMOD=0X01; // 0000 0001
可以看出只有最低位为1,即T0定时器的M0=1;对照上图数据可知,这是使用T0定时器的定时功能中的工作方式1,就是16位定时器。
TMOD=0X20; //0010 0000
这个看数据手册得知是T1定时器的定时功能中的工作方式2,即具有自动重载的8位定时器。
附:定时器使用需要用到中断,这里将中断的中断源优先级放在下面。
中断源优先级
定时器使用
中断函数
在介绍定时器使用时,先介绍中断函数,C51的中断格式如下
void 函数名()iinterrupt 工作组
{
中断内容;
}
中断函数不能返回值,所以前缀为void,函数名可以任意取,但一般建议使用有意义的名字,到时候检查也可以明白是什么函数,interrupt是c语言中的一个关键字--中断,记住就行。工作组就是对应中断源,比如说,使用T1定时器,那中断源就是定时器1中断,这时工作组就是3。下面示范:
void timer_t1() interrupt 3
{
TH1=(0XE0);
TL1=(0X07);
}
上面这个实例很容易理解,对着手册看就知道是T1定时器中断。
定时器初值计算
中断函数明白后,如何定时还是不清楚。开启定时器后,定时器就会开始计数,每次加1的间隔是固定的,而且到达最大值就会溢出,触发中断。这样子的话我们可以设定一个初值,初值到最大值的时间假设为50ms,那样的话定是的效果就达到了。定时器加1时间间隔约等于1.09us,定时器在没有赋值时默认初值为0,最大值为65535,计算可得655351.09us约等于72ms,没有赋初值一次定时最大为72ms。可以设置一个初值,就拿50ms来说,501000/1.09约等于45872,也就是说经过45872次计数时间为50ms,那初值就是65535-45872=19663。
大概了解定时器,来看看如何使用,定时器是由16位可编程寄存器组成,分为高8位,即THX(X=1或X=0)低8位TLX(X=1或X=0)。为了更好定时,肯定会选择赋初值。这里介绍一种简单的方法,不用计算。既然它们分为两部分,可以利用这一特点。举个例子:
选择10ms时间,T1定时器。这里以晶振为12MHZ为准,因为11.0592MHZ计算麻烦,这样计数一次就是1us;
TH1=(65535-10000)/256;//表示初值为55535,/256表示高8位的初值,很好理解,低 8位最多存2的8次方=256个数,每满一次高8位加1, /256表示高8位加了多少次。
TL1=(65535-10000)%256;// %256表示不满256最后留下的数。
使用步骤
计算知道后,来看看定时器使用步骤:
- 对TMOD赋值,确定T0和T1的工作方式
- 计算初值,赋值TH0,TL0或TH1,TH1
- 对IE赋值,启动中断
- TR0或TR1置位,启动定时器
- 处理中断函数,定时器中断后变成默认值0,要重新赋初值
例程
#inlclude<reg52.h> //头文件
sbit led=P1^1; //位定义
unsigned char count; //定义计数次数变量
mian() //主函数
{
TMOD=0X01; //设置定时器T0 定时器功能 工作模式1
TH0=(65536-50000)/256; //赋初值 50ms
TL0=(65536-50000)%256;
EA=1; //打开总中断
ET0=1; //开定时器0中断
TR0=1; //启动定时器
}
void timer_t0() intterrupt 1
{
TH0=(65536-50000)/256; //重新赋值
TL0=(65536-50000)%256;
count++; //每中断一次加1
if(count==20) //count==20时,说明1秒到
{
count=0; //count清零,重新等待1秒的到来
led=~led; //led状态取反
}
}
上面注释已经很清楚,按照步骤来,一步一步设置参数,基本不会出错。在中断函数中设置一个标志位,中断变化,变化成何值时,再状态变化。基本就是这个套路。给个建议,中断函数不要写太多东西,不然会出错。试想一下,假如进入中断需5ms,但在中断函数中命令要运行10ms,命令没有运行完,又进入中断,就会出错。
总结
定时器就这样子,不会很难,一些命令在数据手册都有,忘记了就重新看一下,写多了就会记住·,重要的是记住步骤,记住编程思想,在写之前在脑中想一下步骤,或在纸上把思路画一下,那里不通就会跃然于纸上,再稍加思索一般就行了。