晨辉教你轻松学51按键篇.docx
- 文档编号:10579970
- 上传时间:2023-02-21
- 格式:DOCX
- 页数:13
- 大小:43.50KB
晨辉教你轻松学51按键篇.docx
《晨辉教你轻松学51按键篇.docx》由会员分享,可在线阅读,更多相关《晨辉教你轻松学51按键篇.docx(13页珍藏版)》请在冰豆网上搜索。
晨辉教你轻松学51按键篇
晨辉教你轻松学51--------按键篇
对于一个由单片机为核心构成的系统而言。
输入通道是相当重要的。
可以看到几乎每一样基于单片机的产品都有人机交互的部分。
如各种仪器设备上的各种按钮和开关,以及我们手机上的键盘,MP3上的按键等等。
最常见的输入部分,莫非就是按键了。
对于大多数初学者而言,编写一个好的按键程序是一件颇为头疼的事情。
于是乎在网上乱搜一气,程序倒是找到了不少,但是看了半天依然是不明白。
或者在某某论坛上面发帖“跪求XX按键程序,大虾帮忙……”如果你偶然间进了这个论坛,又偶然看到了这个帖子,而且恰好你对按键程序的写法也不是很清楚,那么我希望你能够静静的看完这个帖子。
如果你觉得对你很有帮助,那么我希望你能够在以后的日子中能够坚持到这个论坛来,一起交流学习,分享自己学习过程中的喜悦或者一起探讨棘手的问题,这是我写这个帖子的最大的初衷了。
OK,不能再说了,再说就变成水帖了。
那么我们开始吧。
按键的种类很多。
不过原理基本相似。
下面我们以一种轻触开关为例讲解按键程序的写法。
这种轻触开关大家不陌生吧^_^
一般情况下,按键与单片机的连接如下面这幅图所示。
(图中电阻值一般去4.7k~10k之间,对于内部端口有上拉电阻的单片机则可省略此电阻)
单片机对于按键的按下与否则是通过检测相应引脚上的电平来实现的。
对于上图而言,当P17引脚上面的电平为低时,则表示按键已经按下。
反之,则表明按键没有按下。
我们在程序中只要检测到了P17引脚上面的电平为低了,就可以判断按键按下。
呵呵,简单吧。
等会,您先别乐呵,话还没说完呢。
下面我们来看看,当按键按下时,P17引脚上面的波形是怎么变化的。
上图是一个理想波形图,当按键按下时,P17口的电平马上被拉低到0V了。
当然理想的东西都是不现实的。
所以我们还是看看现实的波形图吧。
看出什么区别来了没。
呵呵,只要你不是傻子我相信都能看出其中的区别。
由于按键的机械特性。
当按键闭合时,并不能马上保存良好的接触,而是来回弹跳。
这个时间很短,我们的手根本感觉不出来。
但是对于一秒钟执行百万条指令的单片机而言,这个时间是相当的长了。
那么在这段抖动的时间内,单片机可能读到多次高低电平的变化。
如果不加任何处理的话,就会认为已经按下,或者松开很多次了。
而事实上,我们的手一直按在按键上,并没有重复按动很多次。
要想能够正确的判断按键是否按下就要避开这段抖动的时间。
根据一般按键的机械特点,以及按键的新旧程度等而言,这段抖动的时间一般在5MS~20MS之间。
看到这里你明白了该如何做了吧。
看看下面的这个流程图,你应该不陌生吧。
这个流程是好多教科书上的做法。
可惜,误导了好多人。
为什么呢。
因为它根本就没有考虑实际情况。
我们根据这幅流程图来写它的代码看看。
unsignedcharv_ReadKey_f(void)
{
unsignedcharKeyPress;
if(P17==0)
{
Delay(20); //延时20MS
If(P17==0)
{
KeyPress=1;
While(!
P17); //等待释放
}
else
KeyPress=0;
}
}
这样一个程序,相信对很多初学者而言都不陌生。
因为好多书上基本都是这样的一个流程和写法。
可是当有一天,我们想做一个数码管加按键调整的时钟,发现当我们按键按下去的时候,数码管就不亮了。
为什么呢。
原因就在这个键盘扫描函数。
平常没有按键按下还好。
一旦有键按下,它先是浪费了CPU的大部分时间(就是那个什么事情都没做的延时20MS函数)然后,又霸占CPU(就是哪个死死等在那里的while(P17);语句)直到按键释放。
对于这种情况我们是忍无可忍的,那么就让我们彻底的抛弃它吧。
那么到底按键扫描函数改如何写呢……..所谓众里寻她千XX,蓦然回首,那人却在灯火阑珊处。
如果我们把CPU延时的那20MS拿出来去做其它事情,那么不就充分利用CPU的时间了吗。
而一般情况下我们只要前沿去抖动就可以了。
也就是说了,我们只需在按键按下后去抖就可以了,对于按键的释放抖动可以不必要过于关注。
当然这主要和应用的场合有关。
一个能有效识别按键按下并支持连发功能的按键已经能够应用到大多数的场合了。
下面以四个独立按键的处理程序为例来讲解(支持单击和连发)
#include"regx52.h"
sbitKeyOne=P1^0;
sbitKeyTwo=P1^1;
sbitKeyThree=P1^2;
sbitKeyFour=P1^3;
#defineuint16unsignedint
#defineuint8unsignedchar
#defineNOKEY 0xff
#defineKEY_WOBBLE_TIME500 //去抖动时间(待定)
#defineKEY_OVER_TIME15000 //等待进入连击时间(待定),该常数要比正常//按键时间要长,防止非目的性进入连击模式
#defineKEY_QUICK_TIME1000 //等待按键抬起的连击时间(待定)
voidv_KeyInit_f(void)
{
KeyOne=1; //按键初始化(相应端口写1)
KeyTwo=1;
KeyThree=1;
KeyFour=1;
}
uint8u8_ReadKey_f(void)
{
staticuint8LastKey=NOKEY; //保存上一次的键值
staticuint16KeyCount=0; //按键延时计数器
staticuint16KeyOverTime=KEY_OVER_TIME;//按键抬起时间
uint8KeyTemp=NOKEY; //临时保存读到的键值
KeyTemp=P1&0x0f; //读键值
if(KeyTemp==0x0f)
{
KeyCount=0;
KeyOverTime=KEY_OVER_TIME;
returnNOKEY; //无键按下返回NOKEY
}
else
{
if(KeyTemp==LastKey) //是否第一次按下
{
if(++KeyCount==KEY_WOBBLE_TIME) //不是第一次按下,则判断//抖动是否结束
{
returnKeyTemp; //去抖动结束,返回键值
}
else
{
if(KeyCount>KeyOverTime)
{
KeyCount=0;
KeyOverTime=KEY_QUICK_TIME;
}
returnNOKEY;
}
}
else //是第一次按下则保存键值,以便下次执行此函数时与读到的键值作比较
{
LastKey=KeyTemp; //保存第一次读到的键值
KeyCount=0; //延时计数器清零
KeyOverTime=KEY_OVER_TIME;
returnNOKEY;
}
}
}
下面是我测试用的主程序(相关头文件未列出,仅仅作测试演示用)
voidmain(void)
{
uint8KeyValue;
int16Count;
v_LcdInit_f();
v_KeyInit_f();
CLS
LOCATE(3,1)
PRINT("KeyTest")
LOCATE(6,2)
SHOW_ICON
while
(1)
{
KeyValue=u8_ReadKey_f();
if(KeyValue!
=NOKEY)
{ LOCATE(1,2)
if(KeyValue==0x0e)Count++;
if(KeyValue==0x0d)Count--;
if(KeyValue==0x0b)Count=0;
if(KeyValue==0x07)Count=0;
HIDE_ICON
PRINTD(Count,5)
LOCATE(6,2)
}
else
{
//SHOW_ICON
}
}
}
每次执行读键盘函数时,只是对一些标志进行判断,然后退出。
因此能够充分的利用CPU的资源。
同时可以处理连发按键。
此按键扫描按键函数可以直接放在主函数中。
如果感觉按键太过灵敏或者迟钝则改一下相关消抖动的宏定义即可。
此函数也可以通过中断标志位进行定时的扫描。
此时,需要添加一个定时标志位,并将相关消抖动的和连击时间的宏定义改小即可。
然后在主程序类似下面这样写即可
if(KeyTime) //定时扫描时间到
{
KeyValue=u8_ReadKey_f();
}
具体的工作就交给您去完成啦。
看看效果:
按键单击
连发时候的截图
至此,关于单个按键的学习就告一段落了,您是否已经明白了。
如果您还不明白,那么把这个程序好好的看看,并画下流程图,分析分析。
估计您就会恍然大悟。
关键是思路要转换过来。
下面我们来看看多个按键的情况吧
一般情况下,如果多个按键每个都直接接在单片机的I/O上的话会占用很多的I/O资源。
比较合理的一种做法是,按照行列接成矩阵的形式。
按键接在每一个的行列的相交处。
这样对于m行n列的矩阵,可以接的按键总数是m*n。
这里我们以常见的4*4矩阵键盘来讲解矩阵键盘的编程。
上图就是矩阵键盘的一般接法。
这里我们要介绍一种快速的键盘扫描法:
线反转法(或者称为行列翻转法)。
具体流程如下。
首先,让单片机的行全部输出0,列全部输出1,读取列的值(假设行接P3口的高四位,列接低四位)。
即P3=0x0f;此时读列的值,如果有键按下,则相应的列读回来的值应该为低。
譬如此时读回来的值为0x0e;即按键列的位置已经确定。
这时反过来,把行作为输入,列作为输出,即P0=0xf0;此时再读行的值,如果按键仍然被按下,则相应的行的值应该为低,如果此时读回来的值为0xe0,则确定了行的位置。
说到这里,您应该笑了,知道了一个按键被按下的行和列的位置,那么就可以肯定确定它的位置了。
我们把读回来的行值和列值进行或运算。
即0xe0|0x0e即0xee。
那么0xee就是我们按下的按键的键值了。
怎么样。
只需几步就可以判断所有的键值,简单吧。
下面再结合一个例子具体看看。
/******************************************
*此模块所需相关支持库 *
******************************************/
#include"regx52.h"
#defineuint8unsignedchar
#defineuint16unsignedint
/****************************************
*与硬件连接相关的定义及宏定义和操作宏 *
*****************************************/
#defineKEYBOARD P3 //键盘连接到单片机上的端口位置
#defineREAD_ROW_ENLABLE KEYBOARD=0x0f; //读端口之前先把相应口置位(由基本51单片机特性决定的)
#defineREAD_COL_ENLABLE KEYBOARD=0xf0; //根据实际硬件连接情况修改
/*****************************************
*模块内相关的宏定义及常数宏 *
******************************************/
#defineNOKEY 0xff //定义无键按下时的返回值
#defineDELAY_COUNT 2 //消抖时间常数
/*****************************************
*此模块所需的全局或者外部变量 *
*****************************************/
bitbdataStartScan=0;//此变量需放在定时中断中置位
/*****************************************
*按键扫描函数,按下去后经去抖,确定按下*
*则返回键值0~15;无键按下则返回0xff; *
*此函数需要定时器的支持(去抖....) *
*****************************************/
uint8u8_KeyBoardScan_f()
{
staticuint8DelayCount=0;
uint8KeyValueRow=0;
uint8KeyValueCol=0;
uint8KeyValue=0;
if(StartScan) //开始扫描,StartScan在定时中断中置位
{
StartScan=0; //清除开始扫描标志位,避免多次重复执行扫描程序
//读入按键状态前先向相应端口写1(由基本51单片机硬件结构决定)
READ_ROW_ENLABLE
if((KEYBOARD&0x0f)!
=0x0f)//判断是否有键按下
{
DelayCount++;
if(DelayCount<=DELAY_COUNT) //有键按下则判断延时去抖的时间是否达到
{
returnNOKEY;
}
else //消除了抖动
{
if((KEYBOARD&0x0f)!
=0x0f) //再次判断是否按键真的按下
{
DelayCount=0; //确定按下后,延时去抖计时器清0
KeyValueRow=KEYBOARD&0x0f;//取得行码
//准备读列,先向相应端口写1(由基本51单片机硬件结构决定)
READ_COL_ENLABLE
if((KEYBOARD&0xf0)!
=0xf0)//反转,读列码
{
KeyValueCol=KEYBOARD&0xf0; //取得列码
//合并取得的行码和列码,即是相应按键的键值
switch(KeyValueCol|KeyValueRow)
{
case0x77:
KeyValue=0;break;
case0xb7:
KeyValue=1;break;
case0xd7:
KeyValue=2;break;
case0xe7:
KeyValue=3;break;
case0x7b:
KeyValue=4;break;
case0xbb:
KeyValue=5;break;
case0xdb:
KeyValue=6;break;
case0xeb:
KeyValue=7;break;
case0x7d:
KeyValue=8;break;
case0xbd:
KeyValue=9;break;
case0xdd:
KeyValue=10;break;
case0xed:
KeyValue=11;break;
case0x7e:
KeyValue=12;break;
case0xbe:
KeyValue=13;break;
case0xde:
KeyValue=14;break;
case0xee:
KeyValue=15;break;
default:
returnNOKEY;
}
returnKeyValue;
}
else
{
DelayCount=0;
returnNOKEY;
}
}
else
{
DelayCount=0;
returnNOKEY;
}
}
}
else
{
DelayCount=0;
returnNOKEY;
}
}
}
voidv_T0_I
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 晨辉教 轻松 51 按键