1.漏洞代码
假如就按上一讲说的书写方式,实现定时50ms间隔的流水灯。
#include <reg52.h> #include <function.h>//详见第六章第8讲 void main() { LED_Init(); //初始化LED硬件模块 EA = 1; //闭合总中断开关 TMOD = 0x01; //设置定时器0为工作模式1 TH0=( 65536-( (50000*110592)/120000 ) )/256;//设置定时时间为50ms TL0=( 65536-( (50000*110592)/120000 ) )%256; ET0 = 1; //闭合定时器0中断的开关 TR0 = 1; //启动定时器0 while(1); } void TIM0_IRQHandler() interrupt 1 { static u8 i; TH0=( 65536-( (50000*110592)/120000 ) )/256;//重新设置定时时间为50ms TL0=( 65536-( (50000*110592)/120000 ) )%256; P0=~(0x01<<i); i++; if(i>=8)i=0; }
下载进开发板发现根本不是间隔50ms!
进行仿真查看i每次间隔自加所花的时间居然是11ms左右。
2.解析漏洞
首先我们要知道,51单片机能存储最大的一个整型数的大小只有4个字节,也就是最多能记忆这个数到4294967296(2的32次方),而在
“( 65536-( (50000)*110592)/120000 )”中明显不能把“(50000*110592)”给临时存储,因为这个等式的得数已经大过2的32次方。所以我们的定时器才会无法实现准确的50ms定时。如果我们对编程没有一定的积累是很难察觉出这个隐形漏洞的。
解决办法就是,我们的定时最小单位只能是10微秒,也就是定时的时间必须是10微秒的整数倍。书写如下
TH0=( 65536-(5000*110592)/12000 ) )/256;
把之前的“50000”和“120000”都去掉一个零,这样就可以准确的定时50ms了,因为“(5000)*110592)”没有超过2的32次方,读者自行修改本讲提供的代码中的4处之后下载进开发板观察现象是不是又实现50ms的间隔流水了。
3.再次优化书写
上处的5000意为定时的是50ms,也就是5000*10微秒,但我们希望潜意识里假如要定时200微秒,如果写成20我们的思维还要绕个弯再把20默默乘以10才领悟出这是定时200微秒。
倒不如这样,我们看到关键的数字是多少那就是要定时多少微秒。
比如看到关键数字为50000时就知道定时的是50000微秒。
所以我们这样改写:TH0=( 65536-( (50000/10)*110592)/12000 )/256;
这样既保证了“(50000/10)*110592”没有超过2的32次方,也使“50000”更直观的让我们知道要定时的是50000微秒。
但是大家要记住,关键数字必须是10的整数倍,如果想定时个208微秒,“(208/10)”还是等于20,所以定时时间还是200微秒。
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程