您现在的位置是:首页 >技术杂谈 >51单片机--快速了解单片机的定时器与中断网站首页技术杂谈
51单片机--快速了解单片机的定时器与中断
快速开始
有什么用
若以传统的软件计时来实现,我们使用函数等待是让cpu在等待的这段时间干等,而不去干其它任何事,就比如你在烧水的时候,明明还可以去刷会儿手机,你却选择了只盯着看什么时候水烧开。
这个程序实现让我们的cpu每隔1ms进行中断查询来进行中断的相关操作,而我们的时钟周期是1us,所以我们cpu剩余的999us都可以用来干其它事。若是普通的延迟函数,这999us就会干等,而这里我只让他做了一件事,在需要的时候关中断。(当然实际上中断并不可能1us就完成,这里只是举个例子,来阐述这个原理)
启动
我们先快速启动一个代码,来帮助我们了解51单片机的计时器和中断原理。
如果还不懂怎么在单片机中烧录代码的,可以关注上一篇文章 51单片机原理图理解
首先我们要明确,单片机的时钟周期是1us(1/12MHZ)。
这个程序实现了51单片机的T0定时器的初始化,并通过定时器溢出中断,使我们的LED灯循环闪烁,同时通过关闭中断,使我们的小灯停止闪烁。
#include <REGX52.H>
void initTime(){
TMOD &= 0xF0; //设置定时器模式,逻辑与低四位置0,不影响高四位 TMOD=TMOD&1111 0000
TMOD |= 0x01; //设置定时器模式,逻辑或最低位置1,设定工作模式为方式1
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志,中断标志位,防止刚卡死就产生中断,是计数器溢出标志位
TR0 = 1; //定时器0开始计时
ET0=1; //定时器中断打开
EA=1; //总中断打开
PT0=0; //优先级,默认就是0可以不写,0为低优先级,1为高优先级
}
void main(){
initTime();
while(1){
if(P3_0==0){//调用按键
while(P3_0==0);
ET0=~ET0;//关闭和打开定时器中断
}
}
}
void LEDauto() interrupt 1//中断函数,中断发生时执行这里的函数,执行完毕后返回main函数继续执行
{
static unsigned int count;
TL0 = 0x18;
TH0 = 0xFC;
count++;
if(count==200){
P2_0=~P2_0;
count=0;
}
}
后续
好了,本次教学结束,如果你对中断和定时器的原理感兴趣的话可以继续往下看。
51单片机的中断
用通俗的语言解释中断就是当一件事正在进行时,检测到另一间事,立马去做,做完后再回来,继续做走之前的事。
1.51单片机对应的一些中断结构图

一般有INT0、INT1、T0、T1、RX、TX这几个中断
对应P32、P33、P34、P35、P30、P31这几个端口,具体见手册,这里只解释我们用到的T0
- INT0:P3.2
- INT1:P3.3
- T0:P3.4,片内定时/计数器 T0 溢出中断请求标志。
- T1:P3.5,片内定时/计数器 T1溢出中断请求标志。
- RX:P3.0
- TX:P3.1
这里我们使用的事T0计时器的中断,而打开这个中断我们可以从图中看到我们需要TF0(定时器溢出中断标志)、ET0(定时器中断启用标志)、PT0(优先级设置标志)的导通。
- TF0:每次定时器溢出时置1,定时器为16位计数,最大值为216,也就是说定时器单次最大间隔发出中断请求的时间间隔为216us。
- ET0:用来决定是否打开定时器中断。
- PT0:用来决定是将定时器的优先级置位低级自然优先级还是高级自然优先级。
(ps.下面是普中的对应的上述结构的截图,大家可以看一下,基本明白将这些模块置1,中断就可以开启即可)
2.中断寄存器
中断允许控制

中断请求标志(TCON)

这里TR0和TR1对应的就是我们T0和T1计时器启动的标志,可以视为开关。
中断优先级

中断号

中断后就是发生中断的时候,我们会抛出一个中断号,我们函数获取这个中断号来导向我们的中断服务函数
中断响应条件
①中断源有中断请求;
②此中断源的中断允许位为 1;
③CPU 开中断(即 EA=1)
在代码中嵌入该代码打开中断
EA=1;//打开总中断开关
EX0=1;//开外部中断 0
IT0=0/1;//设置外部中断的触发方式
中断服务函数
void int0() interrupt 0 using 1//interrupt 是一个关键字,表示 51 单片机中断。后面的“0”是中断号,外部中断 0 中断号为 0,
{
//编写用户所需的功能代码
}
定时器
实质是自加1的计数器(16位)
由高 8 位和低 8 位两个寄存器 THx 和 TLx 组成。根据时钟周期自加1,当加到计数器为全 1 时,再输入一个脉冲就使计数器回零,且计数器的溢出使相应的中断标志位置 1,向 CPU 发出中断请求,此时就会去执行我们编写的中断服务函数。
这时候如果处于定时模式,代表定时已满,处于计时模式代表时间已到。
我们51单片机用到的事T1和T0定时器,这里实验我们用的是T0计时器。
结构图

定时器配置
①对 TMOD 赋值,以确定 T0 和 T1 的工作方式,如果使用定时器 0 即对 T0 配置,如果使用定时器 1 即对 T1 配置。(TMOD的具体内容需要见51单片机的对应的详细解释的配置文档)
②根据所要定时的时间计算初值,并将其写入 TH0、TL0 或 TH1、TL1。
(计算初值,让达到溢出水平从而实现计时,比如这里的要计时1ms就是用65536-1000=64436的16进制得到的)

③如果使用中断,则对 EA 赋值,开放定时器中断。
④使 TR0 或 TR1 置位,启动定时/计数器定时或计数。
实例
void time0_init(void)
{
TMOD|=0X01;//选择为定时器 0 模式,工作方式 1
TH0=0XFC; //给定时器赋初值,定时 1ms
TL0=0X18;
ET0=1;//打开定时器 0 中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
后续
当然如果要深入了解中断的内部实现(怎么保存断点、怎么恢复、怎么转去执行另一段函数的,如何进行取址的。。。),大家可以去找编译原理的相关书籍去了解,这边看情况抽时间也会去进行整理。
如果还有什么疑问,欢迎大家评论区补充。





U8W/U8W-Mini使用与常见问题解决
QT多线程的5种用法,通过使用线程解决UI主界面的耗时操作代码,防止界面卡死。...
stm32使用HAL库配置串口中断收发数据(保姆级教程)
分享几个国内免费的ChatGPT镜像网址(亲测有效)
Allegro16.6差分等长设置及走线总结