定时器相关寄存器

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-08-29 08:09:36 Views: 0 Likes: 0 Comments: 0

041.5.2 定时器相关寄存器

#include "reg52.h"
#include <intrins.h>

typedef unsigned char uchar;
typedef unsigned int uint;

uint tim_count = 0;

sbit LED1 = P1^0;
sbit LED2 = P1^1;

void Delay_xms(uint xms) // @11.0592MHz
{
    uchar data i, j;

    while (xms)
    {
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j);
        } while (--i);
        xms--;
    }
}

void main()
{
    TMOD &= 0xF0; //TMOD低四位清零
    TMOD |= 0x01; //定时器0配置成工作模式1
    TL0 = 0x00;
    TH0 = 0xDC;   //装载计数初值0xDC00  10ms触发一次中断
    TF0 = 0;      //标志位清零
    TR0 = 1;      //开始运行定时器

    ET0 = 1;      //允许定时器中断
    EA = 1;       //总中断打开

    while (1)
    {
        Delay_xms(1000);
    }
}

/*****************
定时器0的中断处理函数
*****************/
void Timer0_Routine(void) interrupt 1
{
    TL0 = 0x00;
    TH0 = 0xDC;              //重装初值
    tim_count++;             //定时器中断每触发一次,count值+1
    tim_count = tim_count % 100; //让count值在0-99之间变化

    if (0 == tim_count)
    {
        LED1 = ~LED1; //count每计数100次,改变一下灯的状态
        LED2 = ~LED2; //10ms*100 = 1000ms = 1s  每隔一秒灯的状态改变一次
    }
}

一、定时器相关寄存器

STC89C52 的 Timer0、Timer1 不是由一个寄存器完成全部配置。工作模式、当前计数值、启停状态、溢出请求和中断允许分别放在不同寄存器中:

功能类别Timer0Timer1作用
工作方式TMOD低四位TMOD高四位选择门控、定时/计数功能和模式0~3
计数值TH0TL0TH1TL1保存当前计数值或装入计数初值
启停与溢出TCON.TR0TCON.TF0TCON.TR1TCON.TF1控制运行并报告溢出
中断允许IE.ET0IE.EAIE.ET1IE.EA决定溢出请求能否进入 CPU

这些名称有规律可循:T 表示 Timer,H/L 表示高/低字节,R 可记作 Run,F 可记作 Flag,M 表示 Mode,E 表示 Enable。理解每个寄存器在信号链中的位置,比单独背诵赋值更可靠。

本节程序使用 Timer0 模式1,完整路径是:

TMOD选择输入、门控和模式
          │
          ▼
TR0启动计数 → TH0:TL0从0xDC00向上计数
          │
          ▼
0xFFFF再加1 → TF0置1
          │
          ├── ET0=1:Timer0局部允许
          └── EA=1:CPU总允许
          ▼
000BH向量 → Timer0_Routine() interrupt 1

1.1 定时器控制寄存器

Timer0、Timer1 的控制寄存器是 TCON,地址为 88H,可位寻址。它不仅控制定时器开始和停止,还保存 Timer0、Timer1 的溢出请求标志,并兼管 INT0、INT1 的触发方式和请求标志。

TCON(88H) B7   B6   B5   B4   B3   B2   B1   B0
             TF1  TR1  TF0  TR0  IE1  IT1  IE0  IT0
复位值        0    0    0    0    0    0    0    0
名称含义谁负责操作
B7TF1Timer1 溢出请求标志溢出时硬件置 1;CPU 响应 T1 中断时硬件清 0,也可软件清 0
B6TR1Timer1 运行控制,1允许运行、0停止软件设置,还受 TMOD.GATE1 影响
B5TF0Timer0 溢出请求标志溢出时硬件置 1;CPU 响应 T0 中断时硬件清 0,也可软件清 0
B4TR0Timer0 运行控制,1允许运行、0停止软件设置,还受 TMOD.GATE0 影响
B3IE1外部中断1请求标志外部条件成立时硬件置位
B2IT1外部中断1触发方式,0低电平、1下降沿软件设置
B1IE0外部中断0请求标志外部条件成立时硬件置位
B0IT0外部中断0触发方式,0低电平、1下降沿软件设置

TR0TF0 是最容易混淆的一组:

TR0 = 1:打开“计数运行开关”
TF0 = 1:报告“计数器已经溢出”

正常工作过程如下:

软件清TF0 ──► 软件置TR0 ──► 硬件持续计数
                                      │
                                      ▼
                           溢出时硬件置TF0
                                      │
                           ET0和EA都为1?
                                      │ 是
                                      ▼
                              CPU进入Timer0 ISR

程序中的 TF0 = 0 是初始化保险。芯片复位后 TF0 本来就是 0,但程序可能不是刚复位就开始配置 Timer0;显式清零可以消除先前运行阶段遗留的请求。TR0 = 1 只启动计数,不会自动打开中断,Timer0 中断还必须设置 ET0 = 1EA = 1

中断允许寄存器 IE 中与定时器有关的位为:

