第 5 章 MCS - 51 单片机定时器/计数器及其应用
5.1 定时器/计数器的结构与工作方式
5.1.1 定时器/计数器的结构
MCS - 51 单片机通常有两个 16 位的定时器/计数器,即定时器/计数器 0(T0)和定时器/计数器 1(T1),8052 单片机还额外有定时器/计数器 2(T2)。以 T0 和 T1 为例,其结构主要由以下部分组成:
- 16 位计数器:由高 8 位寄存器 THx(x = 0 或 1)和低 8 位寄存器 TLx 组成,用于对输入脉冲进行计数。
- 工作方式寄存器 TMOD:用于设置定时器/计数器的工作方式。TMOD 的低 4 位用于控制 T0,高 4 位用于控制 T1。其各位的含义如下:
- GATE(门控位):GATE = 0 时,定时器/计数器的启动与停止仅由 TRx 位控制;GATE = 1 时,定时器/计数器的启动不仅取决于 TRx 位,还取决于外部中断引脚 INTx 的电平。
- C/T(定时/计数模式选择位):C/T = 0 时,为定时模式,计数器对单片机内部时钟脉冲进行计数;C/T = 1 时,为计数模式,计数器对来自外部引脚 T0(P3.4)或 T1(P3.5)的外部脉冲进行计数。
- M1、M0(工作方式选择位):通过设置 M1、M0 的值,可以选择定时器/计数器的 4 种工作方式。
- 控制寄存器 TCON:用于控制定时器/计数器的启动、停止以及保存溢出标志等。其中与定时器/计数器相关的位有:
- TRx(定时器/计数器运行控制位):TRx = 1 时,启动定时器/计数器;TRx = 0 时,停止定时器/计数器。
- TFx(定时器/计数器溢出标志位):当定时器/计数器计满溢出时,TFx 自动置 1,向 CPU 发出中断请求。在 CPU 响应中断后,硬件自动将 TFx 清零(对于定时器/计数器 0 和 1)。
5.1.2 工作方式
- 方式 0:
- 结构:13 位定时器/计数器,由 THx 的 8 位和 TLx 的低 5 位组成,TLx 的高 3 位未使用。
- 计数范围:最大计数值为 (2^{13} = 8192)。
- 应用场景:早期应用较多,现在因 16 位定时器/计数器(方式 1)更方便,使用逐渐减少。但在一些对计数值要求不高,且希望节省资源的场合仍可使用。
- 方式 1:
- 结构:16 位定时器/计数器,由 THx 和 TLx 组成完整的 16 位计数器。
- 计数范围:最大计数值为 (2^{16} = 65536)。
- 应用场景:是最常用的工作方式,适用于大多数需要定时或计数的场景,如精确的定时控制、较长时间间隔的定时等。
- 方式 2:
- 结构:自动重装初值的 8 位定时器/计数器。TLx 作为 8 位计数器,THx 用于保存初值。当 TLx 计满溢出时,硬件自动将 THx 的值重新装入 TLx,同时 TFx 置 1。
- 计数范围:最大计数值为 (2^{8} = 256)。
- 应用场景:常用于需要精确的固定时间间隔定时的场合,如波特率发生器,因为它可以自动重装初值,无需软件重新设置,保证定时的准确性和稳定性。
- 方式 3:
- 结构:仅适用于 T0。在方式 3 下,T0 被拆分为两个独立的 8 位计数器,TL0 可作为一个独立的 8 位定时器/计数器,TH0 则固定为定时器(对内部时钟脉冲计数),并使用 T1 的 TR1 和 TF1 作为其控制位和溢出标志位。此时 T1 只能用于不需要中断的应用,如作为串行口波特率发生器。
- 应用场景:当需要两个独立的 8 位定时器/计数器时可使用此方式,且在某些情况下能充分利用资源。
5.2 定时器/计数器的基本应用
5.2.1 方式 0 的应用
例:利用定时器/计数器 0 的方式 0 实现 10ms 的定时,假设单片机晶振频率为 12MHz。
ORG 0000H
AJMP MAIN
ORG 000BH
AJMP TIMER0_ISR
MAIN:
MOV TMOD, #00H ; 设置 T0 为方式 0
MOV TH0, #HIGH(8192 - 10000) ; 计算并装入初值高 8 位(12MHz 晶振,1 个机器周期 1μs,10ms 需计 10000 个数)
MOV TL0, #LOW(8192 - 10000) ; 装入初值低 5 位
SETB TR0 ; 启动 T0
SETB ET0 ; 允许 T0 中断
SETB EA ; 开放总中断
SJMP $ ; 主程序原地等待中断
TIMER0_ISR:
MOV TH0, #HIGH(8192 - 10000) ; 重新装入初值高 8 位
MOV TL0, #LOW(8192 - 10000) ; 重新装入初值低 5 位
; 此处可添加定时到后的处理代码,如 LED 闪烁等
CPL P1.0 ; 取反 P1.0 引脚电平,实现 LED 闪烁
RETI ; 返回主程序
END
注意事项:
- 方式 0 是 13 位计数器,计算初值时要注意与 16 位计数器(方式 1)的区别。
- 由于 TLx 只用低 5 位,在装入初值时要确保低 5 位的正确性。
5.2.2 方式 1 的应用
例:利用定时器/计数器 1 的方式 1 实现 50ms 的定时,假设单片机晶振频率为 12MHz。
ORG 0000H
AJMP MAIN
ORG 001BH
AJMP TIMER1_ISR
MAIN:
MOV TMOD, #010H ; 设置 T1 为方式 1
MOV TH1, #HIGH(65536 - 50000) ; 计算并装入初值高 8 位(12MHz 晶振,1 个机器周期 1μs,50ms 需计 50000 个数)
MOV TL1, #LOW(65536 - 50000) ; 装入初值低 8 位
SETB TR1 ; 启动 T1
SETB ET1 ; 允许 T1 中断
SETB EA ; 开放总中断
SJMP $ ; 主程序原地等待中断
TIMER1_ISR:
MOV TH1, #HIGH(65536 - 50000) ; 重新装入初值高 8 位
MOV TL1, #LOW(65536 - 50000) ; 重新装入初值低 8 位
; 此处可添加定时到后的处理代码,如数码管刷新等
CPL P2.0 ; 取反 P2.0 引脚电平,用于简单测试
RETI ; 返回主程序
END
注意事项:
- 方式 1 是 16 位计数器,初值计算要准确,尤其在定时较长时间时,注意计数值不要超出范围。
- 定时时间的精度取决于晶振频率的稳定性。
5.2.3 方式 2 的应用
例:利用定时器/计数器 1 的方式 2 作为波特率发生器,假设单片机晶振频率为 12MHz,波特率为 9600bps。
ORG 0000H
AJMP MAIN
MAIN:
MOV TMOD, #020H ; 设置 T1 为方式 2
MOV TH1, #256 - (12000000 / 12 / 32 / 9600) ; 计算并装入初值(根据波特率计算公式)
MOV TL1, TH1 ; 初值赋给 TL1
SETB TR1 ; 启动 T1
; 此处可添加串口初始化代码
MOV SCON, #50H ; 设置串口工作方式 1,允许接收
SETB REN ; 允许串口接收
SJMP $ ; 主程序等待
END
注意事项:
- 方式 2 自动重装初值,初值计算要准确,特别是在作为波特率发生器时,要根据晶振频率和所需波特率精确计算。
- 由于自动重装初值,不需要在中断服务程序中重新设置初值,但要注意溢出标志的处理。
5.2.4 方式 3 的应用
例:利用定时器/计数器 0 的方式 3 实现两个独立的定时功能,假设单片机晶振频率为 12MHz。
ORG 0000H
AJMP MAIN
ORG 000BH
AJMP TL0_ISR
ORG 001BH
AJMP TH0_ISR
MAIN:
MOV TMOD, #03H ; 设置 T0 为方式 3
MOV TH0, #HIGH(65536 - 20000) ; 为 TH0 装入初值高 8 位(定时 20ms)
MOV TL0, #LOW(256 - 1000) ; 为 TL0 装入初值(定时 1ms)
SETB TR0 ; 启动 TL0
SETB TR1 ; 启动 TH0(此时 TH0 用 T1 的 TR1 控制)
SETB ET0 ; 允许 TL0 中断
SETB ET1 ; 允许 TH0 中断(此时 TH0 用 T1 的 ET1 和 TF1)
SETB EA ; 开放总中断
SJMP $ ; 主程序原地等待中断
TL0_ISR:
MOV TL0, #LOW(256 - 1000) ; 重新装入 TL0 初值
; 此处可添加 TL0 定时到后的处理代码,如控制 LED 闪烁频率
CPL P1.0 ; 取反 P1.0 引脚电平
RETI ; 返回主程序
TH0_ISR:
MOV TH0, #HIGH(65536 - 20000) ; 重新装入 TH0 初值
; 此处可添加 TH0 定时到后的处理代码,如控制数码管显示内容
CPL P2.0 ; 取反 P2.0 引脚电平
RETI ; 返回主程序
END
注意事项:
- 方式 3 仅适用于 T0,且 T1 只能用于不需要中断的应用,如波特率发生器。
- 要分别处理 TL0 和 TH0 的中断服务程序,注意它们的初值设置和定时功能的实现。
5.3 定时器/计数器的扩展应用
5.3.1 计时计数器
原理:通过对外部脉冲的计数,结合定时器/计数器的定时功能,可以实现对事件发生时间的精确测量。例如,测量一个按键按下的时长。
实现:
ORG 0000H
AJMP MAIN
ORG 000BH
AJMP TIMER0_ISR
MAIN:
MOV TMOD, #05H ; 设置 T0 为计数模式,方式 1
MOV TH0, #00H ; 初值设为 0
MOV TL0, #00H
SETB TR0 ; 启动 T0
JB P3.2, $ ; 等待按键按下(假设按键接在 P3.2 引脚,低电平表示按下)
SETB ET0 ; 允许 T0 中断
SETB EA ; 开放总中断
SJMP $ ; 主程序等待中断
TIMER0_ISR:
; 此处可根据计数值计算按键按下时长
; 例如,假设外部脉冲频率已知,通过计数值可算出时间
MOV TH0, #00H ; 重新装入初值
MOV TL0, #00H
RETI ; 返回主程序
END
5.3.2 周期测量
原理:利用定时器/计数器对被测信号的周期进行测量。通过在信号的上升沿或下降沿启动和停止定时器/计数器,根据计数值和定时器的时钟频率计算周期。
实现:
ORG 0000H
AJMP MAIN
ORG 000BH
AJMP TIMER0_ISR
MAIN:
MOV TMOD, #01H ; 设置 T0 为定时模式,方式 1
MOV TH0, #00H ; 初值设为 0
MOV TL0, #00H
JB P3.2, $ ; 等待信号上升沿(假设信号接在 P3.2 引脚)
SETB TR0 ; 启动 T0
JNB P3.2, $ ; 等待信号下降沿
SETB ET0 ; 允许 T0 中断
SETB EA ; 开放总中断
SJMP $ ; 主程序等待中断
TIMER0_ISR:
; 根据计数值计算信号周期
; 假设晶振频率已知,结合计数值可算出周期
MOV TH0, #00H ; 重新装入初值
MOV TL0, #00H
RETI ; 返回主程序
END
5.3.3 频率测量
原理:在固定时间内对被测信号的脉冲个数进行计数,根据计数值和时间计算频率。通常采用定时器/计数器的定时功能确定测量时间,用计数功能对信号脉冲计数。
实现:
ORG 0000H
AJMP MAIN
ORG 000BH
AJMP TIMER0_ISR
MAIN:
MOV TMOD, #05H ; 设置 T0 为计数模式,方式 1
MOV TH1, #HIGH(65536 - 10000) ; 设置 T1 定时 10ms(假设晶振 12MHz)
MOV TL1, #LOW(65536 - 10000)
MOV TH0, #00H ; T0 初值设为 0
MOV TL0, #00H
SETB TR0 ; 启动 T0 计数
SETB TR1 ; 启动 T1 定时
SETB ET1 ; 允许 T1 中断
SETB EA ; 开放总中断
SJMP $ ; 主程序等待中断
TIMER0_ISR:
; 在 T1 定时中断中,根据 T0 计数值计算频率
; 例如,10ms 内的计数值乘以 100 即为频率
MOV TH0, #00H ; 重新装入 T0 初值
MOV TL0, #00H
RETI ; 返回主程序
END
5.3.4 A/D 转换(间接实现)
原理:利用定时器/计数器的定时功能,结合外部电路(如 RC 电路),通过测量 RC 电路的充电或放电时间来间接实现 A/D 转换。不同的模拟电压会导致 RC 电路充电或放电时间不同,通过定时器/计数器测量时间,再根据时间与电压的关系转换为数字量。
实现思路:
- 初始化定时器/计数器为定时模式。
- 控制外部 RC 电路的充电或放电开始。
- 启动定时器/计数器计时。
- 当达到一定条件(如电压比较器输出变化)时,停止定时器/计数器。
- 根据计数值计算对应的模拟电压值。
5.4 定时器/计数器 T2
5.4.1 T2 的工作控制
定时器/计数器 T2 有专门的控制寄存器 T2CON,其各位定义如下:
- TF2(定时器/计数器 2 溢出标志位):当 T2 计满溢出时,TF2 自动置 1。在捕捉方式或自动重装方式下,TF2 必须由软件清零。
- EXF2(定时器/计数器 2 外部标志位):当 EXEN2 = 1 且 T2EX(P1.1)引脚发生负跳变时,EXF2 置 1。