按键扫描函数如下,该函数需周期执行,以扫描按键的状态。以51单片机为例,P1.0~P1.3逐行输出扫描信号,在Key.h模块头文件实现接口宏KeyOutputSelect()
#define KeyOutputSelect(Select) {P1 = ~(1<<(Select));}
输出扫描线后,需要读取对应扫描线的按键状态(P1.4~P1.7),同样在Key.h模块头文件实现引脚状态读取接口宏KeyGetPinState()
#define KeyGetPinState() (P1>> 4)
读取了对应扫描线下的按键引脚状态,就需判断哪些引脚电平为0(按下),对读到的引脚状态进行取反转换成对引脚状态变量进行搜1算法,得到键值的速度能达到最快,并且多个按键同时按下时也能够正确得到优先级最高的按键。按键有效按下会得到0~15的键值,无按键按下时得到键值16。
voidKeyScan()
{
unsigned char i;
unsigned char KeyValue;
unsigned char PinState;
if (KeyState.State == STATE_DISABLE) {
return; // 按键禁用时,不对键盘进行扫描
}
// 键值为0~15,未按键键值为16,任意多的键按下均能
// 正确返回优先级最高的键值
KeyValue = 0;
for (i=0; i<4; i++) {
KeyOutputSelect(i); // 输出扫描线
// 得到对应扫描线时的按键状态
PinState = KeyGetPinState();
// 有键按下时,PinState中有0的位置即为键值位置
PinState = ~PinState;
// 搜索Pinstate第一个为1的位
if (!(PinState & 0xf)) {
KeyValue += 4;
continue; // 该扫描线没有按键按下,进入下一扫描线
}
// 该扫描线有键按下,对半进行检索1的位置
if (!(PinState & 0x3)) {
KeyValue += 2; // 低2位(P1.4~P1.5)没有按下
PinState >>= 2; // 移位检索(P1.6~P1.7)
}
if (!(PinState & 0x1)){
KeyValue += 1;
}
break; // 有鍵按下,退出继续扫描
}
KeyStore(KeyValue); // 保存按键状态
}
得到了按键值后,我们需要对按键值进行处理并根据按键状态把可能产生的按键消息保存进缓冲区中,以便用户程序读取处理。按键通常有按下、松手、长按这几个状态,需要支持按下检测、松手检测、长按、连击的功能,并且需要对按键进行去抖滤波。按键的状态往往会在这几种情况进行切换,因此,对按键进行状态机编程是相当清晰的思路。我们在KeyStore()函数中实现对按键状态的转移判断,在模块中我们通过按键状态结构变量KeyState来跟踪记录按键的状态
typedef struct {
unsigned char State; // 按键的各个状态转移
unsigned int TimeCount; // 用来跟踪各个状态的计时
} KEY_STATE;
static KEY_STATE KeyState; // 按键状态机状态转移
检测到相应的按键事件后(KEY_UP、KEY_DOWN、KEY_LONG),需产生相应的按键消息保存进按键缓存区,通常可以开辟一个按键队列缓存,以便保存多个产生的按键消息,不会因用户代码未能及时处理按键而造成按键丢失,笔者此处为避免复杂,以一个按键缓冲为例,按键事件结构变量KeyBuffer用来保存按键消息