首先,定时器的计数是加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需要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种工作模式:
以下是实现LED状态每秒翻转的完整代码实现:
#include
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。