大家可能会觉得我们用延时函数照样可以实现上一讲代码的实验现象,但是定时器与延时的概念不同,延时函数需要占用CPU的使用权,正在延时的时候其他任务没有CPU的使用权就会拖慢执行效率。
而定时器是不需要占用CPU的使用权的,它是独立自己运行的,就像我们在第一讲的时候提到调好5分钟的闹钟,在这5分钟里我们可以随意执行任务,也可以什么事都不做,但是5分钟过后闹钟响了就要执行相关的任务了。
所以上一讲的代码的实现原理就是每隔51微秒,有个变量会自加1,过了1000个51微秒的时候LED的状态才会改变,可以说CPU在51ms的时间里基本没什么事做,只是在51微秒到了的时候做了“cnt++;”这样简单的任务,然后又空闲地等下一个51微秒的到来再执行“cnt++;”。
与外部中断一样,定时器中断也有中断函数,同理,程序去执行中断函数就会把TF0的中断标志位自动清0,所以只要我们用了定时器中断函数,那么TF0就可以不用再出现在程序书写中了。
还记得外部中断这个图吗
同样定时器0的中断函数使能如下
至于“interrupt”后面的数字为什么是1,请再看我们以前给过大家的这个图的中断函数编号就明白了
这些编号是为了区分哪些硬件资源的相关中断函数,如果我们同时使用两个定时器,那么只能用“interrupt 1”和“interrupt 3”来区分谁是谁的中断函数了。
使用“TIM0_IRQHandler”作为函数名也是模仿STM32定时器中断函数名的写法。
如果我们使用的是工作模式1,每次触发中断函数的执行内容首先就是再次给TH0和TL0赋初值保证下次的定时时间还是一样。
这里我们使用中断函数的执行方式来实现30ms的间隔流水灯,算出TH0和TL0合成的“16位的变量”要填充的值为37888=0x9400。
在中断函数里也是可以定义局部变量的,当然如果这个变量是用来辅助流水灯的,那么肯定是要定义成静态变量的。
#include <reg52.h> #include <function.h>//详见第六章第8讲 void main() { LED_Init(); //初始化LED硬件模块 EA = 1; //闭合总中断开关 TMOD = 0x01; //设置定时器0为工作模式1 TH0 = 0x94; //设置定时时间为30ms TL0 = 0x00; ET0 = 1; //闭合定时器0中断的开关 TR0 = 1; //启动定时器0 while(1); } void TIM0_IRQHandler() interrupt 1 { static u8 i; TH0 = 0x94; //重新设置定时时间为30ms TL0 = 0x00; P0=~(0x80>>i);//这一次让流水灯向右移 i++; if(i>=8)i=0; }
觉得30ms的流速太快,想改为300ms的话,修改一下中断函数即可,如下
void TIM0_IRQHandler() interrupt 1 { static u8 i,cnt; TH0 = 0x94; //重新设置定时时间为30ms TL0 = 0x00; cnt++; if(cnt>=10) { cnt=0; P0=~(0x80>>i);//这一次让流水灯向右移 i++; if(i>=8)i=0; } }
本文固定URL:https://www.dotcpp.com/course/377
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程