对于单片机初学者来说,为了达到延时控制时间等目的,常常让单片机计算for循环函数,随着学习的深入,不可避免的,我们开始逐渐接触到了定时器/计数器中断来控制时间,这里针对定时器/计数器中断,专门作出如下讨论:
-
定时器/计数器是什么?
众所周知,一块单片机的基本由 *处理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中 断系统、定时器/计数器 构成,
定时器/计数器在单片机中的脚管位置,如图
我们可以看到,在P3系列的脚管中,P3.4,P3.5脚管上标注着T0,T1;它们分别代表着单片机内部的定时/计数器0,1,也就是英文 Timer 0,Timer 1,从图上看,一块80C51单片机上有着两个Timer,Timer既有计时的功能,又有计数的功能,通过设置与他们相关的特殊功能寄存器可以选择启用定时功能或者记数功能,关于功能的实现将在第三点讲,现在我们主要研究 Timer是什么 。
下面是Timer的结构框图:
我们可以看到,Timer是一个十六位的加一计数器,TCON(Timer control)指的是Timer的控制寄存器,TMOD(Timer Mode)则是timer的工作方式寄存器;
-
为什么要去实现Timer的中断
原因很简单,1.提高代码的工作效率,由于Timer是单片机中的一个独立的单元,不会去占用CPU的运行速 度,单独运行,自然提高效率
2.精确的控制时间,下面就以keil4中的for循环的delay( )函数和中断函数做对比,来控制蜂鸣器每500ms响一次,通过debug中的运行时间计算,来看看中断时如何精确的控制时间的;
for的代码
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit beep=P2^3;
void delay(uint);
int main(void)
{
while(1)'
{
beep=1;
delay(500);
beep=0;
delay(500);
}
return 0;
}
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
从图中看,delay(500)函数执行用了485ms
再看Timer中断,还是调试蜂鸣器
代码如下:
#define uint unsigned int
#define uchar unsigned char
#include<reg52.h>
sbit beep=P2^3;
uchar num;
int main(void)
{
TMOD=0x01;
TH0=(65535-45872)/256;
TL0=(65535-45872)%256;
EA=1;
ET0=1;
TR0=1;
while(1);
return 0;
}
void T0_time()interrupt 1
{
TH0=(65536-45872)/256;
TL0=(65535-45872)%256;
num++;
if(num==20)
{
num =0;
beep=~beep;
}
}
什么?关于它的运行时间是多少? …… 我也不会用keil4来做,本人小菜鸡哈,求教大神帮忙试一下。没关系我会算:
机器周期=12*时钟周期(我的晶振的频率是11.0592MHZ),我们想让计数器记的数N=t/机器周期,
t是自己设定的,比如我们需要设定的是50ms,那么N=50 000/1.09 =45872,OK,误差在微秒级上,你说哪个精确?
- 怎么实现Timer中断?
我们还是要从头说起,第一点,要说一下51单片机的中断级别,废话不说,直接上图
这张图直接以由高到低的顺序解释了52单片机的中断级别(52与51类似,除了没有T2)C语言用的序号是什么意?
很简单,看上面的蜂鸣器中断的代码:下面是不是有一个interrupt 1 ? 这个1就代表该序号级别的中断,与图中对应,可以看出,那个函数表示的是Timer0的中断。
还是那张图,先来说说TCON(Timer Control),TCON可以被寻址,也就是说没必要用一个类似于0xff之类的十六进制的数字来控制每个位,再说每个位的内容
TF1(Timer1 Filled):也就是Timer1 数据溢出了,此时它会向CPU提出中断请求,是单片机自动的,没必要控制1!
TR1(Timer1 Run): 字面意思,就像那句Run Forrest ! 需要你去声明,例如TR1=1,就是 RUN! TIMER1! 那个Timer就工作了。。
TF0,TR0,那个同上,只不过是角标不一样而已;
在说说TMOD(Timer Mode),Timer Mode没法被寻址,所以必须用一个十六进制数如0xff来控制每一位,进而完成功能实现
1,GATE(基本上没特殊要求的话,GATE这个位直接取0)
GATE=0:Timer启动与停止仅仅受TCON寄存器中的TRX(Timer Run 0或者1 , X是角标),控制 ; GATE=1:由TRX和外部中断引脚 INT0或INT1上的电平控制
2 , C/T(cacluate or time ),定时器模式和计数器模式的选择位,作为Timer中断来说,这位通常取0;
C/T=1:计数器模式
C/T=0:定时器模式
3, M1M2,工作方式选择位,由这两个位共同决定Timer的工作方式,基本上我们都是用 0 1,废话不说,直接上图
开始分析刚刚上的那个蜂鸣器的中断代码:
int main(void)
{
TMOD=0x01; //0000_0001,相当于只打开了Timer0,并且把它的工作状态设为16位的Timer
TH0=(65535-45872)/256;//45872上次的运算结果,就是那个控制50ms的那个数据,除法,将数据
TL0=(65535-45872)%256;//储存进高八位,取余将数据储存进第八位
EA=1;//打开总中断,首要步骤
ET0=1;//打开定时器0中断
TR0=1;//启动定时器0
while(1);
return 0;
}
void T0_time()interrupt 1
{
TH0=(65536-45872)/256;
TL0=(65535-45872)%256;
num++;//不断地累加,到num=20时,重置,20×50ms = 1s
if(num==20)
{
num =0;
beep=~beep;
}
}
总的来说,就是
1,设定TMOD → 2,装入初值(THx,TLx, 45872)→3.打开总中断 EA=1 →4.开启定时器中断→5.启动定时器(TRx)→设定中断函数 (void Tx_time( ) interrupt y, x表示定时器编号,y表示中断序列)