定时器相关寄存器
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 不是由一个寄存器完成全部配置。工作模式、当前计数值、启停状态、溢出请求和中断允许分别放在不同寄存器中:
| 功能类别 | Timer0 | Timer1 | 作用 |
|---|---|---|---|
| 工作方式 | TMOD低四位 | TMOD高四位 | 选择门控、定时/计数功能和模式0~3 |
| 计数值 | TH0、TL0 | TH1、TL1 | 保存当前计数值或装入计数初值 |
| 启停与溢出 | TCON.TR0、TCON.TF0 | TCON.TR1、TCON.TF1 | 控制运行并报告溢出 |
| 中断允许 | IE.ET0、IE.EA | IE.ET1、IE.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
| 位 | 名称 | 含义 | 谁负责操作 |
|---|---|---|---|
| B7 | TF1 | Timer1 溢出请求标志 | 溢出时硬件置 1;CPU 响应 T1 中断时硬件清 0,也可软件清 0 |
| B6 | TR1 | Timer1 运行控制,1允许运行、0停止 | 软件设置,还受 TMOD.GATE1 影响 |
| B5 | TF0 | Timer0 溢出请求标志 | 溢出时硬件置 1;CPU 响应 T0 中断时硬件清 0,也可软件清 0 |
| B4 | TR0 | Timer0 运行控制,1允许运行、0停止 | 软件设置,还受 TMOD.GATE0 影响 |
| B3 | IE1 | 外部中断1请求标志 | 外部条件成立时硬件置位 |
| B2 | IT1 | 外部中断1触发方式,0低电平、1下降沿 | 软件设置 |
| B1 | IE0 | 外部中断0请求标志 | 外部条件成立时硬件置位 |
| B0 | IT0 | 外部中断0触发方式,0低电平、1下降沿 | 软件设置 |
TR0 与 TF0 是最容易混淆的一组:
TR0 = 1:打开“计数运行开关”
TF0 = 1:报告“计数器已经溢出”
正常工作过程如下:
软件清TF0 ──► 软件置TR0 ──► 硬件持续计数
│
▼
溢出时硬件置TF0
│
ET0和EA都为1?
│ 是
▼
CPU进入Timer0 ISR
程序中的 TF0 = 0 是初始化保险。芯片复位后 TF0 本来就是 0,但程序可能不是刚复位就开始配置 Timer0;显式清零可以消除先前运行阶段遗留的请求。TR0 = 1 只启动计数,不会自动打开中断,Timer0 中断还必须设置 ET0 = 1 与 EA = 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 作为运行控制位;TR0、TR1 则分别属于 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位 | 字段 | 作用 |
|---|---|---|---|
| B7 | B3 | GATE | 选择是否让 INT1/INT0 引脚参与运行门控 |
| B6 | B2 | C/T | 选择内部时钟定时或外部脉冲计数 |
| B5 | B1 | M1 | 工作模式选择高位 |
| B4 | B0 | M0 | 工作模式选择低位 |
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 | 模式0 | 13位,THx 8位加 TLx 低5位 | 2^13=8192 | 溢出后软件重装 |
01 | 模式1 | 16位,THx、TLx 全部参与 | 2^16=65536 | 溢出后软件重装 |
10 | 模式2 | 8位,TLx计数、THx保存重装值 | 2^8=256 | 硬件自动重装 |
11 | 模式3 | Timer0拆成两个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=0、C/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
TH0、TL0 各自都是 8 位寄存器。模式1把它们级联成一个 16 位向上计数器:
| 寄存器 | 地址 | 所属定时器 | 模式1中的作用 |
|---|---|---|---|
TL0 | 8AH | Timer0 | 16位计数值的低8位 |
TL1 | 8BH | Timer1 | 16位计数值的低8位 |
TH0 | 8CH | Timer0 | 16位计数值的高8位 |
TH1 | 8DH | Timer1 | 16位计数值的高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位 |
| 计数状态数 | 65536 | 256 |
| 参与计数的寄存器 | TH0与TL0级联 | TL0计数 |
| 初值保存 | TH0、TL0共同保存本轮初值 | TH0持续保存重装值 |
| 溢出后的处理 | 软件重写TH0、TL0 | 硬件自动把TH0复制到TL0 |
| 主要优势 | 单次计数范围大 | 周期自动重装,软件负担小、重复周期更稳定 |
| 主要限制 | 软件重装会引入额外延迟 | 8位范围较小,单次最长时间较短 |
模式2并不是“不建议使用”的模式。需要较短、稳定、重复周期时,自动重装通常非常合适;需要较长的单次定时范围时,模式1更方便。选择依据应是目标周期、时钟频率、允许误差和软件负担,而不是简单认为某一种模式在所有场景都更好。
最后把初值概念归纳为一句话:TH0/TL0 保存的是计数器起点,不是直接保存“10 ms”这样的时间值。只有把系统时钟、6T/12T 设置、工作模式、计数状态数和中断响应过程一起考虑,才能得到正确的定时时间。