尽量用最少的文字描述清楚问题。
事情起因是这样的:
要做遥控小车的平台迁移,STM32开发板无法方便地供电,因此又拿出了尘封的51(STC89C52RC),搭配上最小系统板就可以用排针加杜邦线供电了。
测试的时候出了点问题,51开发板上用作显示的数码管会闪动,而在逻辑正确的情况下是不会出现这个情况的(后来发现数码管的位选段选信号有点小问题)。在排查过程中,一步一步找到了中断处理程序。
51一共有5个中断源,其中有2个定时器中断T0 T1,2个外部中断,1个串口中断。52比51多一个定时器中断T2。
定时器有4种工作模式,T0和T1的M1M0均为01的时候,也即TMOD为0x11时,是工作模式1,为16位定时器。不自动重装初值,需要手动重装初值。
初学51的入门书是郭天祥老师的<新概念51单片机C语言教程>,在第3章第5节讲述定时器中断的时候,为了讲述方便和容易理解,中断服务程序中是这样写的:
void T0_time() interrupt 1 { TH0 = (65536-45872)/256; TL0 = (65536-45872)%256; //... //... }
这是在11.0592MHz晶振下50ms来一次中断的重装初值的方法。关于这个的解释是这样的。
在晶振为12M的时候,由于机器周期是12个时钟周期,机器周期为(1/12M)*12 = 1 (us) 也就是计一个数需要 1us 50ms需要 计50000个数 所要装入的总数是 65536-50000=15536 所要装入的初值是 TH0=(65536-50000)/256; TL0=(65536-50000)%256; 在晶振为11.0592M的时候,机器周期为(1/11.0592M)*12 = 1 x 12 /11.0592 =1.085 (us) 100us,需要计 100/1.085 = 92.16个数,取整 92 所要装入的总数是 65536-92 所要装入的初值是 TH0=(65536-92)/256; TL0=(65536-92)%256;
其实我想说的是,虽然这样可能比较容易看懂(配合注释),但是就把这个表达式放在这儿而不把结果计算出来,会使效率大打折扣!
其实在50ms下还好,因为总时间比较长,每次计算(65536-45872)/256的时间虽然是固定的,但是百分比较小,相对影响较小。
当设定中断为100us来一次的时候,会使得计算表达式的时间所占百分比大大提升。由于100us = 0.1ms = 50ms / 500,也就是说,假设65536-92和65536-45872的时间消耗同一个数量级的(100us的时候中断服务程序中重装的初值45872变成了92),计算
(65536-92)/256 和(65536-92)%256
的时间消耗百分比提升了500倍左右。
举个栗子:
void T0_time() interrupt 1 { TH0 = (65536-45872)/256; TL0 = (65536-45872)%256; }
和
void T0_time() interrupt 1 { TH0 = (65536-92)/256; TL0 = (65536-92)%256; }
假设做
TH0 = (65536-45872)/256; TL0 = (65536-45872)%256;
和
TH0 = (65536-92)/256; TL0 = (65536-92)%256;
都需要1us的时间。
在总时间为50ms时,前者所占百分比为 1/50k = 0.002%
在总时间为100us时,后者所占百分比为 1/100 = 1.000%
尼玛差了500倍左右!能想象吗?
别看绝对数字,因为这1us是我编出来的,实际上51属于低速芯片,而且还有其他事要做。事实上上中断里面除了重装初值还要做其他事情,却让这么一个简单的但没有化简的重装初值的表达式给占用了太多资源和时间。。
我好像说了好多废话,那我就直接说结论吧:就只是把结果计算出来,也就是把
TH0 = (65536-92)/256; TL0 = (65536-92)%256;
换成
TH0 = 255; TL0 = 194;
其他啥都不改,然后把程序下载到芯片上,数码管的变化已经可以由人眼分辨我会乱说?
有的效率提升其实很简单。