Dotcpp  >  编程教程  >  定时器  >  隐形漏洞

隐形漏洞

点击打开在线编译器,边学边练

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


本文固定URL:https://www.dotcpp.com/course/379

上一课:

定时器初始化新写法

下一课:

代码参考

单片机教程
第一章 单片机入门
第二章 LED及入门
第三章 蜂鸣器
第四章 数码管
第五章 独立按键
第六章 多文件编程
第七章 外部中断
第八章 定时器
第九章 舵机与超声波模块
第十章 串口通信
第十一章 1602液晶屏
第十二章 IIC通信
第十三章 红外遥控与温度传感器
第十四章 AD与DA
第十五章 混合例程
第十六章 完结
Dotcpp在线编译      (登录可减少运行等待时间)