1,前置知识
振荡周期: 为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)。
状态周期: 2 个振荡周期为 1 个状态周期,用 S 表示。振荡周期又称 S 周期或时钟周期。
机器周期:1 个机器周期含 6 个状态周期,12 个振荡周期
指令周期:完成 1 条指令所占用的全部时间,它以机器周期为单位。一般1~4个机械周期
如:
外接晶振为 12MHz 时,51 单片机相关周期的具体值为:
振荡周期 = 1/12us;状态周期 = 1/6us;机器周期 = 1us;指令周期 = 1~4us;
2,51单片机定时器
①:51单片机有两组基本的定时器,因为既可以定时,又可以计数,故称之 为【定时器/计数器】
②:定时器/计数器和单片机的 CPU 是相互独立的。定时器/计数器工作的过程 是自动完成的,不需要 CPU 的参与。
③:5 1 单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信 号对寄存器中的数据加 1。---> 定时/计数器的实质是加 1 计数器(16 位),由高 8 位和低 8 位两 个寄存器 THx 和 TLx 组成。
内部结构图:
上图描述了两个定时器T0/T1,计数脉冲是由机器周期脉冲,定时器颞部由两个寄存器为TH0,TL0分别为8bit数据,即定时器初值。
TMOD定义了定时器工作方式,高4位为T1,低4位为T0.
TCON 寄存器为中断请求标志控制寄存器,TF0位中断溢出标志,TR0 = 1为开启定时器0.
GATE 门控位:控制定时器是否受外部中断源信号影响。
① GATE为0时,不受外部中断控制,启动定时器可以通过TR0 = 1/0 和TR1 = 1/0,控制定时器启动。
② GATE = 1时,不仅需要TR0 =1/0,TR1 = 1/0控制定时器启动,还需要外部中断控制引脚INT0、INT1 位1,才能启动定时器工作。
C/T 位: 表示定时/计数模式选择位。C/T =0 为定时模式;C/T =1 为计数模式
M1M0:工作方式设置位。定时/计数器有四种工作方式。
定时器一般使用工作方式1,工作方式二一部用于串口定时。
前一章节提到过这个定时器,低4位为外部中断控制位和中断溢出位,这个高4位为定时器中断控制位和溢出位。所以启动定时器需要去设置TR0、TR1.
注:T1 计数溢出时由硬件自动置 TF1 为 1。CPU 响应中断后 TF1 由硬件自动清 0
注重介绍方式1工作原理:
从右到左看,TMOD寄存器GATE经过一个非门,一级INT0外部中断0的引脚绑定到一个>=1的逻辑门上,我们知道不管Gate位取值多少,要启动定时器需要TR0 = 1,从图中的&门可以看出,如果Gate位为1,则取决于INT0引脚的值是否为1,如果Gate位为0,则与INT0引脚无关,这就是门控位所决定的事。
在看到TMOD中的C/T 位,C/T = 0时为连接到机器周期,C/T = 1时,连接到T0引脚(即单片机P32上)。
使用当中间开关闭合时,定时器就工作了。
所以如何让定时器0工作啦?代码如何操作寄存器。
TMOD |= 0x01 // 让定时器工作在方式1,同时c/t = 0, gate = 0(关闭外部中断影响);
TH0 = 0xFF; // 定时器初值高位
TL0 = 0xAA; // 定时器初值低位
ET0 = 1; //开启Time0中断允许位
EA = 1; // 开启cpu中断使能
TR0 = 1;//启动定时器
3,定时器初值的计算
因为我们让定时器的C/T = 0,即定时信号来自于机器周期,我们知道机器周期等于12 个震荡周期。所以我们如何计算定时的初值那?
我的51单片机所以得外部晶振是11.0592Mhz的,
一个机器周期 时间:T = (12 *1)/ ( 11.0592 * 10^(6) ) = 1.08507 x 10^(-6)s = 1.08507us
由于TH0,TL0最大为16位即,最大值位0xFFFF = 65535,在来一个脉冲就溢出了。如果我现在要实现定时1ms,这个初值怎么计算:
1000 / 1.08507 = 922 个脉冲 ,即定时器需要计数922个脉冲,即使用65536 - 922 = 64614,这个值就为计数器初值:0xFC66.
4,定时器代码实现
实现两定时器每隔1s,1.5s亮灭LED0,LED1 (注意中断号)
#include <reg52.h>
// 1.08507us -->一个机器周期 (12 / 11.0592 us)
// 从初始值加1,直到加到最大值产生time中断
typedef unsigned char u8;
typedef unsigned int u16;
sbit LED1 = P2^0;
sbit LED2 = P2^1;
void init_time0()
{
TMOD |= 0x01; // 选择定时器0,工作方式1
TH0 = 0xFC; //初始time0高位
TL0 = 0x66; //初始time0低位
ET0 = 1; // 打开定时器0中断允许
EA = 1; // 打开系统总中断使能
TR0 = 1; // 打开定时器
}
void time0_run() interrupt 1
{
static u16 i = 0;
//重新设置中断
TH0 = 0xFC; //初始time0高位
TL0 = 0x66; //初始time0低位
// 1s
i++;
if(i == 1000)
{
i=0;
LED1 = !LED1;
}
}
void init_time1()
{
TMOD |= 0x10; // 选择定时器1,工作方式1
TH1 = 0xFC; //初始time1高位
TL1 = 0x66; //初始time1低位
ET1 = 1; // 打开定时器1中断允许
EA = 1; // 打开系统总中断使能
TR1 = 1; // 打开定时器
}
void time1_run() interrupt 3
{
static u16 i = 0;
//重新设置中断
TH1 = 0xFA; //初始time0高位
TL1 = 0x9A; //初始time0低位
// 1.5s
i++;
if(i == 1000)
{
i=0;
LED2 = !LED2;
}
}
void main()
{
init_time0();
init_time1();
while(1);
}
5,总结
定时器操作较为复杂,需要着重理解方式1的工作原理,才能对代码操作的每个寄存器弄明白。其次这里并没有介绍将C/T = 1(计数模式),后期遇到再补充