51单片机中有2个 定时器/计数器 中断,之所以叫定时器/计数器,是因为它既可以当定时器,也可以当计数器,其实本质上都是计数器,如果给一个稳定的时钟源,那么它就是一个定时器的。(下面我说的计数器或定时器都是一回事,不要在意为什么有的地方是定时器,有的地方是计数器)
首先,定时器的计数是加1的,而不是减1的,当加到最大值时,再次加1就会变成0,此时就会触发计数器中断。51中常用的是16位计数器,最大值是65535,如果要计数1000次,那么计数器赋值应该为65535-1000+1(达到最大值后要再加一次才行。)计数器的初值是放在两个8位寄存器中,分别是TH0, TL0(计数器0的高8位和低8位),计数器1使用的是TH1,TL1,原理相同。
65535-1000+1=64536=0xFC18
所以
TH0=0xFC;
TL0=0x18;
上面我们让定时器计数1000次,但是我们要的是固定时长,而不是固定次数,一千次是多长时间呢? 我们需要计算计数器加1需要的时间。
计数器加1需要一个机器周期,而一个机器周期等于6个状态周期,12个震荡周期。如果51芯片的频率是12MHz,那么震荡周期就是1/12M秒,而机器周期就是12×1/12M秒,就是1/1M秒,即1/1000000秒,也就是1微秒。
计数1000次就是1毫秒了;
然后就是如何使用计数器中断了
计数器中断定义和外部中断定义相同格式如下:
void timer0() interrupt 1
定义好了就是如何让他正常工作了
TMOD=0x11; //这种计数器0和1的工作模式,高4位设置计数器1,低4位设置计数器0,我们都设置在工作模式1上。
EA=1; //中断的全局是能开关,使用任何中断都需要打开
ET0=1; //计数器0中断允许控制位
ET1=1; //计数器1中断允许控制位
TR0=1; //计数器工作开关,1-开始工作; 0-停止工作
TR1=1; //同上
TMOD各个字段的含义:
GATE: 为1时,与INT0/1引脚共同控制计数器0/1。我们默认使用0,不受INT0/1控制;
C/T: C为Couter,计数器模式,T为Timer,定时器模式;T上面有横杠,说明是0有效;Timer模式的时候,使用系统内部时钟输入;Couter模式的时候,使用T0/1引脚输入;
M1M0两位组成了4种工作模式:
- 00 :13位定时器,TL0/1只用低5位;TH0/1全部使用;
- 01 : 16位定时器, TH0/1,TL0/1都使用
- 10 : 8位自动重装定时器,当溢出时,将TH0/1存放的值自动装入TL0/1
- 11 : 计时器1停止计数;计时器0作为两个8位寄存器使用;TL0由计数器0控制位控制,TH0由计数器1控制位控制
下面是完整代码实现,实现每秒种LED状态翻转:
#include<reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit LED1=P2^0;
sbit LED2=P2^1;
void timerInit()
{
TMOD=0x11;
EA=1;
ET0=1;
ET1=1;
TR0=1;
TR1=1;
TH0=0xFC;
TL0=0x6A;
TH1=0xFC;
TL1=0x6A;
}
void timer0() interrupt 1
{
static u16 counter=0;
if(counter==1000)
{
LED1=~LED1;
counter=0;
}
counter++;
TH0=0xFC;
TL0=0x6A;
}
void timer1() interrupt 3
{
static u16 counter=0;
if(counter==1000)
{
LED2=~LED2;
counter=0;
}
counter++;
TH1=0xFC;
TL1=0x6A;
}
void main()
{
timerInit();
while(1);
}
代码中我们的计数值不是0xFC18,而是0xFC6A,因为我使用的单片机频率不是12MHz,而是11.018398MHz.