009-独立按键与矩阵按键
一、独立按键
-
原理图:
-
基本原理:按下为后,P3^1低电平。松开为高电平。
实验一:实现按动一下独立按键1,LED模块第一个灯的状态变化一次
#include<reg52.h>
//按动一下独立按键1,LED模块第一个灯的状态变化一次
//原理:按键按下时,P3^1的电压为低电平
//定义使用的IO口
sbit K1 = P3^1;
sbit LED = P2^0;
unsigned int i;
//延时函数的申明
void delay(int i);
//按键处理函数申明
void keypush();
//主函数
void main()
{
LED = 1;
while(1)
{
keypush();
}
}
//延时函数
void delay(int i)
{
while(i--);
}
//按键处理函数,判定K1是否按下
void keypush()
{
if(K1 == 0) //检测按键是否按下
{
delay(1000); //消除抖动,一般为10ms
if(K1 == 0) //再次判断按键是否按下
{
LED = ~LED; //小灯状态改变一次
}
while(!K1); //检测按键是否松开,如果松开了,则继续下一次检测;如果没松开,保持现有状态
}
}
矩阵按键
-
原理图:
为了便于理解,修改效果如下图,与原有图的原理相同,其中O1至O4表示按键输入,I1至I4表示按键输出
-
基本原理:(以S1工作为例)
P1输入0x7F(0111 1111) => 此时P1 ^ 3为高电平 => 按下S1->VCC、电阻,S1、P1^7形成通路 => P1 ^ 3=P1 ^ 7为低电平。
实验二:实现依次按动矩阵按键第一行S1至S4四个,LED模块D1至D4依次点亮
#include<reg52.h> //头文件
//功能:依次按动矩阵按键第一行S1至S4四个,LED模块D1至D4依次点亮
//定义使用的IO口
sbit S1 = P1^3;
sbit S2 = P1^2;
sbit S3 = P1^1;
sbit S4 = P1^0;
sbit LED1 = P2^0;
sbit LED2 = P2^1;
sbit LED3 = P2^2;
sbit LED4 = P2^3;
//主函数
void main()
{
P1 = 0x7F; //0111 1111
while(1)
{
LED1 = S1; //如果S1按下,P1^3接通P1^7,由1->0,使得P2^0 = 0;
LED2 = S2;
LED3 = S3;
LED4 = S4;
}
}
实验三:实现使用S1按键控制数码管最低位显示,按动一下数码管值加1,到9后再次由0开始
#include<reg52.h> //头文件
//功能:使用S1按键控制数码管最低位显示,按动一下数码管值加1,到9后再次由0开始
//定义使用的IO口
sbit S1 = P1^3;
//数码管的真值表
unsigned char shu_ma_guan[16] =
{
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
};
//主函数
void main()
{
bit biao_zhi = 1; //默认为1,表示按键此时为弹起状态
unsigned char cnt = 0;
P2 = 0x00; //选择最低位数码管
P1 = 0x7F; //0111 1111
while(1) //检测按键
{
if(S1 != biao_zhi) //如果S1状态和上一次检测的状态不同
{
if(biao_zhi == 0) //确认按键被按下
{
cnt++; //数字加1
if(cnt == 16) //加到F时
{
cnt = 0; //再次从0开始
}
P0 = shu_ma_guan[cnt]; //数码管显示对应数字
}
biao_zhi = S1; //将上一次的按键状态送到标志位
}
}
}
以上实验存在问题:按键按下一次,数字增加不一定是1,存在抖动。
-
按键抖动:对于一般情况下,按动按键并不会立马接通,存在一个抖动时间。
-
解决方案:
-
硬件电容消抖:抖动本身是高频信号,通过电容消除。但是这会增加硬件成本,还需要焊接等。
-
软件消抖(常用方法)
实验四:采用软件消抖来解决上面【实验二】中的问题,代码如下:
#include<reg52.h> //头文件
//功能:使用S1按键控制数码管最低位显示,按动一下数码管值加1,到9后再次由0开始
//定义使用的IO口
sbit S1 = P1^3;
void delay(int i);
//数码管的真值表
unsigned char shu_ma_guan[16] =
{
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
};
//主函数
void main()
{
bit biao_zhi = 1; //默认为1,表示按键此时为弹起状态
bit keybuf = 1; //S1的暂存位
unsigned char cnt = 0;
P2 = 0x00; //选择最低位数码管
P1 = 0x7F; //0111 1111
while(1) //检测按键
{
keybuf = S1; //将S1值存入keybuf中
if(keybuf != biao_zhi) //如果S1状态和上一次检测的状态不同
{
delay(1000); //延长一段时间,消除抖动
if(keybuf == S1) //再次判断S1值和延时之前是否一致
{
if(biao_zhi == 0) //确认按键被按下
{
cnt++; //数字加1
if(cnt == 16) //加到F时
{
cnt = 0; //再次从0开始
}
P0 = shu_ma_guan[cnt]; //数码管显示对应数字
}
biao_zhi = S1; //将上一次的按键状态送到标志位
}
}
}
}
//定义延时函数
void delay(int i)
{
while(i--);
}
*备注:实际开发中不允许在while中使用delay语句。
实验五:更好的解决抖动的算法案例
连续读取八个状态,如果相同,表示按下或者开启,然后移位一位,再次读取八个状态,检测是否一样,如果一样,则不在抖动区,去过八位数字不同,则存在抖动,继续这样移位读取判断下去。
例如:
1111 1111 1111 1010 0010 0000 0000 0000 0000 0001 1001 0111 1111 1111 1111
使用本算法解决【实验二】的抖动问题,代码如下:
#include<reg52.h> //头文件
//功能:使用S1按键控制数码管最低位显示,按动一下数码管值加1,到9后再次由0开始
//定义使用的IO口
sbit S1 = P1^3;
//数码管的真值表
unsigned char shu_ma_guan[16] =
{
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
};
bit keysta = 1; //按键的状态,默认弹起
//主函数
void main()
{
bit biao_zhi = 1; //默认为1,表示按键之前为弹起状态
unsigned char cnt = 0;
P2 = 0x00; //选择最低位数码管
P1 = 0x7F; //0111 1111
TMOD = 1; //选择定时器工作模式
TH0 = 0xF8; //定时器定时2ms
TL0 = 0x30;
TR0 = 1; //打开定时器
EA = 1; //打开中断总开关
ET0 = 1; //打开T0中断开关
while(1)
{
if(keysta != biao_zhi) //如果按键状态和之前状态不同
{
if(biao_zhi == 0) //确认按键被按下
{
cnt++; //数字加1
if(cnt == 16) //加到F时
{
cnt = 0; //再次从0开始
}
P0 = shu_ma_guan[cnt]; //数码管显示对应数字
}
biao_zhi = keysta; //将现在的状态存起来以备和瑕疵状态比较
}
}
}
//中断函数
void InterruptTime0() interrupt 1
{
static unsigned char keybuf = 0xFF; //假设开始八位全部为1
TH0 = 0xF8;
TL0 = 0x30;
keybuf = (keybuf << 1) | S1;
//XXXX XXX0 | 0000 000S1,如果按键按下,S1为0 ,按位或结果XXXX XXX0;如果按键弹起,按位或结果XXXX XXX1
if(keybuf == 0x00)
{
keysta = 0; //按键按下
}
else if(keybuf == 0xFF)
{
keysta = 1; //按键弹起
}
else //其他状态,都认为是抖动
{
//啥也不干
}
}
实验六:4×4矩阵按键S1至S16表示0至F这十六个数,按动对应的按键在数码管最低位显示对应数字
#include<reg52.h> //头文件
//功能:4×4矩阵按键S1至S16表示0至F这十六个数,按动对应的按键在数码管最低位显示对应数字
//定义使用的IO口
sbit KeyOut1 = P1^7;
sbit KeyOut2 = P1^6;
sbit KeyOut3 = P1^5;
sbit KeyOut4 = P1^4;
sbit KeyIn1 = P1^3;
sbit KeyIn2 = P1^2;
sbit KeyIn3 = P1^1;
sbit KeyIn4 = P1^0;
//数码管的真值表
unsigned char shu_ma_guan[16] =
{
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
};
//默认全部按键弹起状态
unsigned char Keysta [4][4] =
{
{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}
};
//主函数
void main()
{
unsigned char i,j;
//按键值备份,保存前一次的值,默认全部弹起
unsigned char biao_zhi [4][4] =
{
{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}
};
TMOD = 0x01;//设置T0为工作模式1
TH0 = 0xFC; //定时器定时2ms
TL0 = 0x18;
TR0 = 1; //打开定时器
EA = 1; //打开中断总开关
ET0 = 1; //打开T0中断开关
P2 = 0x00; //选择最低位数码管
P0 = 0x00; //数码管全灭
while(1)
{
for(i=0; i<4; i++) //循环检测4*4的矩阵按键
{
for(j=0; j<4; j++)
{
if(Keysta[i][j] != biao_zhi[i][j]) //检测按键动作
{
if(biao_zhi[i][j] == 0) //按键按下时执行动作
{
P0 = shu_ma_guan[i*4+j]; //将按键编号显示到数码管
}
biao_zhi[i][j] = Keysta[i][j]; //更新前一次的备份值
}
}
}
}
}
//中断函数
void InterruptTime0() interrupt 1
{
unsigned char i = 0;
static unsigned char Keyout = 0; //矩阵按键扫描输出索引
//矩阵按键扫描缓冲区,假设开始八位全部为1
static unsigned char Keybuf[4][4] =
{
{0xFF,0xFF,0xFF,0xFF},{0xFF,0xFF,0xFF,0xFF},
{0xFF,0xFF,0xFF,0xFF},{0xFF,0xFF,0xFF,0xFF}
};
TH0 = 0xFC; //重新加载初值
TL0 = 0x18;
//将一行的4个按键值移入缓冲区
Keybuf[Keyout][0] = (Keybuf[Keyout][0] << 1)| KeyIn1;
Keybuf[Keyout][1] = (Keybuf[Keyout][1] << 1)| KeyIn2;
Keybuf[Keyout][2] = (Keybuf[Keyout][2] << 1)| KeyIn3;
Keybuf[Keyout][3] = (Keybuf[Keyout][3] << 1)| KeyIn4;
//消抖后更新按键状态
for(i=0; i<4; i++) //每行4个按键,所以循环4次
{
if((Keybuf[Keyout][i] & 0x0F) == 0x00)
{ //连续4次扫描值为0,即4*4ms内都是按下状态时,可认为按键已稳定的按下
Keysta [Keyout][i] = 0;
}
else if((Keybuf[Keyout][i] & 0x0F) == 0x0F)
{ //连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起
Keysta [Keyout][i] = 1;
}
}
//执行下一次的扫描输出
Keyout++; //输出索引递增
Keyout = Keyout & 0x03; //索引值加到4即归零
switch(Keyout) //根据索引,释放当前输出引脚,拉低下次的输出引脚
{
case 0: KeyOut4 = 1; KeyOut3 = 1;KeyOut2 = 1;KeyOut1 = 0;break;
case 1: KeyOut4 = 1; KeyOut3 = 1;KeyOut2 = 0;KeyOut1 = 1;break;
case 2: KeyOut4 = 1; KeyOut3 = 0;KeyOut2 = 1;KeyOut1 = 1;break;
case 3: KeyOut4 = 0; KeyOut3 = 1;KeyOut2 = 1;KeyOut1 = 1;break;
default:break;
}
}
本文来自博客园,作者:shihao_Yang,转载请注明原文链接:https://www.cnblogs.com/Yang-shihao/p/14379852.html