实现目标
使用两部扫描法,若有键按下,返回按下键的位置;若无键按下,返回0xff。每10ms定时检测一次按键,使用定时器中断实现定时。
Proteus仿真图
C51代码
#include <REG52.H> /* special function register declarations */
#include <intrins.h>
#define byte unsigned char
#define uchar unsigned char
#define word unsigned int
#define uint unsigned int
#define ulong unsigned long
#define BYTE unsigned char
#define WORD unsigned int
#define TRUE 1
#define FALSE 0
void initUart(void);/*初始化串口*/
void time(unsigned int ucMs);//延时单位:ms
#define KEY_PORT P1 /*按键接在KEY_PORT口*/
uchar key_Value=0xff; /*存放键值*/
uchar keyscan(void);/**扫描按键函数-2步判别扫描法 **/
void Key_process(void);/*键值处理程序*/
/******** main 函数 *********/
void main (void) {
initUart(); /* 初始化串口 */
TMOD=0x10; /* 设置定时器1为工作方式1 */
TH1=-10000>>8;TL1=-10000 % 256;/* 定时器1每10000计数脉冲发生1次中断,12MHz晶振,定时时间10000us */
TCON=0x40; /* 内部脉冲计数 */
IE=0x88; /*打开定时器中断*/
key_Value=0xff;
do {
/* 如果key_Value!=0xff 说明有键按下 那么就可以根据此时的key_Value使用key_process函数判断是哪一个按键被按下 并输出 */
if (key_Value!=0xff) Key_process();
}while(TRUE);
}
/******* 定时器/计数器1中断服务程序 ***/
void timer1int(void) interrupt 3
{
/******* 每10ms产生一次中断 每次中断进行键盘扫描 ***/
EA=0;/* 关总中断 */
TR1=0;/*停止计数*/
TH1=-10000>>8;TL1=-10000 % 256;/* 定时器1每10000计数脉冲发生1次中断,12MHz晶振,定时时间10000us */
TR1=1;/*启动计数*/
key_Value= keyscan();
EA=1;/* 开总中断 */
}
/*****扫描按键函数-2步判别扫描法 *****/
uchar keyscan(void)/**扫描按键函数-2步判别扫描法 **/
{
uchar readkey, rereadkey;
uchar x_temp,y_temp;
KEY_PORT=0x0f; /*向P1输出电平 0000 1111 也就是列为低电平 行为高电平 注:这个得看具体连线*/
x_temp= KEY_PORT & 0x0f; /*读取P1的电平情况 并取后四位 得到所有行的电平情况*/
if (x_temp==0x0f) return(0xff); /*如果所有行无按键按下,说明都为高电平,此时P1电平情况为 0000 111 退出*/
/*上一步没有退出 说明有按键按下 那么就该进行列扫描*/
KEY_PORT=0xf0; /*P1输出 所有行为低电平 所有列为高电平*/
y_temp= KEY_PORT & 0xf0; /*获取P1端口所有列的电平情况*/
readkey = x_temp | y_temp; /*两者相或*/
/*为防止键的抖动处理 需要进行第二次判断 若两次判断一样 则确定键按下*/
time(10); /*延时10ms后再测按键*/
KEY_PORT=0x0f;
x_temp= KEY_PORT & 0x0f;
if (x_temp==0x0f) return(0xff); /*无按键,退出*/
KEY_PORT=0xf0;
y_temp= KEY_PORT & 0xf0;
rereadkey= x_temp + y_temp;
if (readkey == rereadkey) { /*2次一致*/
return(~rereadkey);
}
return(0xff);
}
void Key_process(void)/*键值处理程序*/
{
switch (key_Value){ /*根据中断源分支*/
/* 按第2行键 */
case 0x11:
printf ("Key(R1,C1) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x21:
printf ("Key(R1,C2) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x41:
printf ("Key(R1,C3) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x81:
printf ("Key(R1,C4) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
/* 按第2行键 */
case 0x12:
printf ("Key(R2,C1) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x22:
printf ("Key(R2,C2) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x42:
printf ("Key(R2,C3) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x82:
printf ("Key(R2,C4) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
/* 按第3行键 */
case 0x14:
printf ("Key(R3,C1) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x24:
printf ("Key(R3,C2) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x44:
printf ("Key(R3,C3) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x84:
printf ("Key(R3,C4) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
/* 按第4行键 */
case 0x18:
printf ("Key(R4,C1) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x28:
printf ("Key(R4,C2) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x48:
printf ("Key(R4,C3) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
case 0x88:
printf ("Key(R4,C4) is pressed\n");
/* 可在此处插入该按键的处理程序 */
break;
default:break;
}
}
/********** 初始化串口波特率 ************/
void initUart(void)/*初始化串口波特率,使用定时器2*/
{
/* Setup the serial port for 9600 baud at 11.0592MHz */
SCON = 0x50; //串口工作在方式1
RCAP2H=(65536-(3456/96))>>8;
RCAP2L=(65536-(3456/96))%256;
T2CON=0x34;
TI = 1; /* 置位TI*/
}
/*********** 延时单位:ms *******************/
void time(unsigned int ucMs)//延时单位:ms
{
unsigned char j;
while(ucMs>0){
for(j=0;j<10;j++) delay_100us();
ucMs--;
}
}
笔记
思路:
- 为了判断具体是哪一个键被按下,使用了行列两次扫描
- 首先 让所有的列输出低电平 所有行输出为高电平 即P1输出0000 1111 (列接的是高四位 行接的是第四位)
- 然后再识别P1低四位即所有行的电平情况
- 若有一行的按键被按下 那么有一行的电平就为0
- 假设P1.2那行有按键被按下 那么识别到P1的电平情况就是 0000 1011 即 0b(使用变量x_temp=0b)
- 如果没有按键按下 那么就不需要进行列扫描了
- 上述情况若检测到行有按键按下
- 那么就进行列扫描:让所有列输出高电平 行输出低电平 即 1111 0000
- 然后识别P1高四位的电平情况
- 若其中有一列中的按键被按下 假设是P1.6那一列有按键被按下
- 那么识别到此时P1=1011 0000 即 b0(使用变量y_temp=b0)
- 然后 x_temp与y_temp相或 得到 bb
- 再取反 得到 0x44(十六进制)
- 然后我们可以理解为 当获得的key_value=44时 那么就是第三行第三列的按键被按下
- 同理 其他的按键对应的key_value也可以被求出
那么为什么第一次扫描完后,10ms后还要再进行扫描一次呢?
答:这是因为存在键位抖动,因为在按下键的那一刹那,有一个不稳定的电平,也就是电位抖动,必须要通过软件或硬件的方法去消除。这里是通过软件的方法消除的。如果没有第二次扫描,那么只按一次键,程序会识别出按下了多次键。如果10ms后,扫描到还是这个键,那么才会确定键被按下。防止抖动带来的bug
运行结果
说明
参考课本:单片机原理与嵌入式系统设计