IE(A8H)   B7   B6   B5   B4   B3   B2   B1   B0
             EA    -  ET2   ES  ET1  EX1  ET0  EX0
  • EA:CPU 中断总允许位。
  • ET0:Timer0 溢出中断允许位。
  • ET1:Timer1 溢出中断允许位。
  • ET2:Timer2 中断允许位。

STC89C52 还具有 Timer2,但它由独立的 T2CON 等寄存器控制。Timer2 使用 TR2 作为运行控制位;TR0TR1 则分别属于 Timer0、Timer1。本节重点仍是与传统 8051 兼容的 Timer0、Timer1。

1.2 定时器模式寄存器

TMOD 是地址为 89H 的 8 位工作方式寄存器,复位值为 00H,不可位寻址。高四位控制 Timer1,低四位控制 Timer0;两组字段含义相同。

TMOD(89H)

位序号       B7     B6    B5  B4     B3     B2    B1  B0
位名称       GATE1  C/T1  M1  M0     GATE0  C/T0  M1  M0
             └────── Timer1 ──────┘   └────── Timer0 ──────┘
Timer1位Timer0位字段作用
B7B3GATE选择是否让 INT1/INT0 引脚参与运行门控
B6B2C/T选择内部时钟定时或外部脉冲计数
B5B1M1工作模式选择高位
B4B0M0工作模式选择低位

GATE:定时器的启动条件

运行使能逻辑可以写成:

Timer0允许计数 = TR0 AND(NOT GATE0 OR INT0)
Timer1允许计数 = TR1 AND(NOT GATE1 OR INT1)

以 Timer0 为例:

GATE0=0
    │  NOT GATE0=1
    └──────────────┐
                   OR ──┐
INT0 ──────────────┘     AND ──► Timer0计数使能
TR0 ─────────────────────┘

结果:只要TR0=1就能计数,INT0电平无关。
GATE0=1
    │  NOT GATE0=0
    └──────────────┐
                   OR ──┐
INT0 ──────────────┘     AND ──► Timer0计数使能
TR0 ─────────────────────┘

结果:必须TR0=1并且INT0=1才计数,可用于测量INT0高电平脉宽。

因此普通周期定时通常设置 GATE=0,由 TRx 单独控制;GATE=1 不是错误配置,而是面向外部门控测量的功能。

C/T:输入脉冲来源选择

系统时钟 ── 12T或6T分频 ──┐
                           ├── C/T多路选择器 ──► 计数器
P3.4/T0或P3.5/T1外部脉冲 ─┘
C/T功能Timer0脉冲来源Timer1脉冲来源
0定时器系统时钟经 12T 或 6T 分频系统时钟经 12T 或 6T 分频
1计数器P3.4/T0 外部脉冲P3.5/T1 外部脉冲

定时功能统计的是内部时钟节拍,外部计数功能统计的是引脚事件。二者使用同一套 TH/TL 加法器、溢出标志和中断通路,只是计数脉冲来源不同。

M1、M0:工作模式选择

M1 M0模式计数器结构状态数重装方式
00模式013位,THx 8位加 TLx 低5位2^13=8192溢出后软件重装
01模式116位,THx、TLx 全部参与2^16=65536溢出后软件重装
10模式28位,TLx计数、THx保存重装值2^8=256硬件自动重装
11模式3Timer0拆成两个8位单元每个256特殊控制;Timer1在模式3停止

本例选择模式1,所以 Timer0 的四个位为:

GATE0  C/T0  M1  M0
  0      0    0   1  = 0001B

代码没有直接使用 TMOD = 0x01,而是:

TMOD &= 0xF0;
TMOD |= 0x01;

第一句只清除 Timer0 的低四位,保留 Timer1 的高四位;第二句再把 Timer0 的 M0 置 1,得到 GATE0=0C/T0=0、模式1。若配置 Timer1 模式1,则对应的高四位应为 0001B,也就是在不考虑 Timer0 状态时写入高半字节 0x10

模式1内部计数结构

系统时钟/12或/6 ──┐
                    ├── C/T选择 ── 运行门控 ──► TL0(低8位)
P3.4/T0外部脉冲 ──┘                              │ 溢出进位
                                                  ▼
                                             TH0(高8位)
                                                  │ 溢出
                                                  ▼
                                               TF0置1
                                                  │
                                       ET0=1 且 EA=1
                                                  ▼
                                      interrupt 1,向量000BH

TH0TL0 各自都是 8 位寄存器。模式1把它们级联成一个 16 位向上计数器:

寄存器地址所属定时器模式1中的作用
TL08AHTimer016位计数值的低8位
TL18BHTimer116位计数值的低8位
TH08CHTimer016位计数值的高8位
TH18DHTimer116位计数值的高8位
TH0             TL0
高8位           低8位
dddd cccc : bbbb aaaa  →  共16位

Timer0 使用 TH0:TL0,Timer1 使用 TH1:TL1。以 Timer0 为例,组合值可以写成:

16位计数值 = (TH0 << 8) | TL0
TH0 = 0xDC,TL0 = 0x00  →  TH0:TL0 = 0xDC00

