1.定时器&蜂鸣器
一般定时器中断函数里的内容最好是能够快速地去执行完,比如只执行几条简单的语句,这样与主函数配合才会使程序更加高效。前期教学里,我们只使用定时器中断负责某个IO引脚间隔跳变或者使一个变量间隔自加1的简单语句。
比如我们现在要实现间隔50ms左右的时间让流水灯左右循环移动的同时,还需要无源蜂鸣器一直响,这样的功能,思路该怎么去思考?
首先我们知道无源蜂鸣器要想鸣叫的比较尖锐,那P1.6需要一个合适的脉冲信号,这个信号笔者打算使P1.6高低电平保持的时间为300微秒不断循环。
所以我们用定时器中断实现P1.6的电平间隔跳变,主函数里负责完成流水灯的任务即可。
2.代码
#include <reg52.h> #include <function.h>//详见第六章第8讲 u8 T0RH, T0RL; void TIM0_Init(u32 us,int trim)//trim:微调 { u32 tmp; //临时变量 tmp = 11059200 / 12; //定时器计数频率 tmp = ( tmp * (us/100) )/10000;//计算所需的计数值 tmp = 65536 - tmp; //计算定时器重载值 tmp = tmp+trim; //补偿中断响应延时造成的误差 T0RH = (unsigned char)(tmp>>8);//定时器重载值拆分为高低字节 T0RL = (unsigned char)tmp; TMOD &= 0xF0; //清0低四位 TMOD |= 0x01; //设置定时器0为工作模式1 TH0 = T0RH; //加载T0重载值 TL0 = T0RL; ET0 = 1; //闭合定时器0中断的开关 TR0 = 1; //启动定时器0 } void main() { u8 i,dir; LED_Init(); //初始化LED硬件模块 EA = 1; //闭合总中断开关 TIM0_Init(300,0); //用定时器0定时300us,不微调 while(1) { if(i<8)dir=0;//向左移 if(dir==0)P0=~(0x01<<i); if(i>=8)dir=1;//向右移 if(dir==1)P0=~( 0x80>>(i-7) );//当i大于等于8之后,(i-7)其实也还是在1~7之间变化 i++; if(i>=15)i=1;//让i一直在1~14之间变化 delay_ms(50); } } void TIM0_IRQHandler() interrupt 1 { TH0 = T0RH; //重新加载重载值 TL0 = T0RL; BEEP=!BEEP; }
可以看到使用定时器中断比外部中断触发还要高效。
3.讲解微调
然后回到上一讲说过的怎么测试然后去微调使定时时间更精确。
拿我们这讲的代码来说,首先在不微调的情况下,也就是传入的参数trim为0,看看定时的时间是多少。
进入软件调试模式,在“BEEP=! BEEP;”设置断点,在没有微调之前观察每次执行“BEEP =! BEEP;”的间隔时间。
按下RST复位之后,先按两次RUN
再按一次RUN
定时时间为0.003285-0.002979= 0.000306s
所以定时时间超了6微秒,则需要给trim一个合适的参数,这里笔者调出了最合适的数为6,所以我们初始化定时器时改为
TIM0_Init(300,6); //用定时器0定时300us,6是微调使定时时间更精确
这样每次跳转到中断函数去执行的间隔时间就为精确的300us了。
当我们要定时十毫秒或者二三十毫秒时,微调的数可能就不是6了,而是20之间或者其他数,所以每次想要精确定时就需要软件调试亲自测试。
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程