最近在B站学江协的51单片机教程,其中 [7-1] 定时器 一节看了一遍完全没懂,后来又去找了些其他视频和书籍,最终算是知道怎么用定时器了。
本篇内容即为我在这部分的学习笔记。
参考的学习资源:
[1] B站相关视频教程 (这个是我觉得讲的很详细的,看完后基本就懂了)
[2] STC89C52RC pdf资料
[3] 《51单片机 原理及应用 (第4版) 》
目录
一、定时器 / 计数器
用于定时或计数的寄存器。使用时同一时刻内只能使用其中一种功能。
51单片机共有两个16位定时/计数器,叫T0和T1。T0又由两个8位寄存器TH0和TL0构成,同理,T1也由两个8位寄存器TH1和TL1构成。
1.原理

源自 pdf手册
刚才说一个定时器由俩寄存器构成。单片机上电后,两个寄存器的初始值都为0x00。定时器启动后,每隔一个机器周期,寄存器值会自动加1,顺序是先加TL低位寄存器(Timer Low),低位加满后自动进位到TH高位寄存器(Timer High)。由于两寄存器总共16位,所以加到65535后,再加就会导致数值溢出(进位),从而使TH和TL都清零。一次溢出代表一次定时的结束(定时时间就通过溢出来判断)。
51单片机中,一个机器周期固定由12个时钟周期组成。因此,如果使用6MHz的时钟频率,一个机器周期为2μs;如果使用12MHz的时钟频率,一个机器周期就为1μs
怎么限定一次定时的时长呢:修改寄存器初值。以12MHz频率为例,一次溢出需要65536个机器周期,也就是65.536ms。因为溢出才能代表定时的结束,所以两寄存器的末值只能是65535。那么我们就只能通过修改寄存器初始值来规定定时时长。
比如要设置一个50ms的定时器,我们在初始化定时器时可以赋寄存器初值为15536(转16进制为3cb0),这样从初到末,总共经过的时长就为50000个机器周期,也就是50毫秒。
2.编程步骤(以定时器为例)
使用定时器的步骤有:
报备定时器 → 赋初始值 → 启动定时器 → 等待溢出 → 溢出后重置初值→ 清溢出
(1) 报备
定时器报备,其实就是设置定时器的功能。有一个专门的寄存器TMOD(定时模式寄存器)用于设置这些功能。此寄存器每4位控制一个定时器。

GATE:设置启动方式
置0时,只需要TR(第(3)步有说明)为1时就可以启动定时器。
置1时,需要TR和外部中断源INT(之后中断系统有说明)均为1时才能启动。
C/T:设置功能为定时或计数。置0时为定时器,置1为计数器。
M1&M0:模式选择。具体如下(以定时器0的设置为例):

一般常用16位定时器/计数器模式,即M1置0,M0置1。
例如,设置一个定时器0,不使用外部中断源,使用16位模式:
TMOD = 0x01;
(2) 赋初值
赋TH和TL寄存器的初始值,也就是规定定时时长。比如刚才提到的50ms定时器的设置:
TH0 = 0x3c; //二进制0011 1100
TL0 = 0xb0; //二进制1011 0000
//00111100 10110000 = 十进制15536
这样做就给定时器赋了个15536的初始值。
(3) 启动
位于TCON(定时器/计数器控制寄存器,可按位寻址)中的TR位用于控制定时/计数器的启停。(由于TCON更多与中断相关,其他TCON寄存器的信息,在之后中断系统部分有说明)
TR0 = 1; //打开定时器0的开关
(4) 等待
同样位于TCON的TF位用于记录定时器是否溢出。当定时器溢出后,TF位会自动变为1。我们只需要在代码中检测TF位是否为1,就可以确定定时时间是否已到。需注意:在TF变为1后,需要手动将TF重置为0,保证下次正常定时。
(5) 重置初值
若需要定时较长时间,比如想定1秒的时间,但每次定时周期只有50ms,这时就需要连续(循环)定时20次。而每次50ms结束时,需要重置寄存器的初值,保证下次定时还为50ms(而不是溢出后变回0x0000,65.536ms)。所以每循环一次要为TH和TL重置初值。
(6) 清溢出
即刚才说的,每次溢出后,需要将TF手动重置为0。其实置0的操作可以不用显式地写“TF0 = 0;”来清除。若使TH0和TL0重新赋值,这个操作就已经隐式地清除了TF0。
所以,虽然有些代码中没有显式地写TF0 = 0;来清除TF0,但是通过重新赋值TH0和TL0的操作,TF0已经被清除了。这是51中断处理的一个常见做法,可以简化代码并提高效率。
总结:
基本概念:51单片机有两个16位的定时/计数器T0和T1,每个由两个8位寄存器(TH和TL)组成。这些寄存器用于定时或计数,同一时刻只能使用其中一种功能。
工作原理:上电后,寄存器初始值为0。定时器启动后,每隔一个机器周期,寄存器值自动加1,直到溢出(从65535回到0),表示一次定时结束。通过修改寄存器的初始值可以设定定时时长。
编程步骤:
报备:通过TMOD寄存器设置定时器功能,包括启动方式(GATE)、功能选择(C/T)和模式选择(M1和M0)
赋初值:设置TH和TL寄存器的初始值,确定定时时长
启动:通过TCON寄存器中的TR位控制定时器的启动
等待:通过TCON寄存器中的TF位检测定时器是否溢出
重置初值:需要连续定时时,每次定时结束后需要重置TH和TL寄存器的初值
清溢出:每次溢出后,需要手动将TF位重置为0,以保证下次正常定时
二、中断系统
中断就是在正常执行主程序时因某些中断条件触发,导致CPU停止当前运行的程序,转去执行中断条件下的程序,执行完后再返回刚才主程序中断的位置。以下是中断的运行原理和步骤:
1.概念及编程步骤
(1) 中断源
要让中断发生需要中断源。共有5种情况(中断源)可中断CPU当前执行的程序,分别如下:
外部中断INT0(P3.2口),外部中断INT1(P3.3口),定时器T0,定时器T1,串口中断源
(2) 中断允许
用于规定哪些中断源可以触发中断。中断允许是使用专门的中断允许寄存器IE设置的,IE的每个位如下:

