一、前言
在蓝桥的比赛里,按键控制数码管切换界面,按键控制LED灯,按键控制各种外设,每一次的比赛,按键控制数码管切换界面是必考的!而按键的逻辑是比较难的,是蓝桥的一个难点。本文通过独立按键和矩阵键盘两种模式进行讨论。


二、原理图
在开发板上面,按键都是放在一起的,通过J5键帽控制,独立键盘还是矩阵键盘
1、原理图

不管是独立按键还是矩阵键盘,我们判断按键按下的方法都是一样的,都是去检测引脚是不是相同电平。
2、独立按键
内部结构图

从图中看出来,按键右端连接了GND,左端连接了上拉电阻。
通过检测KeyIn1,KeyIn2,KeyIn3,KeyIn4是高电平还是低电平判断按键按下。
没有按键按下时,K1右端连接GND,所以此时的KeyIn1检测就是低电平0,当按键K1按下后,电路被接通,在上拉电阻的作用下,K1此时的电压被拉高,此时KeyIn1的电压测量就是高电平1。
当J5用键帽接1 2时,键盘状态为独立按键

从图里可以看出来,当键盘为独立按键时,S4,S5,S6,S7按键的左端接GND,就是默认左端为低电平0,所以我们要判断按键是否按下,只需要判断S4,S5,S6,S7按键的右端引脚状态即可。S4,S5,S6,S7按键的引脚分别为P33~P30。判断这四个引脚状态即可。
3、矩阵键盘
J5用键帽接2 3,键盘状态为矩阵键盘

当J5接2 3时,S4,S5,S6,S7按键的纵行就是通过引脚P44控制。原理和独立键盘一样都是判断引脚状态为高电平还是低电平。
矩阵键盘是通过按行扫描或者按列扫描来判断按键按下的,所以可以先按行或者列定义引脚的初始化,再通过判断引脚是等于1或者0,判断按键按下。
以按行扫描为例
比如:我们要判断S10按键按下,如果S10按键按下,就是P42和P31两个引脚接通,所以他们的电平是相同的。先定义P44 = 1,P42 = 1;P35 = 1;P34 = 1;P33 = 1;P32=0;P31 = 1;P30 = 1;这样做的目的是让按键相互不影响。然后判断P42和P31两个引脚是不是相同,如果相同,我们就可以认为S10按键按下。
三、代码实现功能
1、独立按键控制LED灯亮灭
(1)按下S4,LED灯的L1亮起。
(2)按下S5,LED灯的L8亮起。
(3)按下S6,LED灯全部亮起。
(4)按下S7,LED灯全部熄灭。
要求按下按键后LED灯立刻电亮/熄灭,放手后LED灯不熄灭,长按不影响LED灯。
#include <STC15F2K60S2.H>
sbit K5 = P3^3;
sbit K6 = P3^2;
sbit K7 = P3^1;
sbit K8 = P3^0;
void Delay20ms() //@11.0592MHz
{
unsigned char data i, j;
i = 216;
j = 37;
do
{
while (--j);
} while (--i);
}
void Scan_Key()
{
if(K5 == 0||K6 == 0||K7 == 0||K8 == 0)
{
Delay20ms(); //按键去抖
if(K5 == 0) //S4按键按下
{
P0 = 0xfe;
}
if(K6 == 0) //S5按键按下
{
P0 = 0x7f;
}
if(K7 == 0) //S6按键按下
{
P0 = 0x00;
}
if(K8 == 0) //S7按键按下
{
P0 = 0xff;
}
}
}
void main()
{
P2 = (P2 & 0x1f) | 0x80; //打开锁存器通道指向LED灯
P0 = 0xff;
while(1)
{
Scan_Key();
}
}
2、矩阵键盘控制数码管显示数字
(1)矩阵键盘每一个按键控制一个数字,如图

