51单片机定时器中断详解

51单片机定时器中断详解目录 1 什么是定时器 2 什么是中断 3 什么是定时器中断 4 配置定时器 4 1 学习建议 4 2 查看手册定时器章节 4 3 定时时间计算 4 4 示例代码 5 配置定时器 0 的中断 5 1 查看手册中断章节 5 2 示例代码 6 定时器 0 中断配置完整代码示例 7 定时器实现 50 亮度的 LED 8 定时器中断实现呼吸灯

大家好,我是讯享网,很高兴认识大家。

目录

1.什么是定时器

2.什么是中断

3.什么是定时器中断

4.配置定时器

4.1学习建议

4.2查看手册定时器章节

4.3定时时间计算

4.4示例代码

5.配置定时器0的中断

5.1查看手册中断章节

5.2示例代码

6.定时器0中断配置完整代码示例

7.定时器实现50%亮度的LED

8.定时器中断实现呼吸灯

9.定时器中断搭建前后台任务系统

10.定时器中断实现无延时按键消

1.什么是定时器

        定时器,顾名思义,就是用来定时的;计数器,顾名思义,就是用来计数的。初学的小伙伴就会有疑问了,为什么定时器又叫计数器呢?其实就是同一个外设能用作两种功能罢了。

2.什么是中断

        中断即中途打断。因某条件触发而打断当前的事,转而去干别的事情,完后回来继续干。单片机的中断也同理:


讯享网

3.什么是定时器中断

        上面的这个比例跟单片机的定时器中断是一样的,首先我们在程序上配置定时器(设闹钟),然后程序进入主循环(睡觉)等待中断的到来,若中断标志位被硬件置一(闹钟响了),程序则跳转到中断服务函数执行里面的代码(弹射起来关闹钟),然后回到主循环内被中断的地方继续执行(继续睡觉)。

4.配置定时器

4.1学习建议

        在这里我想给初学的小伙伴一个建议,那就是一定要学会找手册读手册!点开这个链接去STC官网下载对应型号的芯片用户手册,人家的手册写的很详细,上面那个中断的解释就是从里面截取的。手册里什么都有,外设的说明,寄存器说明,应用举例,汇编代码以及C代码示例等等。

        在学习的时候,你如果是看的人家的视频教程,可以边看视频边对照着手册学习。就算是以后学STM32也是一样,一定要学会找手册,读手册!只有这样你才能学的明白,学的透彻,打好基础!

4.2查看手册定时器章节

        1.先看第一页,信息量巨大,解释了为什么定时器又是计数器,以及其本质(提示:单片机原理课要考哦!)

        其实没有高亮出的那条也很有意思,因为51单片机只有3个定时器(定时器0,1,2),当定时器1用作波特率发生器时,可能导致定时器不够用了,这时定时器0就可以配置到工作模式3:双8位定时器/计数器,即拆分成两个8位定时器,只不过要从定时器1借用中断标志位。

        2.紧接着手册里面列出了与定时器相关的寄存器,由于我们要配置的是定时器0中断,所有我就高亮出了只与定时器0相关的位与寄存器。看图:

也就只有TCON寄存器的TF0位,TR0位TMOD寄存器的低四位寄存器TL0寄存器TH0。我们需要配置关心的也就这几位而已。

        3.在这里我就先贴出定时器0的结构图,好对照,看看上面涉及的位与寄存器在下图哪里。

        4.我们接着手册看,第二页就开始讲解每个寄存器位的作用,首先是TCON寄存器,请对照上图。

TF0解析:他说TF0为定时器/计数器溢出中断标志,这个就类比是闹钟响了嘛,系统会不断的查询此位,如果TF0=1,就会跳转到中断服务函数。他说T0被允许计数:看上图,其实就是control那个开关闭合,前面的脉冲能输入到寄存器TL0,每来一个脉冲,TL0就加一,每当TL0计数到2^8=256后TH0就加一,这样就组成一个16位的计数器,最大能计到2^16=65536。计到最大值后就溢出了嘛,此时TF0就会被硬件置一产生溢出中断。

TR0解析:他说当GATE=0,TR0=1时定时器0就开始计数,对应上图:GATE=0,经过非门(取反)或门(只要有输入为1,输出则为1)输入1,那么输出则为1,INT0位无效。由于或门输出接到了与门(只有输入都为1,输出才为1)输入,与门的另一个输入TR0也=1,那么与门输出=1,control开关闭合,前面的脉冲才能输入到TL0。

    总结TCON初始配置: TF0=0;TR0=1;

         5.继续看手册,TMOD寄存器,也是需要对照定时器0结构图看哦,这里每一位他都讲的很清楚了,我就不再扣了。只是需要注意这个寄存器是不可位寻址的!不知道什么意思请百度

