定时/计数器:从原理到实战,解锁单片机精准时序控制 📅 2026/7/14 17:16:52 1. 定时/计数器单片机的时间指挥官第一次接触51单片机时你可能用过delay()函数做延时——让CPU空转循环来消耗时间。这种简单粗暴的方式就像让厨师亲自数秒煮鸡蛋不仅效率低下还会耽误其他工作。而定时/计数器就是为解决这个问题而生的智能闹钟它能精确计时并自动通知CPU解放处理器去处理其他任务。以STC89C52为例其内部集成了两个16位定时/计数器T0/T1和一个看门狗定时器。定时模式和计数模式是它的两种基本工作状态定时模式对内部晶振12分频后的脉冲计数12MHz晶振时每1μs计数值1计数模式检测外部引脚P3.4(T0)/P3.5(T1)的负跳变信号最高频率不超过晶振频率1/24实际项目中我用定时器做过这些事数码管动态扫描2ms间隔PWM调光LED测量红外遥控信号脉宽电机转速测量2. 内部结构深度拆解2.1 核心部件组成定时器的硬件架构就像精密的瑞士手表主要包含这些部件// 寄存器映射示例 sfr TH0 0x8C; // 定时器0高字节 sfr TL0 0x8A; // 定时器0低字节 sfr TMOD 0x89; // 工作模式寄存器 sfr TCON 0x88; // 控制寄存器加法计数器是核心部件其工作流程如下初始化时写入初值如TH00x3C, TL00xB0启动后每个机器周期自动加1计满溢出时置位TF0标志位若中断已开启触发定时器中断2.2 关键寄存器详解TMOD寄存器不可位寻址像是一个功能选择面板位76543210功能GATEC/TM1M0GATEC/TM1M0说明T1门控T1模式选择T0门控T0模式选择GATE位0软件控制TRx1硬件控制INTx引脚TRxC/T位0定时模式1计数模式M1/M0工作方式选择00-方式001-方式110-方式211-方式3TCON寄存器中的关键位TR0/TR1启动/停止定时器TF0/TF1溢出标志中断方式需手动清零3. 四种工作方式实战解析3.1 方式013位定时器这是历史遗留模式兼容8048实际使用较少。计数范围0-8191初始化示例TMOD 0xF0; // 清零T0控制位 TMOD | 0x00; // 设置T0为方式0 TH0 (8192 - 1000) / 32; // 计算初值 TL0 (8192 - 1000) % 32;3.2 方式116位定时器最常用模式计数范围0-65535。50ms定时配置12MHz晶振TMOD 0xF0; // 清零T0控制位 TMOD | 0x01; // 设置T0为方式1 TH0 (65536 - 50000) / 256; // 初值计算 TL0 (65536 - 50000) % 256; ET0 1; // 开启T0中断 EA 1; // 开总中断 TR0 1; // 启动定时器初值计算公式初值 最大计数值 - (定时时间(μs) × 晶振频率(MHz)) / 123.3 方式28位自动重装适合高频定时需求如串口波特率生成TLx作为计数器THx存储重装值TMOD 0xF0; TMOD | 0x02; // 设置T0为方式2 TH0 256 - 200; // 重装值 TL0 256 - 200; // 初始值3.4 方式3双8位定时器仅T0可用将T0拆分为两个独立8位定时器TL0使用T0原有控制位TH0占用T1的TR1和TF1资源4. 定时与计数模式对比通过TMOD寄存器的C/T位切换模式本质区别在于时钟源特性定时模式计数模式时钟源内部晶振12分频外部引脚(P3.4/P3.5)应用场景延时、PWM生成转速测量、脉冲计数最大频率1MHz(12MHz晶振)500KHz(12MHz晶振)典型应用1ms定时中断编码器信号采集我曾用计数模式实现过红外遥控解码将定时器配置为计数方式测量两个下降沿之间的计数值来计算脉宽。5. 精准时序控制实战案例5.1 数码管动态显示核心要点利用定时中断实现扫描刷新unsigned char displayBuff[4]; void Timer0_ISR() interrupt 1 { static unsigned char pos 0; TH0 (65536 - 2000) / 256; // 2ms定时 TL0 (65536 - 2000) % 256; P2 0xFF; // 关闭显示 P1 segCode[displayBuff[pos]]; // 段选 P2 posMask[pos]; // 位选 pos (pos 1) % 4; }5.2 精准延时函数替代不精确的delay()void delay_ms(unsigned int ms) { TMOD 0xF0; TMOD | 0x01; // T0方式1 while(ms--) { TH0 (65536 - 1000) / 256; TL0 (65536 - 1000) % 256; TR0 1; while(!TF0); TR0 0; TF0 0; } }5.3 频率计实现结合定时和计数模式设置T1为计数模式统计外部脉冲配置T0为1秒定时定时到后读取TH1/TL1计数值6. 中断与查询方式抉择两种处理溢出事件的方式各有优劣中断方式推荐void Timer0_Init() { TMOD 0xF0; TMOD | 0x01; TH0 (65536 - 50000) / 256; TL0 (65536 - 50000) % 256; ET0 1; EA 1; TR0 1; } void Timer0_ISR() interrupt 1 { TH0 (65536 - 50000) / 256; // 重装初值 TL0 (65536 - 50000) % 256; // 处理代码... }查询方式while(1) { if(TF0) { TF0 0; TH0 (65536 - 50000) / 256; TL0 (65536 - 50000) % 256; // 处理代码... } }选择建议高精度需求用中断简单任务可用查询多个定时器组合使用时注意中断优先级7. 进阶技巧与避坑指南初值计算常见错误忘记12分频关系导致时间不准方式2未正确设置THx重装值中断中漏掉重装初值方式1提升精度的方法使用12T模式单片机时选择11.0592MHz晶振采用方式2自动重装减少误差补偿中断响应时间通常多计2-3个机器周期多任务处理框架void Timer0_ISR() interrupt 1 { static unsigned int ticks 0; TH0 (65536 - 50000) / 256; TL0 (65536 - 50000) % 256; ticks; if(ticks % 20 0) task1(); // 每秒执行 if(ticks % 10 0) task2(); // 每0.5秒执行 }遇到过一个典型问题使用方式1时发现定时时间比预期长最终发现是中断服务函数中漏掉了重装初值的操作。这也让我养成了在编写中断服务程序时第一时间处理初值重装的好习惯。