原因:
机械开关被按下之后,触点不会立即稳定下来而是会发生抖动,开关的抖动导致输入输入IO口的电平也会发生高、低电平的抖动。
根据其寿命试验,最坏情况下的开关稳定时间为15ms。按照3倍余量的计算,需要45ms的去抖动时间,在45ms内至少需要4次采样,从而得到最大采样周期为11.25ms。
开关实际输入波形
根据这一要求,设计高可靠开关/按键输入扫描程序:
在switch.h的头文件中,
宏定义开关/按键的数量,
定义开关/按键扫描的结构体类型,
包含记录当前电平状态的成员变量level,
记录当前电平按下/抬起事件的成员变量edge,
记录去抖次数的成员变量debouncecount,
记录去抖之后按键状态的成员变量state;
声明开关/按键扫描使用的结构体变量为外部变量。
#define SWITCH_NUM 8
typedef struct{
u8 level;
u8 edge;
u8 state;
u8 debouncecount;
}stKeyTypeDef;
extern stKeyTypeDef stKeyDet[SWITCH_NUM];
在main.c文件中,使用由timer模块提供的8ms定时标志timerregs.timer8ms;
判断是否到8ms定时时间,如果标志为TRUE,则8ms定时到,执行扫描程序。
从而其扫描周期为8ms<11.25ms满足要求。
定义const数组保存每个输入的去抖次数,去抖次数需要选择合适的数值。
太小即起不到去抖的效果,太大则会导致开关/按键的操作不灵敏。
去抖时间一般在60-100ms之间比较合适,我通常将去抖动次数设置为10次,对应去抖时间为80ms,如下:
const u8 debouncecount[SWITCH_NUM] = {8, 8, 8, 8, 8, 8, 8, 8};
按键扫描函数如下:U8 i;U8 level;if(timerregs.timer8ms) { for(i= 0; i < SWITCH_NUM ; i++) { level = getkeystate(i);//读IO口,获取当前按键/开关输入状态 if(level != stKeyDet.state) //i按键状态有变化 { stKeyDet.debouncecount++; //去抖计数累加 } else { stKeyDet.debouncecount = 0; //去抖次数清零 } if(stKeyDet.debouncecount >= debouncecount)//如果去抖次数超过了设定的次数 { stKeyDet.state = level; //修改开关/按键状态 stKeyDet.edge = TRUE; //开关/按键的按下/弹起事件 stKeyDet.debouncecount = 0; } } }上述代码放在switch.c的RealTime实时函数中,在main主程序中实时调用。
在应用层程序中可以通过stKeyDet.state判断当前的开关/按键状态,
通过if((stKeyDet.state) && (stKeyDet.edge))判断按下事件,
通过if((stKeyDet.state == FALSE) && (stKeyDet.edge))判断弹起事件,
在main主程序的末尾,调用swith.c提供的状态清除函数,将stKeyDet.edge清成FALSE;
该程序有几个设计要求:
1) 根据定时器模块提供的8ms事件实现8ms/次的扫描。
2) 扫描程序在主程序中调用,而不是在中断进行检测,降低中断处理的负载,加快程序的响应。
3)其它模块程序通过state以及edge 变量判断开关/按键的状态和事件,进行逻辑处理。
4) 所有需要用到开关/按键的模块上处理完之后,在main函数的末尾需要把按下/弹起事件变量edge清零。