定时器介绍

  • CPU时序的有关知识

    1. 振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)
    2. 状态周期:2个振荡周期为1个状态周期,用S表示。振荡周期又称S周期或时钟周期
    3. 机器周期:1个机器周期含6个状态周期,12个振荡周期
    4. 指令周期:完成一条指令所占用的全部时间,它以机器周期为单位

    例如:外接晶振12MHz时,51单片机相关周期的具体值为:

    • 振荡周期 = 112\frac{1}{12}us
    • 状态周期 = 16\frac{1}{6}us
    • 机器周期 = 1us
    • 指令周期 = 1~4us
  • 学习定时器前需要明白的几点

    1. 51单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之为定时器/计数器。
    2. 定时器/计数器和单片机的CPU是相互独立的。定时器/计数器工作的过程是自动完成的,不需要CPU的参与。
    3. 51单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加1。

  有了定时器/计数器之后,可以增加单片机的效率,一些简单的重复加1的工作可以交给定时器/计数器处理。CPU转而处理一些复杂的事情。同时可以实现精确定时作用。

单片机定时器原理

  STC89C5X 单片机内有两个可编程的定时/计数器 T0、T1 和一个特殊功能定时器 T2定时/计数器的实质是加 1 计数器(16 位),由高 8 位和低8位两个寄存器THx和TLx组成。它随着计数器的输入脉冲进行自加1,也就是每来一个脉冲,计数器就自动加1,当加到计数器为全1时,再输入一个脉冲就使计数器回零,且计数器的溢出使相应的中断标志位置1,向CPU发出中断请求(定时/计数器中断允许时)。如果定时/计数器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满。可见,由溢出时计数器的值减去计数初值才是加1计数器的计数值。

定时/计数器结构

  51单片机定时器/计数器内部结构如下所示:

  上图中的T0和T1引脚对应的是单片机P3.4和P3.5管脚。51单片机定时/计数器的工作由两个特殊功能寄存器控制。TMOD是定时/计数器的工作方式寄存器,确定工作方式和功能;TCON 是控制寄存器,控制 T0、T1 的启动和停止及设置溢出标志。

工作方式寄存器TMOD

  工作方式寄存器TMOD用于设置定时/计数器的工作方式,低四位用于T0,高四位用于T1。其格式如下:

7 6 5 4 3 2 1 0
字节地址:89H GATE C/T\overline{T} M1 M0 GATE C/T\overline{T} M1 M0 TMOD

  GATE 是门控位, GATE=0时,用于控制定时器的启动是否受外部中断源信号的影响。只要用软件使TCON中的TR0或TR1为1,就可以启动定时/计数器工作;GATA=1 时,要用软件使TR0或TR1为1,同时外部中断引脚INT0/1也为高电平时,才能启动定时/计数器工作。即此时定时器的启动条件,加上了INT0/1引脚为高电平这一条件。

  • C/T : 定时/计数模式选择位

    • C/T = 0 为定时模式
    • C/T = 1 为计数模式
  • M1M0 : 工作方式设置位,定时/计数器有四种工作方式,如下表所示:

    M1M0 工作方式 说明
    00 方式0 13位定时/计数器
    01 方式1 16位定时/计数器
    10 方式2 8位自动重载定时/计数器
    11 方式3 T0分成两个独立的8位定时/计数器;T1此方式停止计数

控制寄存器TCON

  TCON的低4位用于控制外部中断,TCON的高4位用于控制定时器的启动和中断申请。其格式如下:

7 6 5 4 3 2 1 0
字节地址:88H TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0 TCON
  • TF1(TCON.7) : T1 溢出中断请求标志位。T1计数溢出时由硬件自动置TF1为1。CPU响应中断后TF1由硬件自动清0。T1工作时,CPU可随时查询TF1的状态。所以,TF1可用作查询测试的标志。TF1也可以用软件置1或清0,同硬件置1或清0的效果一样。
  • TR1(TCON.6) : T1 运行控制位。TR1置1时,T1开始工作;TR1置0时,T1 停止工作。TR1由软件置1或清0。所以,用软件可控制定时/计数器的启动与停止。
  • TF0(TCON.5) : T0 溢出中断请求标志位,其功能与TF1类同。
  • TR0(TCON.4) : T0 运行控制位,其功能与TR1类同。
  • IT0(TCON.0) : 外部中断0触发方式控制位
    • IT0 = 0,为电平触发方式
    • IT0 = 1,为边沿触发方式(下降沿有效)
  • IE0(TCON.1) : 外部中断0中断请求标志位
  • IT1(TCON.2) : 外部中断1触发方式控制位
  • IE1(TCON.3) : 外部中断1中断请求标志位

定时/计数器工作方式

