51单片机——按键实验
由于机械点的弹性作用,按键开关在闭合时不会马上稳定的接通,在断开时也不会一下子断开,因而在闭合和断开的瞬间均伴随着一连串的抖动。抖动时间的长短由按键的机械特性决定的,一般为 5ms 到 10ms,为了确保 CPU 对按键的一次闭合仅作一次处理,必须进行消抖
普中开发板是采用软件消抖,一般来说一个简单的按键消抖就是先读取按键的状态,如果得到按键按下之后,延时 10ms,再次读取按键的状态,如果按键还是按下状态,那么说明按键已经按下。其中延时10ms 就是软件消抖处理
1、独立按键实验
P3.1控制K1, P3.0控制K2, P3.2控制K3, P3.3控制K4
软件去抖动方法:
(1)先设置 IO 口为高电平(由于开发板 IO 都有上拉电阻,所以默认 IO 为高电平)
(2)读取 IO 口电平确认是否有按键按下
(3)如有 IO 电平为低电平后,延时几个毫秒
(4)再读取该 IO 电平,如果仍然为低电平,说明按键按下
(5)执行按键控制程序
要实现的功能是:通过开发板上的独立按键K1控制D1指示灯亮灭
#include "reg51.h"
typedef unsigned int u16;
typedef unsigned char u8;
void delay(u16 time){
while(time--);
}
//控制D1-D4指示灯
sbit LED1=P2^0;
sbit LED2=P2^1;
sbit LED3=P2^2;
sbit LED4=P2^3;
//使用宏定义的方法定义独立按键的键值
#define KEY1_PRESS 1
#define KEY2_PRESS 2
#define KEY3_PRESS 3
#define KEY4_PRESS 4
#define KEY_UNPRESS 0
//定义按键对应的管脚口
sbit KEY1=P3^1;
sbit KEY2=P3^0;
sbit KEY3=P3^2;
sbit KEY4=P3^3;
//封装一个函数,按键返回一个键值
u8 key_scan(u16 mode){ //mode:模式,有0和1操作,如果操作0,单次扫描;如果操作1,连续扫描
//打一个标志
static u16 key=1;
if(mode==1){
key=1;
}
if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0)){
//消抖处理,需要延时5ms-10ms
delay(1000);
key=0;
//如果仍是按下,信号稳定
if(KEY1==0){
return KEY1_PRESS;
}else if(KEY2==0){
return KEY2_PRESS;
}else if(KEY3==0){
return KEY3_PRESS;
}else if(KEY4==0){
return KEY4_PRESS;
}
}else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1){
key=1;
return KEY_UNPRESS;
}
}
void main(){
u8 key=0;
while(1){
key=key_scan(0);
if(key==KEY1_PRESS){
LED1=!LED1;delay(1000); //加一下延时可以验证单次扫描和连续扫描的情况
}else if(key==KEY2_PRESS){
LED2=!LED2;
}else if(key==KEY3_PRESS){
LED3=!LED3;
}else if(key==KEY4_PRESS){
LED4=!LED4;
}
}
}
2、矩阵按键实验
P1端控制矩阵键盘
P1.7连接矩阵键盘的第1行,P1.6连接矩阵键盘的第2行,P1.5连接矩阵键盘的第3行,P1.4连接矩阵键盘的第4行
P1.3连接矩阵键盘的第1列,P1.2连接矩阵键盘的第2列,P1.1连接矩阵键盘的第3列,P1.0连接矩阵键盘的第4列
0 0 0 0 0 0 0 0
--------------------------------------------------------------------------
P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0
独立键盘有一端固定为低电平,此种方式编程比较简单。 而矩阵键盘两端都与单片机 I/O 口相连,因此在检测时需编程通过单片机 I/O 口送出低电平。
单片机 I/O 口送出低电平检测方法有多种,最常用的是行列扫描和线翻转法:
(1)行列扫描法:先送一列为低电平,其余几列全为高电平(此时我们确定了列数),然后立即轮流检测一次各行是否有低电平,若检测到某一行为低电平(这时我们又确定了行数),则我们便可确认当前被按下的键是哪一行哪一列的
(2)线翻转法:就是使所有行线为低电平时,检测所有列线是否有低电平,如果有,就记录列线值;然后再翻转,使所有列线都为低电平,检测所有行线的值,由于有按键按下,行线的值也会有变化,记录行线的值。从而就可以检测到全部按键
2.1 行列扫描法
行列扫描法:先送一列为低电平,其余几列全为高电平(此时我们确定了列数),然后立即轮流检测一次各行是否有低电平,若检测到某一行为低电平(这时我们又确定了行数),则我们便可确认当前被按下的键是哪一行哪一列的
#include "reg51.h"
typedef unsigned int u16;
typedef unsigned char u8;
void delay(u16 time){
while(time--);
}
//使用宏定义定义矩阵按键的管脚
#define KEY_MATRIX_PORT P1
//数码管:静态数码管操作
#define SMG_A_DP_PORT P0
//0-F
u16 gmsg_code[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
u16 key_matrix_scan(){
//定义一个变量返回
u16 key_value=0;
//给第一列赋值0,其余全为1
KEY_MATRIX_PORT=0xf7;
if(KEY_MATRIX_PORT!=0xf7){
//消抖
delay(1000);
//检测行
switch(KEY_MATRIX_PORT){
case 0x77:
key_value=1;
break;
case 0xb7:
key_value=5;
break;
case 0xd7:
key_value=9;
break;
case 0xe7:
key_value=13;
break;
}
}
//等待按键松开
while(KEY_MATRIX_PORT!=0xf7);
//给第二列赋值0,其余全为1
KEY_MATRIX_PORT=0xfb;
if(KEY_MATRIX_PORT!=0xfb){
//消抖
delay(1000);
//检测行
switch(KEY_MATRIX_PORT){
case 0x7b:
key_value=2;
break;
case 0xbb:
key_value=6;
break;
case 0xdb:
key_value=10;
break;
case 0xeb:
key_value=14;
break;
}
}
//等待按键松开
while(KEY_MATRIX_PORT!=0xfb);
//给第三列赋值0,其余全为1
KEY_MATRIX_PORT=0xfd;
if(KEY_MATRIX_PORT!=0xfd){
//消抖
delay(1000);
//检测行
switch(KEY_MATRIX_PORT){
case 0x7d:
key_value=3;
break;
case 0xbd:
key_value=7;
break;
case 0xdd:
key_value=11;
break;
case 0xed:
key_value=15;
break;
}
}
//等待按键松开
while(KEY_MATRIX_PORT!=0xfd);
//给第四列赋值0,其余全为1
KEY_MATRIX_PORT=0xfe;
if(KEY_MATRIX_PORT!=0xfe){
//消抖
delay(1000);
//检测行
switch(KEY_MATRIX_PORT){
case 0x7e:
key_value=4;
break;
case 0xbe:
key_value=8;
break;
case 0xde:
key_value=12;
break;
case 0xee:
key_value=16;
break;
}
}
//等待按键松开
while(KEY_MATRIX_PORT!=0xfe);
//返回值
return key_value;
}void main(){
u16 key=0;
while(1){
key=key_matrix_scan();
if(key!=0){
SMG_A_DP_PORT=gmsg_code[key-1];
}
}
}
2.2 线翻转法
线翻转法,就是使所有行线为低电平时,检测所有列线是否有低电平,如果有,就记录列线值;然后再翻转,使所有列线都为低电平,检测所有行线的值,由于有按键按下,行线的值也会有变化,记录行线的值。从而就可以检测到全部按键
#include "reg51.h"
typedef unsigned int u16;
typedef unsigned char u8;
void delay(u16 time){
while(time--);
}
//使用宏定义定义矩阵按键的管脚
#define KEY_MATRIX_PORT P1
//数码管:静态数码管操作
#define SMG_A_DP_PORT P0
//0-F
u16 gmsg_code[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};u16 key_matrix_flip_scan(){
u16 key_value=0;
//使所有行线为低电平,列线为高电平
KEY_MATRIX_PORT=0x0f;
if(KEY_MATRIX_PORT!=0x0f){
//消抖
delay(1000);
if(KEY_MATRIX_PORT!=0x0f){
//测试列,使所有行线为低电平,列线为高电平
KEY_MATRIX_PORT=0x0f;
switch(KEY_MATRIX_PORT){
case 0x07:
key_value=1; //暂时赋一个值
break;
case 0x0b:
key_value=2;
break;
case 0x0d:
key_value=3;
break;
case 0x0e:
key_value=4;
break;
}
//测试行,使所有列线为低电平,行线为高电平
KEY_MATRIX_PORT=0xf0;
switch(KEY_MATRIX_PORT){
case 0x70:
key_value=key_value;
break;
case 0xb0:
key_value=key_value+4;
break;
case 0xd0:
key_value=key_value+8;
break;
case 0xe0:
key_value=key_value+12;
break;
}
while(KEY_MATRIX_PORT!=0xf0);
}else{
key_value=0;
}
return key_value;
}
}void main(){
u16 key=0;
while(1){
key=key_matrix_flip_scan();
if(key!=0){
SMG_A_DP_PORT=gmsg_code[key-1];
}
}
}