51入门系列教程| 第一次被控制--按键
前面一贴简单讨论了一下51单片机的IO输出功能
既然是IO,有output肯定就有input
今次咱聊聊input,输入功能
有了input,51就能够根据输入的信号
去做出下一步工作的决定
其实单片机的输入不外乎就是两种
一是数字输入,由单片机去判断输入的高低电平
再就是模拟输入,最典型的就是内置ADC外设的单片机
可以把外部输入的电压值与基准电压进行比较
从而得到一定数值的二进制编码
譬如基准电压3.3v,12位的线性ADC外设
就可以将外部输入的0-3.3v电压按比例用0-4096的十进制数去表示
换成二进制便是000000000000-111111111111
限于手头上51片子没有AD这个外设
咱今天聊的输入,就从数字输入下手
一、按键基本原理
先上电路
先看上面一个图
P0.1口,连接到一个10k的电阻,上拉到电源Vcc(这里Vcc=5.0v)
同时连接到按键的一端
按键的另一端则直接连到地
简单分析一下这个电路
当按键没有按下的时候
10k电阻和P0.1口没有连接到地
由于P0端口是开漏输出,可以看做是悬空
那么经过10k电阻的电流为0,即10k电阻的压降为0
这个时候,可以认为P0.1口的电位是5.0v,一个高电平
So,按键没有按下,单片机可以认为P0.1口输入了一个逻辑“1”,也就是高电平
当按键按下时
P0.1口和10k电阻均被连接到了地
这个时候,P0.1口的电位直接被拉到0v,也就是地
So,单片机可以认为P0.1口输入了一个逻辑“0”,也就是低电平
这里尤其注意一下这个10K电阻的作用
名曰上拉电阻
它的存在是非常有必要的
没有这个电阻,P0.1口直接连接到按键一端或者电源,都会产生不好的后果
如果直接连至电源,按键按下的时候,Vcc和地……,后果你懂的……
如果直接连接到按键的一端,在按键按下前
P0.1都是悬空的,外界的干扰(EMI之类),非常容易引起单片机的误判
它的作用主要是按下前稳定电平、按下后限流
在按键按下的时候,电源—10k电阻—地 这个回路中
会产生5/100000=5mA电流,产生一定的功耗
有朋友会想,加大电阻就可以解决这个问题了呀
但是电阻太大的话,可能会出现开路情况
So,几k到几十k是比较合适的
再看下面一个图
嗯,简单的发光二极管回路
这里就不多解释了
顺道拿电木板焊了个电路
电源、地和2个IO口用排针等插
菊花面,10k上拉电阻和220欧LED限流电阻
这个电木板质量真次,焊盘掉了好几个
51读端口的代码特别简单
只要直接按位或者按port来逐位读取即可
譬如要把P01口的逻辑值赋给变量temp
直接temp = P01即可
P01输入一个低电平,temp=0x00
P01输入一个高电平,temp=0x01
上个测试代码
#include <reg51.h>
sbit P11 = P1^1;//按位定义
sbit P01 = P0^1;
int main()
{
while(1)
{
if(P01==0)//判断P01口的值,0为按下
{P11 = ~P11;}//按下后的操作,P11口取反
else (P11 = P11;)
}
return 0;
}
简单阅读一下代码
大概的功能就是判断P01口电平是否为0
为0的话,P11口电平取反,也就是LED点亮或者熄灭
编译下载,看看结果
不难发现
按键虽然能够控制51去点亮或者熄灭LED
但是似乎并不是那么顺手
不能非常准确地操作LED的亮或者灭
为嘛?
二、去抖
在解决上面问题之前
必须意识到,按键其实是一个机械弹性开关结构
在按下或者松开的瞬间,也就是机械触点断开、闭合时
不会稳定地接通或者断开,存在一连串的抖动
这种抖动时间与按键的机械特性有关,几个ms到几十个ms都有可能
由于51执行代码的速度是us级
So,自己感觉只按下了一次按键
其实51已经反复多次执行了按键按下的操作
所以造成上面gif中的现象
所以,按键去抖,是无论哪种单片机都需要面临的问题
当然,有些比较有特点的单片机会有按键相关的外设,无需过多关注去抖,这个在此不讨论
有关去抖,有硬件和软件两种办法
硬件上去抖的办法
第一种是使用RS触发器/锁存器
锁存器的工作特点就决定了,即使有按键抖动,也不会影响上如两个与非门构成的RS锁存器的输出
再一种是在按键上并联一个合适大小的电容
其实就是利用电容充放电的特性,无它
硬件去抖,基本上不占用单片机的资源
致命的缺陷是需要额外的器件
会使PCB面积、BOM、成本有所增加
在成本为王的时代,基本上这种办法很少有人使用了
但是非常可靠
再来看看软件去抖
第一种是使用延时去抖,是目前用得比较多的
上个代码
#include <reg51.h>
sbit P11 = P1^1;
sbit P01 = P0^1;
unsigned char flag;
void delay_ms(unsigned int xms)//定义一个ms级的延时函数
{
unsigned int i,j;
for(i=xms;i>0;i--)
{
for(j=124;j>0;j--);
}
}
unsigned char keyscan()//扫描按键
{
unsigned char key_value;
if(P01 == 0)//P01被拉低,也就是有按键按下
{
delay_ms(100);//延时100ms
if(P01 == 0) //延时后再判断P01口是不是被拉低(按下)
{key_value = 1;}//如果是,键值位置1
else {key_value = 0;}//否则置0
}
return key_value;//返回键值
}
void key_op()
{
if(flag) //判断标志位,然后进行操作
{
P11 = ~P11;
flag = 0;
}
else {;}
}
int main()
{
while(1)
{
flag = keyscan();//把扫描的键值赋给标志位
key_op();//根据标志位进行操作
}
return 0;
}
简单分析一下上面的代码
最最核心的就是
如果发现P01的按键被按下
就延时100ms后再读一下P01的值
注意一下,这里的100ms其实和按键的机械特性有关
根据实际情况调整
如果还是被按下的状态,则认为按键确实被按下
单片机再进行下一步操作
上个GIF
可以发现
按键已经可以很准确地控制LED的亮灭了
但是,单片在执行delay_ms(100)的时候
其它任何事情都不能做呀
所以很多时候,这个延时会采用定时器中断来完成
这个后面再讨论
其实还有一种不需要延时的去抖方法
可以理解成一个状态机
上代码
#include <reg51.h>
sbit P11 = P1^1;
sbit P01 = P0^1;
unsigned char flag;
void delay_ms(unsigned int xms)
{
unsigned int i,j;
for(i=xms;i>0;i--)
{
for(j=124;j>0;j--);
}
}
unsigned char keyscan()
{
static unsigned char key_state = 0;
static unsigned char key_value = 0;
unsigned char key_press,key_return = 0;
key_press = P01; //读按键
switch(key_state)
{
case 0 ://按键初始态
if(key_press == 0){key_state = 1;}//按键被按下,但需要确认
break;
case 1://按键进行确认
if(key_press == 0)//如果还是被按下,则开始决定键值
{
key_value = 1;
key_state = 2;//确定被按下,转到按键被按下状态
}
else { key_value = 0;}//否则认为是抖动
break;
case 2://按键释放状态
if(key_press == 1)
{
key_return = key_value; //按键释放后输出键值
key_value = 0;
key_state = 0; //按键释放,进入按键初始态
}
break;
}
return key_return; //返回键值
}
void key_op()
{
if(flag)
{
P11 = ~P11;
flag = 0;
}
else {;}
}
int main()
{
while(1)
{
flag = keyscan();
key_op();
}
return 0;
}
利用状态机的编程思想
代码上有解释了,这里不过多深入讨论
上GIF
可以看得出来
其实效果也还不错
三、矩阵键盘
到这里,一个基本键盘需要考虑的东西
大概就是这么多了
还有一种针对比较多按键的电路组态
叫做是矩阵键盘
简单介绍一下吧
上个结构图
上图是个4x4键盘,16个按键
按照前面的方法,得需要16个IO口才能完成接入
这里其实只需要P1口的8个端口即可
这种矩阵键盘判断按键的方法有好几种
用的比较多的有2种
一是行/列扫描法,分两步
1、判断键盘中有无键按丿将全部行线Y0-Y3置低电平,然后检测列线的状态。只要有一列的电平为低,则表示键盘中有键被按下,而且闭合的键位于低电平线丿根行线相交叉皿个按键之中。若所有列线均为高电平,则键盘中无键按下
2、判断闭合键所在的位置 在确认有键按下后,即可进入确定具体闭合键的过程。其方法是:依次将行线置为低电平,即在置某根行线为低电平时,其它线为高电平。在确定某根行线位置为低电平后,再逐行检测各列线的电平状态。若某列为低,则该列线与置为低电平的行线交叉处的按键就是闭合的按键
再一个是翻转法,三步
1、行线输出全为0,读出列线值。
2、列线输出上次读入的列线值。
3、读入行线值,并与前次列线值组合,生成组合码值。根据这个组合码来确定被按下的按键。
没来得及做个矩阵键盘
如有疑问,可把问题发送给“云汉电子社区”微信公众号平台,我们会及时回复,关注公众号可阅读更多51系列教程!我们欢迎您的沟通!
请先 后发表评论~