关于“消抖”

来源:互联网 发布:sai mac下下来打不开 编辑:程序博客网 时间:2024/06/03 04:49

    在处理按键、传感器信号的过程中,常会遇到电平抖动的问题,如不进行有效地处理,将可能导致系统的误动作,如光纤传感器并没有检测到白线而只是因为场地上的微小白点或电路电平的抖动而使传感器信号出现短暂的抖动使控制器误以为检测到白线而采取动作。在做机器人时采用的方法有两种:硬件上-------对电信号进行滤波,把高频部分过滤掉;软件上----------当检测到电平跳变时,延时一段时间再次检测,若两次检测到的电平一致,则认为传感器信号可信(如按键被按下并且只按了一次,光纤传感器确实处于白线上等)。软件延时是最常见的方法,但也有着明显的缺陷,只是执行空指令进行延时,降低了MCU的使用效率,使系统处于短暂的盲区而漏掉重要的传感器信息或是使执行器处于短暂失控状态,降低了系统的实时性,打个形象的比方,假设大脑是单线程的,走在路上,忽然远处有个疑似美女,大脑在不停的做判断是凤姐还是美女,短暂失去对手脚的控制,脚下恰好有个坑,悲剧发生了,更悲剧的是确实是凤姐。有没有办法让准确判断是凤姐还是真的美女的同时观察路况控制手脚呢?办法当然是有的,要不天下要因凤姐产生多少悲剧!我们完全可以先观察路况控制手脚,用十秒钟靠上去仔细看看!对于MCU来说就是把那段延时给利用起来。我们可以利用定时器中断,每隔几毫秒进入中断检测一次IO口,这几毫秒的时间间隔恰起到了与延时一段时间排除电平抖动相同的作用,而由于使用了定时器中断,在这几毫秒的时间间隔内依然可以执行正常的程序流程对其他传感器信号进行检测、对执行机构进行控制。问题又出现了,在中断中检测到一次低(高)电平后检测到高(低)电平与连续两次检测到高(低)电平所要进行的处理时不同的,如何对如此之多的情况进行判断呢?一个很好的解决方法是使用有限状态机。

有限状态机学术性的解释为:由有限的状态及其相互之间的转移构成,在任何时候只能处于给定数目状态中的一个。当接收到一个输入事件时,状态机产生一个输出,同时也可能伴随着状态的转移。状态机由时间序列同步触发,定时检测输入,以及根据当前的状态输出相应的信号,并确定下一次系统状态的转移。时间序列时间间隔的选取,应稍微小于外部输入信号中变化最快的周期值。

以按键检测为例,将一次按键完整的操作过程分解为三个状态:无按键状态0、按键确认状态1、等待释放状态2 系统的输入信号时与按键连接的IO口电平,“1”表示按键处于开放状态,“0”表示按键处于闭合状态,系统的输出用“1”表示确认一次按键闭合操作。取状态机时间序列间隔为10毫秒(即多长时间进入中断检测一次传感器信号),这样不仅可以跳过按键抖动的影响,同时也远小于按键0.30.5秒的稳定闭合期,不会将按键操作过程丢失。

状态0为按键初始状态,当状态机输入“1”表示按键处于开放状态,输出“0”,下一状态认为状态0;当状态机输入“0”表示与按键连接的IO口检测到低电平,仍输出“0”,因为不能确定是电平抖动还是按键被按下,并转入下一状态1

在按键确认状态1(表示前10ms检测到低电平),当状态机输入“1”,表示按键处于开放状态,故可认为10ms前为抖动干扰,返回状态0;若状态机输入“0”,说明再次检测到低电平,故认为按键确实被按下,输出1,并转入状态2

在等待释放状态2,当状态机输入“0”,表示按键人处于闭合状态,输出“0”,并仍停留在状态2;当状态机输入“1”,表示按键已释放,输出0,并转入状态0

在一次按键操作过程中,按键的状态转移:状态0——>状态1——>状态2——>状态0。使用状态机表示的按键系统,不仅克服了按键抖动问题,同时由于输出信号仅在状态1给出唯一的按键闭合确认信号“1”,所以不管按键被按下多长时间,都只认为检测到了唯一一次按键按下。

对传感器信号的处理亦可以采用相同思路。

 

检测按键程序范例:

#define KEY_STATE_0 0

#define KEY_STATE_1 1

#define KEY_STATE_2 2

 

typedef unsigned char uchar;

 

uchar read_key()

{

    static uchar keyState;

    uchar key=0,keyReturn=0;

 

    key = keyInput;

 

    switch(keyState)

    {

    case KEY_STATE_0:

       if(!key)

           keyState = KEY_STATE_1;

       break;

    case KEY_STATE_1:

       if(!key)

       {

           keyState = KEY_STATE_2;

           keyReturn = 1;

       }

       else

       {

           keyState = KEY_STATE_0;

       }

       break;

    case KEY_STATE_2:

       if(key)

           keyState = KEY_STATE_0;

       break;

    }

 

    return keyReturn;

}

 

 

 

原创粉丝点击