第 7 篇:定时器——精确控制时间
之前我们一直用 delay() 延时——两个 for 循环空转,精度差、阻塞 CPU。定时器彻底解决这个问题。
1. 定时器原理
STC89C52 的定时器本质是一个 16 位计数器:
TH0(高 8 位) TL0(低 8 位)
┌──────┬──────┐
│ 0x3C │ 0xB0 │ = 0x3CB0 = 15536
└──────┴──────┘
每个机器周期(12 个时钟周期)计数器 +1
从 15536 加到 65536(溢出)= 50000 个机器周期
50000 × (12/11.0592MHz) = 50000 × 1.085μs ≈ 54ms
2. 初值计算
// 定时 50ms(晶振 11.0592MHz,12T 模式)
// 计数次数 = 50ms / (12 / 11059200) = 46080
// 初值 = 65536 - 46080 = 19456 = 0x4C00
TH0 = 0x4C;
TL0 = 0x00;
为什么用 11.0592MHz?因为它能被波特率整除——串口通信不会产生误差。
3. 用定时器实现精确 LED 闪烁
unsigned int count_50ms = 0; // 50ms 计数器
void timer0_init(void)
{
TMOD &= 0xF0; // 低 4 位清零(定时器 0)
TMOD |= 0x01; // 方式 1(16 位定时器)
TH0 = 0x4C; // 初值 = 19456
TL0 = 0x00;
ET0 = 1; // 使能定时器 0 中断
TR0 = 1; // 启动定时器 0
EA = 1;
}
void timer0_isr(void) interrupt 1
{
TH0 = 0x4C; // 重装初值
TL0 = 0x00;
count_50ms++;
if (count_50ms >= 20) { // 20×50ms = 1 秒
count_50ms = 0;
LED = ~LED; // 每秒翻转一次
}
}
void main()
{
timer0_init();
LED = 1;
while (1) {
// 主循环可以干别的事了
// 数码管扫描、按键检测...全塞这里
}
}
4. 用定时器做数码管动态扫描
前面第 4 篇我们用的 delay() 做扫描——占 CPU。用定时器才是正道:
void timer0_isr(void) interrupt 1
{
TH0 = 0xFC; // 定时 1ms
TL0 = 0x66;
display_scan(); // 每 1ms 切换一位数码管
// 4 位数码管 = 4ms 一轮 = 250Hz 刷新率 → 完全看不到闪烁
}
5. 小结
- 定时器 = 硬件计数器,溢出时触发中断
- 初值 = 65536 - (定时时间 / 机器周期)
- 用定时器 + 计数变量替代 delay(),CPU 利用率飙升
- 数码管扫描、按键检测、LED 闪烁全可以挂在定时器中断里
下一篇:串口通信——让单片机"说话"
下一篇:串口通信——让单片机"说话"
——定时器&spm=1001.2101.3001.5002&articleId=162881763&d=1&t=3&u=ee7153706f6e40d78ce37b58d9f71bae)
8817

被折叠的 条评论
为什么被折叠?



