单片机系统的按键处理
即可。然后在主程序类似下面这样写即可
if( KeyTime )
//定时扫描时间到
{
KeyValue = u8_ReadKey_f() ;
}
具体的工作就交
给您去完成啦。
看看效果:
按键单击
、
连发时候的截图 至此,关于单个按键的学习就告一段落了,您是否已经明白了。如果您还不明白,那么把这 个程序好好的看看,并画下流程图,分析分析。估计您就会恍然大悟。关键是思路要转换过 来。 下面我们来看看多个按键的情况吧 一般情况下,如果多个按键每个都直接接在单片机的 I/O 上的话会占用很多的 I/O 资源。比 较合理的一种做法是,按照行列接成矩阵的形式。按键接在每一个的行列的相交处。这样对 于 m 行 n 列的矩阵,可以接的按键总数是 m*n。这里我们以常见的 4*4 矩阵键盘来讲解矩 阵键盘的编程。
下面以四个独立按键的处理程序为例来讲解(支持单击和连发)
#include"regx52.h"
sbit KeyOne = P1^0 ;
sbit KeyTwo = P1^1 ;
sbit KeyThree = P1^2 ;
sbit KeyFour = P1^3 ;
#define uint16 unsigned int
//临时保存读到的键值 //读键值
if( KeyTemp == 0x0f )
{
KeyCount = 0 ;
KeyOverTime = KEY_OVER_TIME ;
return NOKEY ;
//无键按下返回 NOKEY
}
else
{
if( KeyTemp == LastKey ) //是否第一次按下
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 } } }
#define NOKEY
0xff //定义无键按下时的返回值
#define DELAY_COUNT 2 //消抖时间常数
/*****************************************
* 此模块所需的全局或者外部变量
*
*****************************************/
按键的种类很多。不过原理基本相似。下面我们以一种轻触开关为例讲解按键程序的 写法。
这种轻触开关大家不陌生吧^_^
一般情况下,按键与单片机的连接如下面这幅图所示。
(图中电阻值一般去 4.7k~10k 之间,对于内部端口有上拉电阻的单片机则可省略此电阻) 单片机对于按键的按下与否则是通过检测相应引脚上的电平来实现的。对于上图而言, 当 P17 引脚上面的电平为低时,则表示按键已经按下。反之,则表明按键没有按下。我们 在程序中只要检测到了 P17 引脚上面的电平为低了,就可以判断按键按下。呵呵,简单吧。
#include"regx52.h"
#define uint8 unsigned char
#define uint16 unsigned int
/**************************************** * 与硬件连接相关的定义及宏定义和操作宏 * *****************************************/ #define KEYBOARD P3 #define READ_ROW_ENLABLE KEYBOARD = 0x0f ; 本 51 单片机特性决定的) #define READ_COL_ENLABLE KEYBOARD = 0xf0 ;
每次执行读键盘函数时,只是对一些标志进行判断,然后退出。因此能够充分的利用 CPU
的资源。同时可以处理连发按键。此按键扫描按键函数可以直接放在主函数中。如果感觉按
键太过灵敏或者迟钝则改一下相关消抖动的宏定义即可。此函数也可以通过中断标志位进行
定时的扫描。此时,需要添加一个定时标志位,并将相关消抖动的和连击时间的宏定义改小
上图就是矩阵键盘的一般接法。
这里我们要介绍一种快速的键盘扫描法:线反转法(或者称为行列翻转法)。具体流程如下。 首先,让单片机的行全部输出 0,列全部输出 1,读取列的值(假设行接 P3 口的高四位,列 接低四位)。即 P3= 0x0f ; 此时读列的值,如果有键按下,则相应的列读回来的值应该为低。 譬如此时读回来的值为 0x0e ; 即按键列的位置已经确定。这时反过来,把行作为输入,列 作为输出,即 P0 = 0xf0 ;此时再读行的值,如果按键仍然被按下,则相应的行的值应该为低, 如果此时读回来的值为 0xe0,则确定了行的位置 。说到这里,您应该笑了,知道了一个按 键被按下的行和列的位置,那么就可以肯定确定它的位置了。我们把读回来的行值和列值进
单片机系统的按键小议 -------蒋晨辉
对于一个由单片机为核心构成的系统而言。输入通道是相当重要的。可以看到几乎每 一样基于单片机的产品都有人机交互的部分。如各种仪器设备上的各种按钮和开关,以及我 们手机上的键盘,MP3 上的按键等等。最常见的输入部分,莫非就是按键了。对于大多数 初学者而言,编写一个好的按键程序是一件颇为头疼的事情。于是乎在网上乱搜一气,程序 倒是找到了不少,但是看了半天依然是不明白。或者在某某论坛上面发帖“跪求 XX 按键程 序,大虾帮忙……”如果你偶然间进了这个论坛,又偶然看到了这个帖子,而且恰好你对按 键程序的写法也不是很清楚,那么我希望你能够静静的看完这个帖子。如果你觉得对你很有 帮助,那么我希望你能够在以后的日子中能够坚持到这个论坛来,一起交流学习,分享自己 学习过程中的喜悦或者一起探讨棘手的问题,这是我写这个帖子的最大的初衷了。OK,不 能再说了,再说就变成水帖了。那么我们开始吧。
{
static uint8 LastKey = NOKEY ;
//保存上一次的键值
static uint16 KeyCount = 0 ;
//按键延时计数器
static uint16 KeyOverTime = KEY_OVER_TIME ; //按键抬起时间
uint8 KeyTemp = NOKEY ; KeyTemp = P1 & 0x0f ;
{
if( ++KeyCount == KEY_WOBBLE_TIME ) //不是第一次按下,则判断
//抖动是否结束
{
return KeyTemp ;
//去抖动结束,返回键值
}
else
{
if( KeyCount > KeyOverTime )
{
KeyCount = 0 ;
KeyOverTime = KEY_QUICK_TIME ;
{ Delay(20) ;
//延时 20MS
If( P17 == 0)
{
KeyPress = 1 ; While( !P17) ; //等待释放
}
else KeyPress = 0 ; } }
这样一个程序,相信对很多初学者而言都不陌生。因为好多书上基本都是这样的一个 流程和写法。可是当有一天,我们想做一个数码管加按键调整的时钟,发现当我们按键按下 去的时候,数码管就不亮了。为什么呢。原因就在这个键盘扫描函数。平常没有按键按下还 好。一旦有键按下,它先是浪费了 CPU 的大部分时间(就是那个什么事情都没做的延时 20MS 函数)然后,又霸占 CPU( 就是哪个死死等在那里的 while(P17);语句)直到按键释放。对 于这种情况我们是忍无可忍的,那么就让我们彻底的抛弃它吧。那么到底按键扫把 CPU 延时 的那 20MS 拿出来去做其它事情,那么不就充分利用 CPU 的时间了吗。而一般情况下我们 只要前沿去抖动就可以了。也就是说了,我们只需在按键按下后去抖就可以了,对于按键的 释放抖动可以不必要过于关注。当然这主要和应用的场合有关。一个能有效识别按键按下并 支持连发功能的按键已经能够应用到大多数的场合了。
#define KEY_QUICK_TIME 1000 //等待按键抬起的连击时间(待定)
void v_KeyInit_f( void )
{ KeyOne = 1 ;
//按键初始化(相应端口写 1)
KeyTwo = 1 ;
KeyThree = 1 ;
KeyFour = 1 ;
}
uint8 u8_ReadKey_f(void)
#define uint8 unsigned char
#define NOKEY 0xff
#define KEY_WOBBLE_TIME 500
//去抖动时间(待定)
#define KEY_OVER_TIME 15000 //等待进入连击时间(待定),该常数要比正常
//按键时间要长,防止非目的性进入连击模式
//键盘连接到单片机上的端口位置 //读端口之前先把相应口置位(由基
// 根据实际硬件连接情况修改
/*****************************************
* 模块内相关的宏定义及常数宏*******************************************/
等会,您先别乐呵,话还没说完呢。下面我们来看看,当按键按下时,P17 引脚上面的波形 是怎么变化的。
这张图是一个理想波形图,当按键按下时,P17 口的电平马上被拉低到 0V 了。当然理 想的东西都是不现实的。所以我们还是看看现实的波形图吧。