51单片机——独立按键实验,小白讲解,相互学习

按键介绍:

        按键是一种电子开关,使用时轻轻按开关按钮就可式开关接通,当松手时,开关断开。开发板上使用的按键及内部简易图如下图:

        按键管脚两端距离长的表示默认是导通状态,距离短的默认是断开状态,如果按键按下,初始导通状态变为断开,初始断开状态变为导通。

        通常的按键所用开关位机械弹性开关,当机械触点断开,闭合时,电压信号如下图所示:

        由于机械点的弹性作用,按键开关在闭合时不会马上稳定的接通,在断开时也不会一下子断开,因而在闭合和断开的瞬间均伴随着一连串的抖动。抖动时间在长短由按键的机械特性决定,一般为5ms和10ms.按键稳定闭合时间的长短则由操作人员的按键动作决定的,一般为零点几秒至数秒。按键抖动会引起按键被误读多次。为了确保CPU对按键的一次闭合仅作为一次处理,必须进行消抖。

        按键的消抖有两种方式,一种是硬件消抖,另一种是软件消抖。为了使电路更加简单,通常采用软件消抖。我们开发板也采用软件消抖,一般来说一个简单的按键消抖就是先读取按键的状态,在得到按键按下之后,延时10ms,再次读取按键的状态,如果按键还是按下状态,那么说明按键已经按下。其中延时10ms就是软件消抖处理,至于硬件消抖,大家可以百度了解下,网上都有非常 详细的介绍。

这里给大家列出单片机常用的软件去抖动方法:

  1. 先设置IO口为高电平(由于开发板IO都有上拉电阻,所以默认IO为高电平)。
  2. 读取IO口电平确认是否有按键按下。
  3. 如有IO电平为低电平后,延时几个毫秒。
  4. 在读取该IO电平,如果仍然为低电平,说明按键按下。
  5. 执行按键控制程序。

        独立按键电路构成是由各个按键的一个管脚连接在一起接地,按键其他引脚 分别接到单片机 IO 口。

        

        我们知道单片机的 IO 口既可作为输出也可作为输入使用,当检测按键时用 的是它的输入功能,独立按键的一端接地, 另一端与单片机的 I/O 口相连,开 始时先给该 IO 口赋一高电平,然后让单片机不断地检测该 I/O 口是否变为低 电平,当按键闭合时,即相当于该 I/O 口通过按键与地相连,变成低电平,程 序一旦检测到 I/O 口变为低电平则说明按键被按下,然后执行相应的指令。

硬件设计 :

开发板上的独立按键模 块电路如下图所示:

        从上图中可以看出,4 个独立按键的控制管脚连接到 51 单片机的 P3.0-P3.3 脚上。其中 K1 连接在 P3.1 上,K2 连接在 P3.0 上,K3 连接在 P3.2 上,K4 连接 在 P3.3 上。4 个按键另一端全部连接在 GND,当按键按下后,对应 IO 口即为低 电平。

软件设计