(2)数码管第一位显示,其他位置不显示,不出现频闪,过亮,过暗现象。
要求按下按键后数码管立刻电亮,放手后数码管不熄灭,长按不影响数码管显示效果。
#include <STC15F2K60S2.H>
sbit K1 = P4^4;
sbit K2 = P4^2;
sbit K3 = P3^5;
sbit K4 = P3^4;
sbit K5 = P3^3;
sbit K6 = P3^2;
sbit K7 = P3^1;
sbit K8 = P3^0;
code unsigned char Seg_Table[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
void Delay20ms() //@11.0592MHz
{
unsigned char data i, j;
i = 216;
j = 37;
do
{
while (--j);
} while (--i);
}
void Nixie_pos_num(unsigned char dat)
{
P2 = (P2 & 0x1f) | 0xc0; //打开锁存器通道指向数码管位选
P0 = 0x01;
P2 = (P2 & 0x1f) | 0xe0; //打开锁存器通道指向数码管段选
P0 = Seg_Table[dat];
}
void Scan_Key()
{
//按行扫描
//第一行扫描
K1 = K2 = K3 = K4 = K5 = K6 = K7 = 1;K8 = 0;
if(K1 == 0||K2 == 0||K3 == 0||K4 == 0)
{
Delay20ms(); //去抖
if(K1 == 0) //s7按键按下
{
Nixie_pos_num(0); //显示数字0
}
if(K2 == 0) //s11按键按下
{
Nixie_pos_num(1); //显示数字1
}
if(K3 == 0) //s15按键按下
{
Nixie_pos_num(2); //显示数字2
}
if(K4 == 0) //s19按键按下
{
Nixie_pos_num(3); //显示数字3
}
}
//第二行扫描
K1 = K2 = K3 = K4 = K5 = K6 = K8 = 1;K7 = 0;
if(K1 == 0||K2 == 0||K3 == 0||K4 == 0)
{
Delay20ms(); //去抖
if(K1 == 0) //s6按键按下
{
Nixie_pos_num(4); //显示数字4
}
if(K2 == 0) //s10按键按下
{
Nixie_pos_num(5); //显示数字5
}
if(K3 == 0) //s14按键按下
{
Nixie_pos_num(6); //显示数字6
}
if(K4 == 0) //s18按键按下
{
Nixie_pos_num(7); //显示数字7
}
}
//第三行扫描
K1 = K2 = K3 = K4 = K5 = K7 = K8 = 1;K6 = 0;
if(K1 == 0||K2 == 0||K3 == 0||K4 == 0)
{
Delay20ms(); //去抖
if(K1 == 0) //s5按键按下
{
Nixie_pos_num(8); //显示数字8
}
if(K2 == 0) //s9按键按下
{
Nixie_pos_num(9); //显示数字9
}
if(K3 == 0) //s13按键按下
{
Nixie_pos_num(10); //显示数字10
}
if(K4 == 0) //s17按键按下
{
Nixie_pos_num(11); //显示数字11
}
}
//第四行扫描
K1 = K2 = K3 = K4 = K6 = K7 = K8 = 1;K5 = 0;
if(K1 == 0||K2 == 0||K3 == 0||K4 == 0)
{
Delay20ms(); //去抖
if(K1 == 0) //s4按键按下
{
Nixie_pos_num(12); //显示数字12
}
if(K2 == 0) //s8按键按下
{
Nixie_pos_num(13); //显示数字13
}
if(K3 == 0) //s12按键按下
{
Nixie_pos_num(14); //显示数字14
}
if(K4 == 0) //s16按键按下
{
Nixie_pos_num(15); //显示数字15
}
}
}
void main()
{
P2 = (P2 & 0x1f) | 0x80;
P0 = 0xff;
while(1)
{
Scan_Key();
}
}
四、补充
按键去抖
在按键按下的时候,有时候并不是直接按下电路接通,有时候按下的力度,时间都会影响按键的接触,如果每一次的按键接触,CPU都需要产生反应,会大大降低CPU处理的速度。所以我们需要判断按是不是真的按下了,排除按键抖动的干扰,这就需要在按键按下时,要先延时一段时间,确定按键是一直接触着的,才让CPU做出反应。Delay20ms(); 的作用就是去抖,排除抖动的干扰。