学习矩阵键盘的使用


本文条件限制:4x4矩阵键盘;使用逐行扫描方法,区分按下和抬起,不区分连击、长按和单击;线路逻辑如下图。

策略:设置定时器,每过8~10毫秒,取决于软件消抖动通常采取的延时,就扫描一次矩阵键盘。将按键状态记录下来,将其与上一次按键状态进行对比,从而计算出按键动作。随后将按键动作加入循环队列,供其它程序使用。队列满时可以丢弃新的动作,也可以丢弃之前的动作,这取决于你的需要。

扫描:逐次将各条行线置低,四条列线拉高。随后读入4位列信号,根据列信号中出现低电平(零)的位置来判断该行中四个按键的状态。

动作:要确定每个按键的动作,至少需要为每一个按键保存一个状态。使其根据按键的状态,也就是用户的按键输入,在各个状态之间迁移。这是一般化方法。

状态:对于一次完整的击键动作,对应的列线电平状态可以简单描述为下图。红色折线代表列线电平变化,灰色区域为按键抖动,黑色竖线为时间点。

 时间点1:稳定释放,2:按键被按下,3:稳定按下,4:按键被释放

C语言代码:

#define NO_KEY 0
#define KEY1_DOWN 1
#define KEY2_DOWN 2
#define KEY3_DOWN 3
#define KEY4_DOWN 4
#define KEY5_DOWN 5
#define KEY6_DOWN 6
#define KEY7_DOWN 7
#define KEY8_DOWN 8
#define KEY9_DOWN 9
#define KEY10_DOWN 10
#define KEY11_DOWN 11
#define KEY12_DOWN 12
#define KEY13_DOWN 13
#define KEY14_DOWN 14
#define KEY15_DOWN 15
#define KEY16_DOWN 16
#define KEY1_UP -1
#define KEY2_UP -2
#define KEY3_UP -3
#define KEY4_UP -4
#define KEY5_UP -5
#define KEY6_UP -6
#define KEY7_UP -7
#define KEY8_UP -8
#define KEY9_UP -9
#define KEY10_UP -10
#define KEY11_UP -11
#define KEY12_UP -12
#define KEY13_UP -13
#define KEY14_UP -14
#define KEY15_UP -15
#define KEY16_UP -16

#define key_port P1

#define queue_max 8
static char queue[queue_max];
static unsigned char front, rear;

static char states[key_n];

static void push(char msg)
{
    if ((rear+1) & 7 == front) return;
    queue[rear] = msg;
    rear = (rear+1) & 7;
}
static char pop()
{
    char e;
    if (rear == front) return NO_KEY;
    e = queue[front];
    front = (front+1) & 7;
    return e;
}

void init_key()
{
    char i;
    front=0, rear=0;
    for(i=0; i0; }
} 
void key_scan()
{
    unsigned char tmp;
    unsigned int mask=0x8000, curr=0xffff;
    char i;
    key_port=0xfe;  tmp = key_port>>4;  curr = (curr<<4) + tmp;
    key_port=0xfd;  tmp = key_port>>4;  curr = (curr<<4) + tmp;
    key_port=0xfb;  tmp = key_port>>4;  curr = (curr<<4) + tmp;
    key_port=0xf7;  tmp = key_port>>4;  curr = (curr<<4) + tmp;

    for (i=0; i>= 1, ++i){
        switch(states[i]){
        case 0:
            if(!(curr&mask)) {states[i]=1;}
            break;
        case 1:
            if(!(curr&mask)) {push(i+1); states[i]=2;}
            else {states[i]=0;}
            break;
        case 2:
            if(curr&mask) {states[i]=3;}
            break;
        case 3:
            if(curr&mask) {push(-i-1);}
            states[i]=0;
            break;
        }//switch
    }//for
}
char get_key(){ return pop(); }

定时器中断处理程序要保证每隔8~10毫秒扫描一次矩阵键盘。

若要阻塞式地等待某按键动作发生,可以将get_key放入循环结构内以重复判断按键动作。这样做会抛弃指定按键动作发生之前的所有其它按键动作。