我后面的示例代码配置的是模式2:8位自动重装载所以TMOD=0x02;

        6.然后手册就对定时器的四种模式进行了解析其实也就是TL0与TH0的组合。都用则是16位,最大计数到65536,但是这样的话就不能硬件自动重装载初始值。只用TL0用来计数的话,就只有8位,最大计数到256发生溢出中断,此时就能把TH0的值自动硬件装载到TL0。也可以TL0只用低5位,TH0全用。

TL0跟TH0是可以赋初值的,比如定时器配置成工作模式2:8位自动重装载,你想计数100就溢出,那么往TL0写入256-100=156就🆗。TH0为重新装载的值,如果你想要每计数100就发生溢出中断,那么TH0也要写入156。

4.3定时时间计算

        假如我们配置定时器0工作在模式1:16位定时/计数模式,每1ms产生一中断。

        SYSCLK即晶振频率,假如用的是12MHZ晶振,那么SYSclk=12MHZ,读下图黄色高亮部分,默认是12T模式,则计数脉冲频率=SYSclk/12=1MHZ。对应计数周期即为1us,TL0每1us自加1。我们想要定时1ms,那计数器初值是不是等于65536-1000?那么TL0=(65536-1000)%256;TH0=(65536-1000)/256。

4.4示例代码

示例:定时器0中断

晶振频率:12Mhz

工作模式:2

定时时间:100us