方式0

  方式0为13位计数,由TL0的低5位(高3位未用)和TH0的8位组成。TL0 的低5位溢出时向TH0进位,TH0溢出时,置位TCON中的TF0标志,向CPU发出中断请求。其结构图如下所示:

  门控位GATE具有特殊的作用。当GATE=0时,经反相后使或门输出为1,此时仅由TR0控制与门的开启,与门输出1时,控制开关接通,计数开始;当GATE=1时,由外中断引脚信号控制或门的输出,此时控制与门的开启由外中断引脚信号和TR0共同控制。

  当TR0=1时,外中断引脚信号引脚的高电平启动计数,外中断引脚信号引脚的低电平停止计数。这种方式常用来测量外中断引脚上正脉冲的宽度。计数模式时,计数脉冲是T0引脚上的外部脉冲。计数初值与计数个数的关系为:X=2132^{13}-N。

方式1

  方式1的计数位数时16位,由TL0作为低8位,TH0作为高8位,组成了16位加1计数器,其结构图如下所示:

  计数初值与计数个数的关系为:X=2162^{16}-N。

方式2

  方式 2 为自动重装初值的 8 位计数方式。工作方式 2 特别适合于用作较精确的脉冲信号发生器。其结构图如下所示:

  计数初值与计数个数的关系为:X=282^{8}-N。

方式3

  方式3只适用于定时/计数器T0,定时器 T1 处于方式3时相当于TR1=0,停止计数。工作方式 3 将 T0 分成为两个独立的 8 位计数器 TL0 和 TH0。其结构如下所示:

这几种工作方式中应用较多的是方式1和方式2。定时器中通常使用定时器方式1,串口通信中通常使用方式2

定时器配置

  在使用定时器时,应该如何配置使其工作呢?其步骤如下(各步骤个任意):

  1. TMOD赋值,以确定T0和T1的工作方式,如果使用定时器0即对T0配置,如果使用定时器1即对T1配置

  2. 根据所要定时的时间计算初值,并将其写入TH0TL0TH1TL1

  3. 如果使用中断,则对EA赋值,开放定时器中断

  4. 使TR0TR1置位,启动定时/计数器定时或计数

    上述中有一个定时/计数器初值的计算,下面我们来看下如何计算定时/计数器初值。

  前面我们介绍过机器周期的概念,它是CPU完成一个基本操作所需要的时间。其计算公式是:机器周期=1/单片机的时钟频率。51单片机内部时钟频率是外部时钟的12分频,也就是说当外部晶振的频率输入到单片机里面的时候要进行12分频。比如说你用的是12MHZ晶振,那么单片机内部的时钟频率就是12/12MHZ,当你使用12MHZ的外部晶振的时候,机器周期=1/1M=1us。 如果我们想定时1ms的初值是多少呢?1ms/1us=1000。也就是要计数1000个,初值=65535-1000+1(因为实际上计数器计数到65536(2的16次方)才溢出,所以后面要加1)=64536=FC18H,所以初值即为THx=0XFC,TLx=0X18。

  知道了如何计算定时/计数器初值,那么想定时多长时间都可以计算出,当然由于定时计数器位数有限,我们不可能直接通过初值定时很长时间,如果要实现很长时间的定时,比如定时1秒钟。可以通过初值设置定时1ms,每当定时1ms结束后又重新赋初值,并且设定一个全局变量累计定时1ms的次数,当累计到1000次,表示已经定时1秒了。需要其他定时时间类似操作,这样我们就可以使用定时器来实现精确延时来替代之前的delay函数。

这里以定时器0为例介绍配置定时器工作方式1、设定1ms初值,开启定时器计数功能以及总中断,如下:

1
2
3
4
5
6
7
8
9
void time0_init()
{
TMOD |= 0x01; // 选择位定时器0模式,工作方式1
TH0 = 0xFC; // 给定时器赋初值(64536),定时1ms
TL0 = 0x18;
ET0 = 1; // 打开定时器0中断允许
EA = 1; // 打开总中断
TR0 = 1; // 打开定时器
}

软件设计

  本实验要实现的功能时:通过定时器0中断控制D1指示灯间隔1秒闪烁。

实物接线图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "reg52.h"

// 对系统默认数据类型进行重命名
typedef unsigned int u16;
typedef unsigned char u8;

// 定义LED1管脚
sbit LED1 = P2^0;

/*******************************************************************************
*函数名: time0_init
*函数功能:定时器0中断配置函数,通过设置TH和TL即可确定定时时间
*输入:无
*输出:无
*******************************************************************************/
void time0_init()
{
TMOD |= 0x01; // 选择为定时器0模式,工作方式1
TH0 = 0xFC; // 给定时器赋初值(64536)
TL0 = 0x18;
ET0 = 1; // 打开定时器0中断允许
EA = 1; // 打开总中断
TR0 = 1; // 打开定时器
}

void main()
{
time0_init(); // 定时器0中断配置

while(1)
{

}
}

void time0() interrupt 1 // 定时器0中断函数
{
static u16 count = 0; // 静态变量
TH0 = 0xFC;
TL0 = 0x18;
count++;
if(count == 1000) // 1000次 * 1000us = 1000次 * 1ms = 1000ms = 1s
{
count = 0;
LED1 = !LED1; // 翻转LED1的状态
}
}