2019-09-29按键

作者: 嵌入式Linux小白 | 来源:发表于2019-10-08 10:55 被阅读0次

    1.按键相关知识

    1.1、按键工作原理

    (1)内部机械结构
    (2)电路连接与原理图中图标


    按键图标

    (3)按键电路接法、上拉电阻。上拉是为了让引脚默认是高电平,但是上拉的力量扛不住接地,所以按键没法按下时上拉的力量保证了IO引脚输入为1,而按下后绝对为0.
    (4)按下和弹起的区别就是接地不接地的问题,也就是引脚输入为1还是0的问题。
    (5)按键这个设备对我们的意义:按键对于我们CPU来说是一个输入设备,输入的是人的操作。CPU通过监测按键连接的IO引脚的电平输入是1还是0就知道外部有没有人按下这个按键。相当于人通过按键给CPU输入了一个信号,这个信号可以被CPu监测到从而指导CPU去做一定的工作。

    1.2、CPU如何处理按键

    (1)轮询式:所谓轮询式就是CPU不断的隔很小一段时间去查看有没有按键被按下,如果按下就处理按键,如果没按下就过一会再来查看。(按键什么时候被按下CPU是无法预知的)。
    (2)中断式

    1.3、按键电路接法分类

    (1)独立按键
    (2)矩阵按键

    2.独立按键编程

    2.1、原理图和接线分析

    (1)8个独立按键接法一样:都是一端接GND,另一端接插座上
    (2)接线:插座接到P1端口,接完之后P1端口8个IO分别对应8个按键(P1.0对应K1、P1.1对应K2……P1.7对应K8)。
    (3)为了用LED点亮或熄灭来指示按键是否按下,还要给LED接线。P0端口接LED。

    2.2、先监测1个按键(用LED作为指示)

    (1)使用轮询法来处理独立按键K1,单片机在循环中每隔很短的时间就监测K1对应的P1.0引脚的输入电平是1还是0。如果是1则表示按键没有按下,熄灭LED作为指示。延时等待下一次检验;如果是0表示按键已经按下了,点亮一颗LED作为指示。

    #include <reg51.h>
    
    sbit key1 = P1^0;
    sbit led1 = P0^0;
    
    void main(void)
    {
        while (1)
        {
            // C语言中把一个IO引脚定义成一个变量key1
            // 然后给key1变量赋值就相当于是向这个IO引脚输出
            // 直接使用(读)这个key1变量,就相当于从这个IO引脚输入
            if (key1 == 1)
            {
                // 没有按键的时候
                // led1 = 0;   // led1熄灭
            }
    
            else 
            {
                // 有按键的时候
                led1 = 1;       // led1点亮
            }
    
        }
    }
    

    2.3、扩展为监测8个独立按键

    (1)如果要监测的按键数量少,可以用位监测(就像上面一样)。如果多则可以直接用端口字节变量(0xfe)来监测。
    (2)独立按键多个按键之间是彼此独立的,所以允许多个按键同时按下而不会影响。(矩阵按键就只能一次按下一个按键,不能多个同时按下)。

    3.键值监测与显示

    3.1、何为键值?

    (1)一般的产品中按键都有很多,对于整个程序来说一般都是把按键进行编码,给每个按键一个对应的编码值,就叫做按键的键值。
    (2)在正式的比较庞大的程序中,按键的监测部分和处理部分都是隔开的。这两部分隔开有利于各自部分的程序编写,两部分之间用键值来连接。按键监测部分负责监测按键,一旦发生一个按键事件就产生一个键值,然后将键值传递给按键处理部分。

    3.2、加入数码管显示键值

    整个程序包括两部分:一部分做按键监测并且发出键值,另一部分负责将接收到的键值显示在独立数码管上。

    #include <reg51.h>
    
    // 当前要处理的是K1,对应P1.0IO口,操控的LED是LED1,对应P0.0
    
    /*********************变量定义************************************/
    sbit key1 = P1^0;
    sbit key2 = P1^1;
    sbit key8 = P1^7;
    
    
    
    // 独立数码管的段码表
    unsigned char val[16] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};
    
    
    
    /*********************************** 函数声明 ***********************/
    void display(unsigned char num);
    void delay(void);
    
    
    
    void main(void)
    {
        unsigned char keynum = 0;
    
    
    
        while (1)
        {
             // C语言中把一个IO引脚定义成一个变量key1
             // 然后给key1变量赋值就相当于是向这个IO引脚输出
             // 直接使用(读)这个key1变量,就相当于从这个IO引脚输入
    
            unsigned char i = 0;
            for (i=0; i<8; i++)
            {
                if ((P1 & (0x1<<i)) == 0)
                {
                    keynum = i + 1;
                }
            }
    
    /*
             // 11111110
             if (P1 == 0xfe)
             {
                keynum = 1;
             }
    
             if (P1 == 0xfd)
             {
                keynum = 2;
             }
    
             if (P1 == 0xfb)
             {
                keynum = 3;
             }
    
             if (P1 == 0xf7)
             {
                keynum = 4;
             }
    
             if (P1 == 0xef)
             {
                keynum = 5;
             }
    
             if (P1 == 0xdf)
             {
                keynum = 6;
             }
    
             if (P1 == 0xbf)
             {
                keynum = 7;
             }
    
             if (P1 == 0x7f)        // 7e
             {
                keynum = 8;
             }
     */
    
             // 在这里去处理按键
             // 处理方法就是把这个按键发出到独立数码管去显示
             display(keynum);
        }
    }
    
    
    // 该函数将num数字送到独立数码管去显示
    void display(unsigned char num)
    {
        P0 = val[num];
    }
    
    // 延时函数
    void delay(void)
    {
        unsigned char i, j;
    
        for (i=0; i<100; i++)
            for (j=0; j<100; j++);
    }
    

    4.消抖

    4.1、案例:按键按一次数码管显示数字加1

    4.2、什么是抖动?

    (1)按键按下和弹起时的电平变化图
    (2)抖动如何产生
    (3)抖动的危害:在抖动时间范围内引脚的电平变化是不定的,如果程序在这一段范围内去判断引脚的电平从而来判断有无按键,则有很大可能性会误判。

    4.3、如何消抖

    (1)硬件消抖,在硬件设计上想办法降低抖动,这是一种主动消抖。加电容,可以让电平抖动变成光滑曲线。
    (2)软件消抖,既然在硬件上不可能完全消除抖动,软件设计上就要想办法绕开抖动造成的影响,这是一种被动(逃避式)的消抖。使用delay10ms函数,从接收到一个低电平信号开始,延时10ms后再判断是否还是低电平,如果是,则说明是真的有按键按下了。

    #include <reg51.h>
    
    // 当前要处理的是K1,对应P1.0IO口,操控的LED是LED1,对应P0.0
    
    /*********************变量定义************************************/
    sbit key1 = P1^0;
    sbit key2 = P1^1;
    sbit key8 = P1^7;
    
    
    
    // 独立数码管的段码表
    unsigned char val[16] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};
    
    
    
    /*********************************** 函数声明 ***********************/
    void AddDisplay(void);
    void delay(void);
    void delay10ms(void);
    
    /******************************全局变量定义*************************/
    unsigned char dnum = 0;
    
    void main(void)
    {
        unsigned char keynum = 0;
    
    
    
        while (1)
        {
             // C语言中把一个IO引脚定义成一个变量key1
             // 然后给key1变量赋值就相当于是向这个IO引脚输出
             // 直接使用(读)这个key1变量,就相当于从这个IO引脚输入
    
    //      if (key1 == 0)
    //      {
    //          AddDisplay();   
    //      }
            if (key1 == 0)
            {
                // 发现1次低电平,有可能是按键按下,也有可能是抖动,软件消抖
                delay10ms();
                if (key1 == 0)
                {
                     // 10ms后还是低电平,说明真的是按键按下了,不是抖动
                     AddDisplay();
                }
            }
    
    
    
             delay();
             // 在这里去处理按键
             // 处理方法就是把这个按键发出到独立数码管去显示
             
        }
    }
    
    
    // 该函数将num数字送到独立数码管去显示
    void AddDisplay(void)
    {
    
        dnum = dnum + 1;
        if (dnum > 15)
        {
            dnum = 0;
        }
    
        P0 = val[dnum];
    }
    
    // 延时函数
    void delay(void)
    {
        unsigned char i, j;
    
        for (i=0; i<200; i++)
            for (j=0; j<200; j++);
    }
    
    void delay10ms(void)   //误差 0us
    {
        unsigned char a,b,c;
        for(c=5;c>0;c--)
            for(b=4;b>0;b--)
                for(a=248;a>0;a--);
    }
    

    5.完整的按键监测

    5.1、一次完整的按键事件

    (1)按键事件就是按键操作过程的不同状态切换
    (2)一个完整的按键事件包括:按下事件(由高变低)、弹起事件(由低变高)
    (3)一般都认为发生了一次完整的按键事件才算是用户操作了一次按键,程序才会去处理按键,所以在一次完整的按键事件中程序只会去处理一次按键。

    5.2、改良版按键增加数码管显示

    #include <reg51.h>
    
    // 当前要处理的是K1,对应P1.0IO口,操控的LED是LED1,对应P0.0
    
    /*********************变量定义************************************/
    sbit key1 = P1^0;
    sbit key2 = P1^1;
    sbit key8 = P1^7;
    
    
    
    // 独立数码管的段码表
    unsigned char val[16] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};
    
    
    
    /*********************************** 函数声明 ***********************/
    void AddDisplay(void);
    void delay(void);
    void delay10ms(void);
    
    /******************************全局变量定义*************************/
    unsigned char dnum = 0;
    
    void main(void)
    {
        unsigned char flag = 0;     // 默认状态等于0
    
        while (1)
        {
            if (key1 == 0)
            {
                // 发现1次低电平,有可能是按键按下,也有可能是抖动,软件消抖
                delay10ms();
                if (key1 == 0)
                {
                     // 10ms后还是低电平,说明真的是按键按下了,不是抖动
                     // 这里说明发现了一个按下事件
                    //flag = 1;
                    if (flag == 0)
                    {
                        AddDisplay();
                        flag = 1;
                    }
                }
            }
            else
            {
                // 电平 == 1
                delay10ms();
                if (key1 == 1)
                {
                    // 说明弹起了
                    if (flag == 1)
                    {
                        //AddDisplay();
                        flag = 0;
                    }
                }
            }
    
            delay(); 
        }
    }
    
    
    // 该函数将num数字送到独立数码管去显示
    void AddDisplay(void)
    {
    
        dnum = dnum + 1;
        if (dnum > 15)
        {
            dnum = 0;
        }
    
        P0 = val[dnum];
    }
    
    // 延时函数
    void delay(void)
    {
        unsigned char i, j;
    
        for (i=0; i<100; i++)
            for (j=0; j<200; j++);
    }
    
    void delay10ms(void)   //误差 0us
    {
        unsigned char a,b,c;
        for(c=5;c>0;c--)
            for(b=4;b>0;b--)
                for(a=248;a>0;a--);
    }
    

    6.中断的引入

    6.1、任务:独立数码管循环显示0-F,同时按键控制LED亮灭

    (1)分析能否实现,实践证明可以实现功能,但是按键监测控制LED这边非常不灵敏。
    (2)逐步认识到单片机只有一个“主线任务”的特点。
    (3)多任务如何及时响应?

    6.2、中断的思路

    (1)“主线任务”为常规任务,默认运行
    (2)中断发生后CPU暂停主线任务转去处理中断任务,完成后再回来接着执行主线任务。

    6.3、中断的意义

    (1)中断处理能力让CPU可以全力处理主线任务而不用担心会错过中断任务(举例:看电影和收快递)
    (2)中断式比轮询式更适合处理异步事件,效率更高。
    (3)中断处理的事件的特点是:处理时间短、响应要求急、不可预见。

    7.使用单片机外部中断来处理按键

    7.1、外部中断INT0和INT1

    (1)何为外部中断。中断源来自单片机外部就叫外部中断,51单片机支持4个外部中断。分别对应4个引脚。每一个外部中断都对应一个特定的单片机IO引脚(比如INT0对应P3.2,这个是单片机在设计时候定好的,是无法改变的)。我们软件只需要对P3.2做一些相关配置,P3.2就可以响应外部的中断事件。当硬件产生了一个外部中断时CPU就会收到一个中断信号,从而转去执行外部中断对应的处理程序(这个处理程序也是我们软件需要去编写提供的)。
    (2)外部中断对应哪个引脚?
    数据手册上有。

    7.2、参考数据手册中示例代码写程序

    (1)编程中使用INT0处理按键
    (2)程序解释
    IT0这一位用来设置中断的触发模式:下降沿触发(Falling)或者低电平触发(Low level)
    EX0这一位是INT0的开关。如果EX0等于0则外部中断在单片机内部被关闭,此时CPU无法收到INT0的中断信息所以不会处理INT0;如果需要使用INT0就一定要设置爱为1.
    EA是全局的中断开关。EA如果关掉则整个CPU不能响应中断,所有中断都被关了。光EA打开也不一定能响应中断,还得具体的中断开关打开才行。

    7.3、总结

    (1)中断能力是CPU本身设计时支持的,并不是编程制造出来的。
    (2)程序员只要负责2件事即可:主程序中初始化中断、定义中断处理程序。
    (3)当中断条件发生时,硬件会自动检测到并且通知CPU,CPU会自动去执行中断处理程序,这一切都是CPU设计时定下的,不需要编程干预。

    8.矩阵键盘

    8.1、矩阵键盘的原理图分析

    矩阵键盘

    (1)横向和纵向分割
    (2)按键两端分别接不通的IO引脚
    (3)按键的物理作用不变:按下接通电路,弹起断开电路

    8.2、矩阵键盘的工作过程

    (1)先送(IO引脚输出)0x0f
    (2)若有按键收到的不是0x0f,从收到的数据(IO引脚输入)判断哪一行按下了。
    (3)再送(IO引脚输出)0xf0
    (4)从收到的数据(IO引脚输入)判断哪一列按下了
    (5)综合两次得到的行和列位置,计算出键值。
    注意:CPU的IO发送,接收也是CPU的IO。下面的8号引脚改为1号引脚,因为教学视频和购买的开发板不一样。


    矩阵键盘工作原理

    8.3、矩阵键盘的特点

    (1)优点:省单片机IO
    (2)缺点:不能同时按下多个按键

    9.矩阵键盘编程实战

    (1)实验研究按键按下的规律(LED显示辅助)
    (2)编写键值检验函数
    (3)独立数码管显示键值

    #include <reg51.h>
    
    // P0端口接LED
    // P0端口接数码管
    // P3端口接矩阵键盘
    
    #define LED P0
    #define KEY P3
    
    #define DIG P0
    
    unsigned char GetKey(void);
    void delay10ms(void);
    
    
    // 独立数码管的段码表
    unsigned char val[16] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};
    
    
    void main(void)
    {
        unsigned char key = 0;
    
        while (1)
        {
            key = GetKey();
    
        
            if (key != 0)
            {
                DIG = val[key]; 
            }
            
        }
    
    
    }
    
    unsigned char GetKey(void)
    {
        unsigned char hang = 0, lie = 0;
        unsigned char keyvalue = 0;
    
         // 第1回合第1步
         KEY = 0x0f;                // 从IO口输出,写IO口
         if (KEY != 0x0f)           // 从IO口输入,读IO口
         {
             // 读出的不是0x0f说明有按键被按下
             // 第1回合第2步:读出端口从读出值来判断是哪一行
             
             delay10ms();
             // 第一回合中算出行号
             switch (KEY)
             {
                case 0x0e:  hang = 1;   break;
                case 0x0d:  hang = 2;   break;
                case 0x0b:  hang = 3;   break;
                case 0x07:  hang = 4;   break;
                default:                break;
             }
    
             // 第2回合第1步
             KEY = 0xf0;
             if (KEY != 0xf0)
             {
                  switch (KEY)
                 {
                    case 0xe0:  lie = 1;    break;
                    case 0xd0:  lie = 2;    break;
                    case 0xb0:  lie = 3;    break;
                    case 0x70:  lie = 4;    break;
                    default:                break;
                 }
    
                // 经过2个回合后hang和lie都知道了,然后根据hang和lie去计算键值即可
                keyvalue = (hang - 1) * 4 + lie;
    
                return keyvalue;
             }
         }
    
         return 0;
    }
    
    void delay10ms(void)   //误差 0us
    {
        unsigned char a,b,c;
        for(c=5;c>0;c--)
            for(b=4;b>0;b--)
                for(a=248;a>0;a--);
    }
    

    相关文章

      网友评论

        本文标题:2019-09-29按键

        本文链接:https://www.haomeiwen.com/subject/hvvfpctx.html