16 位共有 65536 个状态,范围是 0x0000~0xFFFF,也就是十进制 0~65535。计数器到达 0xFFFF 时只是处在最后一个有效状态;下一个计数脉冲使其回绕到 0x0000,此时才发生溢出并由硬件置位 TF0

1.3 应用案例

本例在 11.0592 MHz、12T 定时方式下,把 Timer0 配置成约 10 ms 溢出一次。寄存器和代码的对应关系如下:

配置寄存器结果含义
`TMOD &= 0xF0; TMOD= 0x01;`Timer0低四位=0001B
TH0=0xDC; TL0=0x00;TH0:TL0=0xDC00装入16位计数初值
TF0=0;TCON.B5=0清除待处理的溢出请求
TR0=1;TCON.B4=1启动 Timer0
ET0=1;IE.B1=1允许 Timer0 中断
EA=1;IE.B7=1打开 CPU 总中断允许

定时时间取决于计数脉冲频率和初值。通用关系为:

计数次数 N = 65536 - 初值X
定时时间 t = N ÷ 计数频率

反推初值:X = 65536 - t × 计数频率

在 12T 模式下:

计数频率 = 11.0592 MHz ÷ 12 = 921600 Hz
10 ms所需计数 = 0.010 s × 921600 Hz = 9216
初值 = 65536 - 9216 = 56320 = 0xDC00

因此硬件按下面的顺序运行:

0xDC00 → 0xDC01 → ... → 0xFFFE → 0xFFFF
                                          │ 下一个计数脉冲
                                          ▼
                              0x0000,同时TF0=1
                                          │
                                          ▼
                                   进入Timer0 ISR

ISR 重装 0xDC00,再让 tim_count 加一。100 次约 10 ms 中断累计为约 1 s,此时翻转 LED1、LED2。Timer0 的硬件计数与主循环中的 Delay_xms(1000) 并行进行;CPU 执行延时循环时仍可被 Timer0 中断打断。

模式1的 16 位范围比模式2更大,可以直接得到更长的单次定时;这不代表模式1的基本时间分辨率天然更高。只要计数脉冲相同,两种模式每增加一次所代表的时间相同,差别主要在可计数范围和重装方式。

1.3.1 例题: 重装初值

“装载初值”是启动定时器前把起始值写入 TH0、TL0;“重装初值”是一次溢出后,为下一轮计时再次写入相同起始值。

本例第一次装载发生在初始化阶段:

TL0 = 0x00;
TH0 = 0xDC;

模式1溢出后,硬件只会让计数器回绕到 0x0000,不会自动恢复 0xDC00,所以 ISR 中再次写入:

void Timer0_Routine(void) interrupt 1
{
    TL0 = 0x00;
    TH0 = 0xDC;
    // 后续中断处理
}

如果省略重装,第一次间隔仍约为 10 ms,但下一轮会从 0x0000 开始,必须累计完整的 65536 次才再次溢出。在 11.0592 MHz、12T 模式下,下一轮约为:

65536 ÷ 921600 Hz ≈ 71.11 ms

这就破坏了预期的固定 10 ms 节拍。只有当设计本来就需要从 0x0000 计满时,才不必装入非零初值。

模式1的软件重装过程可以画成:

软件装入0xDC00
      │
      ▼
硬件向上计数并溢出
      │
      ▼
TF0=1,CPU进入ISR
      │
      ├── 软件重新写TH0/TL0=0xDC00
      └── 执行业务处理
      ▼
开始下一轮约10 ms计时

Timer0 在 ISR 中仍保持运行,溢出到完成重装之间已经经过若干机器周期,所以实际周期会包含中断响应和重装指令的时间,并存在少量误差。更严格的设计可以采用经过补偿的初值、短小 ISR、硬件自动重装模式,或者更高级的定时外设。若选择先 TR0=0、重装后再 TR0=1,可以避免写寄存器时继续计数,但停止与启动指令本身仍要计入误差分析。

模式2提供硬件自动重装:

TH0:保存8位重装值,通常保持不变
TL0:8位向上计数

TL0从0xFF再加1
      │
      ├── TF0置1并请求中断
      └── 硬件把TH0内容自动复制到TL0
对比项模式1模式2
计数位数16位8位
计数状态数65536256
参与计数的寄存器TH0与TL0级联TL0计数
初值保存TH0、TL0共同保存本轮初值TH0持续保存重装值
溢出后的处理软件重写TH0、TL0硬件自动把TH0复制到TL0
主要优势单次计数范围大周期自动重装,软件负担小、重复周期更稳定
主要限制软件重装会引入额外延迟8位范围较小,单次最长时间较短

模式2并不是“不建议使用”的模式。需要较短、稳定、重复周期时,自动重装通常非常合适;需要较长的单次定时范围时,模式1更方便。选择依据应是目标周期、时钟频率、允许误差和软件负担,而不是简单认为某一种模式在所有场景都更好。

最后把初值概念归纳为一句话:TH0/TL0 保存的是计数器起点,不是直接保存“10 ms”这样的时间值。只有把系统时钟、6T/12T 设置、工作模式、计数状态数和中断响应过程一起考虑,才能得到正确的定时时间。