分类: C/C++
2010-07-09 10:49:25
C51单片机矩阵键盘扫描去抖程序
最近有一个C51的项目,用的是新华龙的C51 F020单片机。项目中要实现4*5的矩阵键盘。矩阵电路图如下如示
其中,四条列线接在
F020的P2~P5口线上,5条行线接在P5口线上(F020的P5口是不同于普通C51的扩展接口,不能位寻址)。同时4条列线接在一四输入与非门(74LS20)上,门输出接F020的外中断1,这样,任何一键按下,都会产生中断,通知程序进行键盘扫描。
托一个新手给写了键盘的扫描程序,基本功能都能实现,但对于键盘的去抖处理总是做不好,表现是或者不能去抖,或者按键响应过慢,或者采集到错误键值。看来新手对于矩阵键盘扫描原理掌握较好(网上资料多),但对于键盘去抖的知识却有所欠缺,基本都是按照书上说的延时一段时间再采集键值,实际应用中,这样的处理是远远不够的,过于简单。实际去抖处理应该这样进行更合理一些,即连续采集键值,当采集到的键值在一段时间内是相同的,即认为按键状态已稳定,此键值为真实键值。另外,按键释放时,也会有抖动,导致误采键值,因此在键释放时,也应进行去抖处理,处理方法同时是连续一段时间采集到无键按下状态,才认为按键被释放。根据这个方法,我重写了新手的程序,实际应用中表现极好。
现将程序公布如下,供新手参考。
Key.h文件内容
#ifndef __key_H__
#define __key_H__
#define NULL_KEY 0x0000
#define S1 0x3801
#define S2 0x3401
#define S3 0x3802
#define S4 0x3402
#define S5 0x3804
#define S6 0x3404
#define S7 0x3808
#define S8 0x3408
#define S9 0x3810
#define S10 0x3410
#define S11 0x2C01
#define S12 0x1C01
#define S13 0x2C02
#define S14 0x1C02
#define S15 0x2C04
#define S16 0x1C04
#define S17 0x2C08
#define S18 0x1C08
#define S19 0x2C10
#define S20 0x1C10
#define KEY_DELAY 20
extern unsigned int Key_Value;
extern void Init_Key();
extern void Scan_Key();
extern bit Key_Pressed;
extern bit Key_Released;
extern unsigned int idata Keypress_Count;
extern unsigned int idata Keyrelease_Count;
#endif
key.c 文件内容
#include
#include "key.h"
bit Key_Down; //是否有键按下的标志
unsigned int idata Keypress_Count;
sbit Col_Key0 = P2^2;
sbit Col_Key1 = P2^3;
sbit Col_Key2 = P2^4;
sbit Col_Key3 = P2^5;
bit Key_Pressed;
bit Key_Released;
unsigned int Key_Value;
bit Key_Down; //是否有键按下的标志
unsigned int idata Keypress_Count; //一毫秒增加一次的变量
unsigned int idata Keyrelease_Count; //一毫秒增加一次的变量
//矩阵键盘使用中断1作为键盘中断
void Init_Key()
{
P5 = 0; //行线全部置为0
EX1 = 1; // 允许外部时钟秒中断
IT1 = 1; // 外部时钟中断设置为边沿触发
}
void Key_Int() interrupt 2
{
Key_Pressed = 1;
EX1 = 0;
}
void Scan_Key()
{
unsigned char temp,rowvalue;
unsigned int key;
int i;
temp = P2;
temp &= 0x3C;
if(temp == 0x3C)
{
Key_Released = 0;
Key_Pressed = 0;
key = NULL_KEY;
EX1 = 1;
}
else
{
key = temp;
key = key<<8;
rowvalue = 0x01;
for(i=0;i<5;i++)
{
P5 = rowvalue<
DelayMs(1);
temp = P2;
temp &= 0x3C;
if(temp == 0x3c)
{
rowvalue = rowvalue<
key = key | rowvalue;
P5 = 0x00;
break;
}
}
P5 = 0x00;
DelayMs(1);
}
if(key!=NULL_KEY) //如果有键按下
{
if(key==Key_Value) //如果按下的是相同的键
{
if(Keypress_Count>=KEY_DELAY)
{
Key_Down = 1;
}
}
else if(Key_Down != 1)
{
Keypress_Count=0;
Keyrelease_Count = 0;
Key_Value=key;
}
}
else //如果无键按下
{
if(Key_Down) //如果当前是键释放,返回键值
{
if(Keyrelease_Count >= KEY_DELAY)
{
Key_Down=0;
Keypress_Count=0;
Keyrelease_Count=0;
Key_Released = 1;
EX1 = 1;
return;
}
}
else
{
Keypress_Count=0;
Keyrelease_Count=0;
Key_Value = NULL_KEY;
EX1 = 1;
return;
}
}
}
在main.c中的调用方法为
if(Key_Pressed == 1)
{
//Key_Pressed = 0;
Scan_Key();
}
if(Key_Released == 1)
{
Key_Released = 0;
Ack_Key();
}
其中Ack_Key()函数为具体的键盘响应程序,就不列出了。
我在另外使用Arm9的项目中,linux底层扫描键盘的驱动中,也使用了这种去抖方法,同样表现良好。