/*定时器0初始化*/ void Timer0_Init() { TMOD=0x02; //8位自动重装载定时器/计数器模式 TH0=156; //重装载值 TL0=156; //定时器初始值 0~256 TF0=0; //清定时器0中断标志位 ET0=1; //开定时器0中断 EA=1; //开总中断 TR0=1; //打开定时器0 }

讯享网

注:ET0与EA是下节内容,先不管。

5.配置定时器0的中断

5.1查看手册中断章节

        1.先理解一下啥是中断嵌套

      

  

        2.中断查询次序即默认中断优先级。在相同优先级下,将按默认优先级处理

        3.中断号是啥?有啥用?其实中断查询次序号就是中断号,中断号是用来标明中断服务函数的,告诉系统这个函数是中断服务函数,发生中断后你就跳转到这个函数执行里面的代码。

        4.中断结构图,看下面图里红色部分

ET0:就一个单独的开关,默认是断开状态。前面定时器0的溢出中断信号作为开关的输入

EA:所有中断信号的总开关,默认也是断开状态。

只有这两个开关都闭合,定时器01的溢出中断信号才能被系统检测到,单片机才会执行相应的中断服务函数。

中断优先级控制寄存器:IP,XICON,IPH。可以看到默认都设置到了最高优先级中断1,1。这里具体配置就不再详解,请自行阅读手册。

5.综上所述,我们只需要闭合开关ET0和EA就行使能定时器0中断。那我们继续看手册这两个位位于哪个寄存器吧,具体需要怎么配置。

可以看到,这两个控制位位于中断允许寄存器IE,这个寄存器是可位寻址的,ET0=1,EA=1就能使能定时器0中断

5.2示例代码

可以看到就只是在前面示例代码的基础上加上ET0=1;EA=1;允许定时器0中断而已。

讯享网/*定时器0初始化*/ void Timer0_Init() { TMOD=0x02; //8位自动重装载定时器/计数器模式 TH0=156; //重装载值 TL0=156; //定时器初始值 0~256 TF0=0; //清定时器0中断标志位 ET0=1; //开定时器0中断 EA=1; //开总中断 TR0=1; //打开定时器0 }

6.定时器0中断配置完整代码示例

此示例代码只是配置了定时器0在工作模式2下每100us中断一次,不做任何任务。

/* STC89C52RC定时器中断测试 * * @author:NachoNEKO * @date:2023/10/30 * @brief:STC89C52RC定时器0中断测试 / #include <STC89C5xRC.H> /变量声明*/ /*函数声明/ /*定时器0初始化*/ void Timer0_Init() { TMOD=0x02; //8位自动重装载 TH0=156; //自动重装载值 TL0=156; //装填初值 TF0=0; //清定时器0中断标志位 ET0=1; //开定时器0中断 EA=1; //开总中断 TR0=1; //打开定时器0 } void main() { Timer0_Init(); while(1) { } } void Timer0_irt() interrupt 1 //定时器0中断 0.1ms/次 { }

7.定时器实现50%亮度的LED

讯享网/* STC89C52RC定时器中断测试 * * @author:NachoNEKO * @date:2023/10/30 * @brief:STC89C52RC定时器0中断测试 / #include <STC89C5xRC.H> /变量声明*/ sbit LED = P2^0; //#define LED P20 /*函数声明/ /*定时器0初始化*/ void Timer0_Init() { TMOD=0x02; //8位自动重装载 TH0=156; //自动重装载值 TL0=156; //装填初值 TF0=0; //清定时器0中断标志位 ET0=1; //开定时器0中断 EA=1; //开总中断 TR0=1; //打开定时器0 } void main() { Timer0_Init(); while(1) { } } void Timer0_irt() interrupt 1 //定时器0中断 0.1ms/次 { static unsigned int counter1=0; if(++counter1==100) counter1=0; //10ms if(counter1<50) LED=0; //模拟100HZ 50%占空比PWM else LED=1; }

8.定时器中断实现呼吸灯

在上面代码的基础上,思考,若让比较值慢慢变化起来会达到什么效果?怎样才能让比较值逐渐变起来?

解析:定义一个全局变量compare代替50,compare在主循环里面每10ms自加/减一次。compare自加到100则自减,自减到0则自加,如此循环。是不是就实现呼吸灯的效果了?

/* STC89C52RC定时器中断测试 * * @author:NachoNEKO * @date:2023/10/30 * @brief:STC89C52RC定时器0中断测试 / #include <STC89C5xRC.H> /变量声明*/ sbit LED = P2^0; //#define LED P20 bit turn_flag=1; //呼吸状态翻转标志 unsigned char compare=0; /*函数声明/ void delay_ms(unsigned char xms); void LED_Breathing(void); /*定时器0初始化*/ void Timer0_Init() { TMOD=0x02; //8位自动重装载 TH0=156; //自动重装载值 TL0=156; //装填初值 TF0=0; //清定时器0中断标志位 ET0=1; //开定时器0中断 EA=1; //开总中断 TR0=1; //打开定时器0 } void main() { Timer0_Init(); while(1) { LED_Breathing(); //呼吸灯 delay_ms(10); //阻塞延时10ms } } void Timer0_irt() interrupt 1 //定时器0中断 0.1ms/次 { static unsigned int counter1=0; if(++counter1==100) counter1=0; //10ms if(counter1<compare) LED=0; //模拟100HZ 50%占空比PWM else LED=1; } void LED_Breathing(void) //呼吸灯 { if(turn_flag){ compare++; if(compare>100) turn_flag=0; } else{ compare--; if(compare<1) turn_flag=1; } } void delay_ms(unsigned char xms) { while(xms--){ unsigned char data i, j; i = 2; j = 199; do { while (--j); } while (--i); } }

9.定时器中断搭建前后台任务系统

        现在我们再思考,如果上述的主循环内还有其他业务函数,此业务函数要求每5ms被执行一次,那么此处的10ms阻塞延时函数还能用嘛?程序应该如何改才能实现10ms渐变呼吸灯的同时5ms执行一次此业务函数呢?

模拟前后台任务系统:这里把定时器中断服务函数当作后台,主要负责分配调度任务;主函数当作前台处理任务。

讯享网/* STC89C52RC定时器中断测试 * * @author:NachoNEKO * @date:2023/10/30 * @brief:STC89C52RC定时器0中断测试,利 * 用定时器中断搭建前后台系统,充分释放C51性能 / #include <STC89C5xRC.H> /变量声明*/ sbit LED1= P2^0; //#define LED P20 sbit LED2= P2^1; bit turn_flag = 1; bit task2_flag=0; bit task3_flag=0; unsigned char compare=0; /*函数声明/ void LED_Breathing(void); /*定时器0初始化*/ void Timer0_Init() { TMOD=0x02; //8位自动重装载 TH0=156; //自动重装载值 TL0=156; //初始值 TF0=0; //清定时器0中断标志位 ET0=1; //开定时器0中断 EA=1; //开总中断 TR0=1; //打开定时器0 } void main() { Timer0_Init(); while(1) { /*长时间,周期型任务/ if(task2_flag){ //任务2:呼吸灯 LED_Breathing(); task2_flag=0; } if(task3_flag){ //任务3:闪灯 LED2=!LED2; task3_flag=0; } } } void Timer0_irt() interrupt 1 //定时器0中断 0.1ms/次 { static unsigned char counter1=0; static unsigned int counter2=0; counter1++; if(counter1>100){ //10ms 100HZ PWM counter1=0; task2_flag=1; } counter2++; if(counter2>5000){ //500ms counter2=0; task3_flag=1; } /短时间任务/ if(counter1<compare) LED1=0; //任务1:软件模拟PWM else LED1=1; } void LED_Breathing(void) //呼吸灯 { if(turn_flag){ compare++; if(compare>100) turn_flag=0; } else{ compare--; if(compare<1) turn_flag=1; } }

可以看到,代码里面没有用到任何的阻塞延时函数。

        还有需要注意的是,执行中断内程序的总时间不能超过每次中断的时间,否则连续不停的中断会导致主循环内的程序得不到执行,程序卡顿。也不要在中断内执行除法运算,51速度有限,执行一条除法运算时间大概在0.6ms左右。

10.定时器中断实现无延时按键消抖

接上述,我们加入按键任务,实现以下操作:

示例代码:https://download.csdn.net/download/_/?spm=1001.2014.3001.5503

小讯
上一篇 2025-03-13 21:24
下一篇 2025-03-13 17:10

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/135970.html