IE就相当于各中断源的开关,哪位为1,其对应的中断允许就打开。
(3) 中断请求
每个中断源对应了一个中断请求的形式和中断请求标志。在达到特定条件后,中断源会向CPU发出中断请求,同时改变中断请求标志的状态。CPU就根据中断请求标志的状态来判断是否有中断。不同中断源的请求形式如下:
| 中断源 | 请求形式 |
| 外部中断INT0 | P3.2口低电平或下降沿 |
| 定时器T0 | 溢出 |
| 外部中断INT1 | P3.3口低电平或下降沿 |
| 定时器T1 | 溢出 |
| 串口 | 总线接受/发送数据结束 |
中断请求标志则是存放在TCON寄存器内的B7/B5/B3/B1位。比如定时器T0的溢出,同时会带动TF0自动置1,代表中断请求发出了。
关于外部中断源,分为两种请求形式:下降沿(由高电平转低电平)触发和低电平触发。这两种触发方式也由TCON来设置。例如要设置INT0的请求形式为下降沿触发,则需要把TCON的IT0设置为1。
TCON的信息如下:

(4) 中断处理
在某中断源发出中断请求(中断请求标志位变成1)后,CPU首先会保存当前的断点(当前正执行的语句位置),然后开始执行中断服务程序。中断服务程序的格式如下:
函数返回类型 函数名(参数) interrupt 中断号 {
函数内容
}
e.g.
void timer0() interrupt 1 {
/*...content...*/
}
可以看出中断服务程序的格式除了普通函数有的之外,还有一个表示这是中断程序的单词interrupt,以及一个中断号(中断查询次序号)。
在普通程序中,我们要执行一个函数,就是直接在代码行中加入函数名来跳转。而在中断程序中,中断函数的跳转不需要在代码行中写出,而是中断请求发出后,自动根据中断号跳转到对应中断程序。每个中断源对应的中断号如下:
| 中断源 | 中断号 |
| 外部中断INT0 | 0 |
| 定时器T0 | 1 |
| 外部中断INT1 | 2 |
| 定时器T1 | 3 |
| 串口 | 4 |
完成中断程序的执行后,CPU重新找到断点的地址,然后返回到原程序继续向下执行。
总结:
中断源:中断系统允许在特定条件下暂停当前程序,转而执行中断服务程序。有五种中断源可以触发中断:INT0、INT1、T0、T1和串口
中断允许:通过中断允许寄存器IE来控制哪些中断源可以触发中断。IE寄存器的每一位对应一个中断源,设置为1表示允许对应的中断源触发中断
中断请求:满足特定条件时,中断请求会被发出,并改变中断请求标志的状态。CPU根据这些标志来判断是否有中断需要处理。中断请求标志存放在TCON寄存器中
中断处理:当一个中断源发出中断请求后,CPU会保存当前断点,然后执行相应的中断服务程序。中断服务程序的格式包括关键字interrupt和中断号,中断号对应于触发中断的中断源
中断服务程序执行完毕后,CPU会返回到之前保存的断点位置,继续执行原来的程序。

2.中断优先级
如果在某一时刻发生了两个中断,或是一个中断正在执行,这时另一个中断请求发出,这种情况该执行哪个中断就涉及到不同中断的优先级顺序了。
(1) 自然优先级
51单片机有一个自然优先级,即单片机会按照硬件电路实现的默认顺序来响应中断,这个默认中断优先级顺序是根据中断号来排序的。具体的规则和自然优先级顺序如下图所示:

如果多个中断同时发生(且没有手动设置优先级),CPU会根据这个顺序来决定先响应哪个中断。
(2) 手动修改优先级
当你的程序有某些特殊需求时(如需要定时器的计时非常准确),就可以通过IP(中断优先级寄存器)来手动修改优先级顺序。

其中,每位名字的第一个字母P为Priority (优先级)
第二个字母则分别是:S (Serial 串口) ,T (Timer 定时器),X(eXternal 外部中断)
(比如定时器1就是PT1,外部中断0就是PX0)
通过分别给某位赋值1,就能设置此位为高优先级
如果同时设置了两个高优先级,那么中断的执行顺序就是先按自然优先级排序高优先级(置1的)中断,再同样按自然优先级排序低优先级(置0的)。比如给两个定时器置1,那中断顺序就为:T0, T1, INT0, INT1, Serial
如果想设置更多级优先,可以使用IPH寄存器。其操作与IP相同,只是设置的优先级能更高


(3) 中断嵌套
这张图就很好描述了中断嵌套,不再赘述:

取自上述B站教程
--END
767