/********************************************************************
****************** 实验名称:独立按键实验
接线说明:
实验现象:下载程序后,按下“独立按键”模块中 K1 键,控制 D1 指示灯亮灭
注意事项:
*********************************************************************
******************/
#include "reg52.h"
typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;
//定义独立按键控制脚
sbit KEY1=P3^1;
sbit KEY2=P3^0;
sbit KEY3=P3^2;
sbit KEY4=P3^3;
//定义 LED1 控制脚
sbit LED1=P2^0;
/*
使用宏定义独立按键按下返回的键值
为什么要定义这个宏呢?是为了方便与我们在程序当中直接使用这个返回值,这样
后期在调用这个函数扫描的时候,得到它的返回值可以直接用KEY1_PRESS来代表,而
不是直接用1 2 3 4来表示 。1 2 3 4在我们用户在看着这个程序的时候,不知道1代表什么
2代表什么,现在KEY1_PRESS 通过英文字母我们可以知道是K1按键按下
当然觉得能看懂,也可以不用宏定义,直接用1 2 3 4 0 来表示这几个
*/
#define KEY1_PRESS 1
#define KEY2_PRESS 2
#define KEY3_PRESS 3
#define KEY4_PRESS 4
#define KEY_UNPRESS 0
/********************************************************************
***********
* 函 数 名 : delay_10us
* 函数功能 : 延时函数,ten_us=1 时,大约延时 10us
* 输 入 : ten_us
* 输 出 : 无
*********************************************************************
**********/
void delay_10us(u16 ten_us)
{
    while(ten_us--);
}
/********************************************************************
***********
* 函 数 名 : key_scan
* 函数功能 : 检测独立按键是否按下,按下则返回对应键值
* 输 入 : mode=0:单次扫描按键
mode=1:连续扫描按键
* 输 出 : KEY1_PRESS:K1 按下
KEY2_PRESS:K2 按下
KEY3_PRESS:K3 按下
KEY4_PRESS:K4 按下
KEY_UNPRESS:未有按键按下
*********************************************************************
**********/
u8 key_scan(u8 mode)
{
    static u8 key=1;//static 修饰符,key的值不会被改变,会保存上次的值
//if(mode)key=1;同if(mode==1)key=1,mode=1为真,就进入这个if,key=1,就是连续检测
//mode=1时候 mode==1不成立,为假,就不执行这个if语句,是单次检测
    if(mode)key=1;
    if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0))//任意按键按下的时候为低电平
    {
        delay_10us(1000);//延时进行消抖处理
/*按键按下后key=0,不松开就不会在进入这个if,再次进行扫描按键,不会在返回这个简 
return KEY1_PRESS......这几个键值了,如果没有这个key=0,不松开程序就不停的在进入
这个if在进行扫描
*/
        key=0;
               
        if(KEY1==0)
            return KEY1_PRESS;
        else if(KEY2==0)
            return KEY2_PRESS;
        else if(KEY3==0)
            return KEY3_PRESS;
        else if(KEY4==0)
            return KEY4_PRESS;
    }
    else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1) //无按键按下的时候全部都为高电平
    {
/*前面按键下的时候key=0,如果没有这个key=1,下次按键按下的时候key=0就不满足if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0))这个,不能进行扫描,不采集的,所以在按键松开后(就是没有按键按下的时候)会执行else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1) 扫描后把赋值,更改为key=1退出程序,下次按键在按下的时候key=1,继续执行if里面的语句。
*/
    key=1;
    }
//如果按键按下,执行完return KEY1_PRESS 这条语句,返回的函数直接返回出去了,就直接
//退出这个函数了,自然后面的语句就不会在执行,如果没有按键按下,第二个if语句有而不会
//执行下去,直接就执行后面的语句,所以这条语句放在else if 的里面或者外面都可以
    return KEY_UNPRESS;
}
/********************************************************************
***********
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************
**********/
void main()
{
//因为这个ey_scan(u8 mode)是有返回值的,所以要定义一个U8类型的值来保存这个返回的键值
    u8 key=0;
    while(1)
    {
        key=key_scan(0);//传入0的值是按键的单次检测,连续检测为0
        if(key==KEY1_PRESS)//检测按键 K1 是否按下
            LED1=!LED1;//按键按下的时候LED默认为高电平,所以!LED1为低电平,LED点亮,
                       //再次按键的时候LED1为低电平,!LED1为高电平,LED关闭
    }
}

        key_scan 函数带一个形参 mode,该参数用来设定是否连续扫描按键,如果 mode 为 0,只能操作一次按键,只有当按键松开后才能触发下次的扫描,这样 做的好处是可以防止按下一次出现多次触发的情况。如果 mode 为 1,函数是支 持连续扫描的,即使按键未松开,在函数内部有 if(mode==1)这条判断语句,因 此 key 始终是等于 1 的,所以可以连续扫描按键,当按下某个按键,会一直返 回这 个按键的键值,这样做的好处是可以很方便实现连按操作。函数内的delay_10us(1000)即为软件消抖处理,通常延时 10ms 即可。

        key_scan 函数还带有一个返回值,如果未有按键按下,返回值即为 KEY_UNPRESS,否则返回值即为对应按键的键值,如 KEY1_PRESS、KEY2_PRESS、 KEY3_PRESS、 KEY4_PRESS,这都是程序开头定义好的宏,方便大家理解和使用。 函数内定义了一个 static 变量 key,相当于全局变量,所以该函数不是一个可 重入函数。还有一点要注意的就是该函数按键的扫描是有优先级的,因为函数内 用了 if…else if…else 格式,所以最先扫描处理的按键是 KEY1,其次是 KEY2,然后是 KEY3,最后是 KEY4。如果需要将其优先级设置一样,那么可以全用 if 语句。

        main 函数中主要就是调用 key_scan 函数用于检测按键,此时传入的 mode 值为 0,表示单次扫描按键,然后将扫描按键的值保存在变量 key 中,最后通过if 判断语句控制 LED1 状态。

物联沃分享整理
物联沃-IOTWORD物联网 » 51单片机——独立按键实验,小白讲解,相互学习

发表评论