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左右。

定时器7


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微秒。

点赞(0)

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

Dotcpp在线编译      (登录可减少运行等待时间)