目录
[0 基础知识](#0 基础知识)
[1.1 AT89C52的3个定时器](#1.1 AT89C52的3个定时器)
[1.2定时器在单片机里的 4 大作用](#1.2定时器在单片机里的 4 大作用)
[1.3 四种工作方式(T0/T1)](#1.3 四种工作方式(T0/T1))
[1.4使用步骤(以T0方式12 MHz 晶振产生 1 ms 中断为例)](#1.4使用步骤(以T0方式12 MHz 晶振产生 1 ms 中断为例))
[1.4.1 算初值计算](#1.4.1 算初值计算)
[1.4.2 寄存器配置](#1.4.2 寄存器配置)
[1.5.1 电路原理图](#1.5.1 电路原理图)
[1.5.2 控制程序](#1.5.2 控制程序)
[1.5.3 小灯的输入电压实时检测](#1.5.3 小灯的输入电压实时检测)
[1.6 T2 简要补充(8052 专有)](#1.6 T2 简要补充(8052 专有))
[1.8 定时器T0延时实现](#1.8 定时器T0延时实现)
[1.8.1 定时器T0的基本原理](#1.8.1 定时器T0的基本原理)
[1.8.2 定时器T0的初始化](#1.8.2 定时器T0的初始化)
[1.8.3 定时器T0实现延时函数](#1.8.3 定时器T0实现延时函数)
[1.8.4 代码说明](#1.8.4 代码说明)
[1.8.5 定时器T0模式1中的TF0](#1.8.5 定时器T0模式1中的TF0)
[1.9 阻塞式延时实现](#1.9 阻塞式延时实现)
[1.9.1 延时时间估算(假设12MHz晶振)](#1.9.1 延时时间估算(假设12MHz晶振))
[1.9.2 阻塞式延时程序//1ms](#1.9.2 阻塞式延时程序//1ms)
[1.9.3 详细解释](#1.9.3 详细解释)
[1.10 完整例子2------流水灯](#1.10 完整例子2——流水灯)
[1.10.1 电路原理图](#1.10.1 电路原理图)
[1.10.2 控制程序](#1.10.2 控制程序)
**摘要:**本文系统介绍了AT89C52单片机的定时器应用,重点讲解了T0/T1/T2三个定时器的工作原理及使用方法。主要内容包括:1. 定时器基础概念:机器周期计算、四种工作方式及应用场景;2. 定时器配置方法:以12MHz晶振产生1ms中断为例,详细说明初值计算和寄存器配置步骤;3. 两种延时实现方式:通过定时器中断和阻塞式循环,分别给出LED闪烁和流水灯的应用实例;4. 特殊功能补充:8052专有的T2定时器的自动重装、捕获等高级功能。文中配有完整的电路原理图和控制程序,通过对比分析帮助读者掌握定时器的核心应用技巧,实现精确计时和高效控制。
0 基础知识
频率单位:赫兹(Hz)
1 赫兹(Hz) :每秒1 次 周期:1s
1 千赫兹(Hz) :每秒1000 次 周期:1ms
1 兆赫兹(Hz) :每秒1000000 次 周期:1us
1 吉兹(Hz) :每秒1000000000 次 周期:1ns
机器周期(Machine Cycle)是 8051 单片机 执行一条指令的基本时间单位。理解它对于计算延时、定时器初值、波特率等都非常关键。
总结:
8051 的机器周期 = 12 × 时钟周期
12MHz 晶振下,1 机器周期 = 1μs
1.1 AT89C52的3个定时器
3个16位可编程定时/计数器:T0、T1、T2
T0、T1:标准 8051 兼容(方式 0~3)
T2:8052 专有,功能更强(可 16 位自动重装、捕获、波特率发生等)
1.2定时器在单片机里的 4 大作用
产生精确定时(1 ms、10 ms、1 s......)
对外部脉冲计数(T0/T1 脚当计数输入)
生成波特率(UART 方式 1、3 时)
做PWM/脉冲测量/电机测速(配合 T2 捕获功能)
1.3 四种工作方式(T0/T1)
|--------|-------------|---------|---------------|
| 方式 | 位数 | 特点 | 典型用途 |
| 0 | 13 位 | 早期兼容,少用 | 特殊场合 |
| 1 | 16 位 | 一次溢出重装 | 1 ms、50 ms 基时 |
| 2 | 8 位自动重装 | 低字节自动回装 | 波特率、高频中断 |
| 3 | T0 分成两个 8 位 | T1 失去中断 | 特殊应用 |
1.4使用步骤(以T0方式12 MHz 晶振产生 1 ms 中断为例)
1.4.1 算初值计算
机器周期 = 1 µs(12 MHz/12)
1 ms 需计数 1000 次 → 初值 = 65536 − 1000 = 64536 = 0xFC18
定时器T0 设置
TMOD &= 0xF0; // 清零 T0 位
TMOD |= 0x01; // T0 方式 1
定时周期(1ms)
TH0 = 0xFC; // 高 8 位
TL0 = 0x18; // 低 8 位
1.4.2 寄存器配置
定时器T0
TMOD &= 0xF0; // 清零 T0 位
TMOD |= 0x01; // T0 方式 1
TH0 = 0xFC; // 高 8 位
TL0 = 0x18; // 低 8 位
TR0 = 1; // 启动 T0
ET0 = 1; // 允许中断
EA = 1; // 总中断
定时器T0 中断服务函数
void Timer0_ISR(void) interrupt 1
{
TH0 = 0xFC; // 重装初值
TL0 = 0x18;
/* 用户代码:计数、刷新显示、产生 PWM 等 */
}
1.5完整例子1------小灯闪烁1S
1.5.1 电路原理图

电路原理图由AT89C52单片机、示波器、LED电路、复位电路和晶振电路组成,实现P1.0 引脚上的 LED 每 1 秒翻转一次,简单地说就是 1 Hz 的闪烁。
1.5.2 控制程序
定时器T0 实现延时方法1
objectivec
//头文件与位定义
#include <reg52.h>
sbit LED = P1^0;
unsigned int cnt = 0;
//定时器0初始化
void inittimer()
{
TMOD = 0x01;//设置定时器0为模式1(16位定时器)
TH0 = 0xFC;//高8位 定时器周期1 ms
TL0 = 0x18;//低8位 定时器周期1 ms
TR0 = 1;//启动定时器0
ET0 = 1;// 允许定时器0中断
EA = 1;// 打开总中断
}
//主函数
void main()
{
inittimer();//初始化定时器
while(1);//死循环
}
//定时器0中断服务函数
void Timer0_ISR() interrupt 1
{
TH0 = 0xFC;
TL0 = 0x18;// 定时器周期1 ms
//小灯闪烁
if(++cnt >= 1000) // 1000 × 1 ms = 1 s
{
cnt = 0;
LED = ~LED; // LED 翻转
}
}
定时器实现延时方法2
objectivec
//头文件与位定义
#include <reg52.h>
sbit LED = P1^0;
// 定时器T0初始化函数
void Timer0_Init()
{
TMOD |= 0x01; // 设置定时器T0为模式1(16位定时器)
TH0 = (65536 - 50000) / 256; // 设置定时器初值(高8位)
TL0 = (65536 - 50000) % 256; // 设置定时器初值(低8位)
ET0 = 1; // 开启定时器T0中断
EA = 1; // 开启全局中断
TR0 = 1; // 启动定时器T0
}
// 定时器T0中断服务函数
void Timer0_ISR() interrupt 1
{
TH0 = (65536 - 50000) / 256; // 重新加载初值(高8位)
TL0 = (65536 - 50000) % 256; // 重新加载初值(低8位)
// 在这里可以添加需要在定时器中断中执行的代码
}
// 延时函数
void Delay(unsigned int ms)
{
unsigned int i;
for (i = 0; i < ms; i++)
{
while (!TF0); // 等待定时器T0溢出
TF0 = 0; // 清除溢出标志
}
}
void main()
{
Timer0_Init(); // 初始化定时器T0
while (1)
{
LED=1;
Delay(1000); // 延时1000ms
LED=0;
Delay(1000); // 延时1000ms
}
}
1.5.3 小灯的输入电压实时检测

由示波器检测实时电压可知,定时器(T0)中断服务函数实现1秒高低电平的切换。
1.6 T2 简要补充(8052 专有)
寄存器:T2CON、T2MOD、RCAP2H/L
功能:
16 位自动重装(比 T0/T1 方式 2 更宽)
捕获(测量脉冲宽度)
波特率发生器(UART 方式 1/3)
可编程时钟输出(P1.0 输出 50% 方波)
1.7一句话总结
AT89C52 的定时器 = "硬件计数/分频器 + 中断"
只要配置好初值、工作方式、中断,就能让 CPU 从"空转延时"里解放出来,去做更实时、更精确的事情。
1.8 定时器T0延时实现
AT89C52单片机是一款经典的8位单片机,它内部有定时器T0和T1,可以通过定时器实现精确的延时功能。
1.8.1 定时器T0的基本原理
定时器模式:定时器T0可以工作在模式0(13位定时器)、模式1(16位定时器)、模式2(8位自动重装载定时器)和模式3(拆分为两个独立的8位定时器)。
定时原理:定时器通过内部的计数器来实现延时。当计数器从初始值计数到溢出值时,产生一个溢出中断(如果中断使能)或可以通过查询标志位来判断是否达到延时。
1.8.2 定时器T0的初始化
在使用定时器T0之前,需要进行初始化,设置定时器的工作模式、初始值等参数。
1.8.3 定时器T0实现延时函数
objectivec
#include <reg52.h> // 包含AT89C52的寄存器定义
// 定时器T0初始化函数
void Timer0_Init()
{
TMOD |= 0x01; // 设置定时器T0为模式1(16位定时器)
TH0 = (65536 - 50000) / 256; // 设置定时器初值(高8位)
TL0 = (65536 - 50000) % 256; // 设置定时器初值(低8位)
ET0 = 1; // 开启定时器T0中断
EA = 1; // 开启全局中断
TR0 = 1; // 启动定时器T0
}
// 定时器T0中断服务函数
void Timer0_ISR() interrupt 1
{
TH0 = (65536 - 50000) / 256; // 重新加载初值(高8位)
TL0 = (65536 - 50000) % 256; // 重新加载初值(低8位)
// 在这里可以添加需要在定时器中断中执行的代码
}
// 延时函数
void Delay(unsigned int ms)
{
unsigned int i;
for (i = 0; i < ms; i++)
{
while (!TF0); // 等待定时器T0溢出
TF0 = 0; // 清除溢出标志
}
}
void main()
{
Timer0_Init(); // 初始化定时器T0
while (1)
{
Delay(1000); // 延时1000ms
// 在这里可以添加需要在主循环中执行的代码
}
}
1.8.4 代码说明
定时器初值:在模式1下,定时器是16位的,最大计数值为65536。如果需要延时50ms,假设单片机的时钟频率为12MHz,机器周期为1μs,那么50ms对应的计数值为50000。因此,定时器的初值为65536 - 50000 = 15536,分别加载到TH0和TL0中。
中断服务函数:在定时器中断服务函数中,重新加载初值,以实现周期性定时。
延时函数:通过循环等待定时器溢出标志TF0来实现延时。
1.8.5 定时器T0模式1中的TF0
在AT89C52单片机中,定时器T0在模式1(16位定时器模式)下,溢出标志TF0的行为和作用是至关重要的。
1. 模式1 的特性
16 位定时器:模式1是一个16位定时器,可以计数从0到65535(0xFFFF)。
初值设置:通过设置TH0(高8位)和TL0(低8位)来设置定时器的初始值。
溢出条件:当定时器从初始值计数到65535时,溢出标志TF0会被硬件自动置位。
2. 溢出标志TF0 的位置
TF0位于定时器控制寄存器TCON中,具体位置如下:
TCON 寄存器:地址为0x88,是一个8位寄存器,用于控制和指示定时器的状态。
TF0:是TCON寄存器的第7位(最高位),即TCON的第7位。
3. TF0 的作用
溢出标志:当定时器T0从其初始值计数到65535时,TF0会被硬件自动置位(变为1)。
中断请求:如果定时器T0的中断使能位ET0被设置为1,并且全局中断使能位EA也被设置为1,那么TF0置位时会触发一个中断请求。
查询标志:在非中断模式下,可以通过查询TF0的状态来判断定时器是否已经溢出。
4. 清除TF0
TF0可以通过以下两种方式清除:
硬件清除:当定时器T0的中断被响应时,TF0会被硬件自动清除。
软件清除:可以通过软件将TF0清零。例如:TF0 = 0; // 清除定时器T0溢出标志
5. 示例代码
完整的示例代码:如何在模式1 下使用TF0 来实现延时功能。
objectivec
#include <reg52.h> // 包含AT89C52的寄存器定义
// 定时器T0初始化函数
void Timer0_Init()
{
TMOD |= 0x01; // 设置定时器T0为模式1(16位定时器)
TH0 = (65536 - 50000) / 256; // 设置定时器初值(高8位)
TL0 = (65536 - 50000) % 256; // 设置定时器初值(低8位)
TR0 = 1; // 启动定时器T0
}
// 延时函数
void Delay(unsigned int ms)
{
unsigned int i;
for (i = 0; i < ms; i++)
{
while (!TF0); // 等待定时器T0溢出
TF0 = 0; // 清除溢出标志
TH0 = (65536 - 50000) / 256; // 重新加载初值(高8位)
TL0 = (65536 - 50000) % 256; // 重新加载初值(低8位)
}
}
void main()
{
Timer0_Init(); // 初始化定时器T0
while (1)
{
Delay(1000); // 延时1000ms
// 在这里可以添加需要在主循环中执行的代码
}
}
6. 代码说明
初始化:在Timer0_Init函数中,设置定时器T0为模式1,并加载初始值。
延时函数:在Delay函数中,通过循环等待TF0置位来实现延时。每次TF0置位后,清除TF0,并重新加载定时器初值。
主循环:在主循环中调用Delay函数,实现周期性延时
1.9 阻塞式延时实现
1.9.1 延时时间估算(假设12MHz晶振)
AT89C52的一个机器周期 = 12个时钟周期 = 1μs(12MHz晶振下)
for(j=0;j<500;j++); 大约消耗 500× 2 ≈ 1000 个机器周期 ≈ 1000μs
外层循环 n 次,总延时 ≈ n × 1000μs
1.9.2 阻塞式延时程序//1ms
objectivec
void delay(uint n)
{
uint i = 0, j = 0;
for(i = 0; i < n; i++)
{
for(j = 0; j <500; j++); // 空循环
}
}
1.9.3 详细解释
1. 8051 的时钟结构:
晶振频率:外部晶振(如 12MHz、11.0592MHz)
时钟周期 = 1 / 晶振频率
例如:12MHz → 1/12μs ≈ 83.33ns
机器周期 = 12 × 时钟周期
因为 8051 把 12 个时钟周期定义为 1 个机器周期
2. 举个例子(12MHz ):
|--------|------------|----------|
| 名称 | 计算方式 | 时间长度 |
| 时钟周期 | 1 / 12MHz | ≈ 83 ns |
| 机器周期 | 12 × 83 ns | 1 μs |
| 指令周期 | 1~4 个机器周期 | 1~4 μs |
3. 实际应用:
定时器初值计算: 如果你想让定时器每 1ms 中断一次:
1000μs / 1μs = 1000 个机器周期
初值 = 65536 - 1000 = 64536
TH0 = 64536 / 256
TL0 = 64536 % 256
1.4 注意
这是阻塞式延时,CPU 不能做其他事情。
如果你用了11.0592MHz 晶振,延时时间会略有不同。
1.10 完整例子2------流水灯
1.10.1 电路原理图

电路原理图由AT89C52单片机、流水灯电路、复位电路和晶振电路组成,实现P1.0~P1.7引脚上的 LED 每 1 秒轮流闪烁一次,其中流水灯为共阴极接法。
1.10.2 控制程序
1 、通过定时器T0 实现延时函数实现
objectivec
//头文件与位定义
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
//定时器T0初始化
void inittimer()
{
TMOD = 0x01;//设置定时器0为模式1(16位定时器)
TH0 = 0xFC;//高8位 定时器周期1 ms
TL0 = 0x18;//低8位 定时器周期1 ms
TR0 = 1;//启动定时器0
ET0 = 1;// 允许定时器0中断
EA = 1;// 打开总中断
}
//延时函数
void Delay(unsigned int ms)
{
unsigned int i;
for (i = 0; i < ms; i++)
{
while (!TF0); // 等待定时器T0溢出
TF0 = 0; // 清除溢出标志
}
}//主函数
void main (void)
{
inittimer();//初始化定时器
uchar i,temp;
//P1.0->P1.7依次点亮---> P1.7->P1.0依次点亮 照此一直循环
while(1)
{
// P1.0->P1.7依次点亮
temp=0x01;
for(i=0;i<8;i++)//左移循环
{
delay(500);
temp=temp<<1;//左移一位
}
// P1.7->P1.0依次点亮
temp=0x80;
for(i=0;i<8;i++)//右移循环
{
delay(500);
temp=temp>>1;//右移移位
}
}
}
//定时器0中断服务函数
void Timer0_ISR() interrupt 1
{
TH0 = 0xFC;
TL0 = 0x18;// 定时器周期1 ms
}
2 通过阻塞式延时函数实现
objectivec
//头文件与位定义
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
//延时函数
void delay(uint n)
{
uchar i;
uint j;
for(j=0;j<n;j++)
for(i=0;i<123;i++);
}
//主函数
void main (void)
{
uchar i,temp;
while(1)
{
temp=0x01;
for(i=0;i<8;i++)//左移循环
{
delay(500);
temp=temp<<1;//左移一位
}
temp=0x80;
for(i=0;i<8;i++)//右移循环
{
delay(500);
temp=temp>>1;//右移移位
}
}
}
通过移位运算实现引脚P1的电平变化从而实现流水灯功能,通过定时器或阻塞式实现延时函数。