《《《《《正文》》》》》
最初,我在按键消抖软件策略上的实现方法是获取按键电平后,延时一段时间后,再去判断按键电平,如果2次电平相同就确定为消抖成功,最后的电平就可作为判断按键操作的依据。大致伪代码如下:
void main(void)
{
Key_Init(); //底层初始化
while(1)
{
KeyIO_1 = GetKeyIO();
delay_ms(100); //延时50ms函数
KeyIO_2 = GetKeyIO();
if(KeyIO_2 == KeyIO_1)
{
KeyIO = KeyIO_1;
}
}
}
如果项目只有一个按键的事务处理,那CPU都是按键事务的,那如上编程倒没有任何问题;如果项目还有其他事务需要处理,比如控制Led灯,这个时候CPU的使用权就有了2个事务!按上面的程序的话,led灯事务要等按键事务执行完了再执行led灯事务,同样,按键事务也要等led灯事务执行完了再执行;伪代码如下图:
void main(void)
{
Key_Init(); //底层初始化
while(1)
{
//按键事务
KeyIO_1 = GetKeyIO();
delay_ms(100); //延时50ms函数
KeyIO_2 = GetKeyIO();
if(KeyIO_2 == KeyIO_1)
{
KeyIO = KeyIO_1;
}
//led事务
LED_1();
delay_ms(100);
LED_0();
}
}
按上面的代码我们大概的算一下CPU花在处理事务上的使用率,假设上面while内的代码运行一次需要201ms(实际更小,基本接近于200ms),其中延时函数总共花了200ms,那CPU花在事务上的百分比%= 201-200/201 = 0.5%,那其余的时间CPU在干嘛呢?延时函数告诉你CPU就在那里死等!这段延时(死等)的时间,CPU没有做任何事情,那就是浪费CPU。
所以在实际编程中,我们一定要尽可能避免这种死等的操作,之所以用尽可能是因为不是说死等就是没用,在某些必要的情况下,仍然需要死等这个操作,比如一些需要同步操作的,或者延时时间很短不影响主程序等等。所以平时大都不要轻易使用延时(死等)这种操作,耗时是小事,死在里面就麻烦了!
《那怎么解决这种问题呢?》
这是今天的重点!下面这个例子应该可以让你秒懂!
大家平时都去过银行吧!拿完排队的票,发现还有很多人,你会怎么办?有人坐在大厅等;有人就会先去干其他的事情,然后过段时间再来看看,要是还没排到就再去干其他事情先,直到排到;
这里坐在大厅等的人,就是上面的延时(死等)函数操作;而另一种人就是我们现在要说的,定期去看看,排到了就办理业务,没排到就继续去干其他事情;
那有人说,如果回去银行发现已经过了自己的号怎么办?这里大家注意一点思维,因为得益于单片机处理速度的快速,一个while(不含延时)里的程序我们都认为是同时运行的,虽然从微观看,肯定是有先后,但是从宏观看,就是同时运行的,所以我们单片机可以做到极快的回去看看轮到我们没有,不会把事务丢掉(当然如果你事务也极快,那你回去看的频率当然要比它快,防止事务丢掉)。所以我们可以在时基中断里产生一个时间中断的标志位,比如2ms,对于按键事务,2ms足以满足检测频率,不会丢掉按键的电平变化操作(除非你在2ms以内来一次按键操作,你手可以吗?)。这样我们每2ms去处理一次按键事务,其他时间处理其他事务;这样子就提高了CPU的利用率!
伪代码如下:
uint8_t _2ms_Flag = 0; //2ms标志位
//2ms中断函数
void ISR_2ms(void)
{
_2ms_Flag = 1;
}
void main(void)
{
Key_Init(); //底层初始化
Other_Init();
while(1)
{
if(_2ms_Flag ==1) //定期回来看看
{
_2ms_Flag = 0
Key_Deal();//这里用计数器实现消抖策略
}
Other_Deal(); //其他事务
}
}
这种方式其实叫前后台轮询系统,事务触发标志在中断里触发(后台),事务处理在主循环里处理(前台)!这里的中断可以扩展为外部输入事务,这样实时性、事务处理速度、CPU利用率都会大大提高!何乐不为?!
《《《《《END》》》》》