从零开始的单片机学习(九)

九、按键
    1、按键相关知识
        a、按键工作原理
            (1)内部机械结构:按键外部表现为一个长方体,上部有一个圆柱体的按钮,下部有四个引脚。在内部四个引脚中有
         两个为一组是相互连接的,可等效为1、2两组电路。在内部,2组电路在下,1组电路在上,同时1组电路也是一个压片弹
         簧,按键按下的时候1、2组电路接通,松开时,1、2组电路断开。
            (2)电路连接与原理图中图标:在电路连接和原理图中,按键是由两个引脚上面有一个悬空的“帽子”,其变现形式
         和我们对按键内部结构的认识基本相同。
            (3)按键电路接法、上拉电阻:按键一端连在单片机引脚上,一端接地,同时单片机引脚还外接了一个电阻,电阻一
         端连单片机引脚,一端接电源VCC。因此该电阻十分重要,这个电阻使得当我们按键不按下的时候该引脚是保持高电平的。
         这个电阻被称为上拉电阻。注意:上拉是为了让引脚保持高电平,但是上拉的力量扛不住接地的力量的,只要一接地电平
         就会变成低电平。所以按键没有按下的时候上拉的力量保证了IO引脚输入为1,而按下后由于接地IO引脚的输入为0。
            (4)按下和弹起的区别:区别就是IO引脚接不接地的问题。按下按键的时候,单片机引脚接地,从高电平变为低电平。
         并且造按键按下期间都是低电平;按键弹起的时候,单片机和GND连接断开,VCC使该引脚从低电平变为高电平。并且按键
         不按下该引脚一直是高电平。也就是引脚输入是1还是0的问题了。
             (5)按键这个设备的意义:按键对于CPU来说是一个输入设备,输入的是人的操作。CPU通过检测按键连接的IO引脚
         的电平输入是1还是0就知道外部有没有人按下这个按键。相当于人通过按按键给CPU输入一个信号,这个信号可以被CPU监
         测到从而指导CPU去做一定的工作。

        b、CPU如何处理按键
            (1)轮询式:所谓轮询式就是CPU不断的间隔很小的时间间隔去查看有没有按键被按下,如果按下就处理按键,如果
         没有按下就过一会儿再来查看。(按键什么时候被按下CPU是无法预知的)
            (2)中断式:何为中断?中断就是CPU在执行程序的时候,去处理突发事件,CPU要停止原程序的处理,处理完突发
         事件继续执行原程序。中断分为硬件中断和软件中断。

        c、按键电路接法分类
            (1)独立按键:独立按键均为一端接地,另一端接到单片机的引脚上。
            (2)矩阵键盘:16个按键以4*4的正方形排列,这些按键每一行的左端引脚接在一起和一个单片机引脚接在一起,每
         一列的按键的右端引脚接在一起和一个单片机的引脚相连。这样16个按键只需要8个引脚就可以检测其全部的输出,如果以
         独立按键的方式连接则需要16个引脚才能全部检测。

    2、独立按键编程
        a、原理图与接线分析
            (1)8个独立按键接法一样,一端接地,一端接到单片机的IO引脚上。
            (2)接线:8PIN杜邦线连接单片机IO端口和8个独立按键的插针排座。例如:P1.0对应K1、P1.1对应K2......
         P1.7对应K8。
             (3)为了用LED来指示按键是否按下,需要把LED的插针排座有杜邦线和单片机IO端口相连接。

        b、先检测一个独立按键(LED指示)
            (1)使用轮询法来处理独立按键K1,单片机在循环中没隔很短的时间就检测K1对应的P1.0引脚的输入电平是1还是0。
         如果是1则表示按键还没有按下,熄灭LED为指示。延时等待下一次检验;如果是0表示按键已经按下了,点亮一颗LED为指
         示。
             (2)当前要处理的是K1,对应P1.0IO端口,操控的LED是LED1,对应P0.0
             (3)代码:
                     #include <REGX51.H>

                     sbit key1 = P1^0;
                     sbit led1 = P0^0;

                    void main(void)
                    {
                        while (1)
                        {
                            //C语言把一个IO引脚定义成一个变量key1
                            //然后key1变量赋值就相当于是向这个IO引脚输出
                            //直接使用(读)这个key1变量,就相当于从这个IO引脚输入
                            if (key != 1)
                            {
                                //按键按下
                                 led1 = 0;        //点亮
                            }else
                            {
                                //按键没有按下
                                led1 = 1;        //熄灭
                            }    
                            //注:在这个版本开发板中LED的阳极一起接了VCC,负极接单片机引脚。因此,led1为1时灭
                            //led1为0时亮。
                        }
                    }

        c、扩展为检测8个独立按键
            (1)如果检测的按键数量少,可以用位来监测。如果端口多则可以直接用端口字节来监测。
            (2)独立按键多个按键之间是相互独立的,所以允许多个按键同时按下而不会影响。(矩阵按键就只能一次按一个按
                键,不能多个同时按下)
            (3)代码:
                    #include <REGX51.H>

                    #define led P0
                    #define key P1
                    void main(void)
                    {
                        while (1)
                        {
                            if (key != 0xff)
                            {
                                 led = key;        //LED是1灭0亮可以直接赋值,如果是1亮0灭则需要取反再赋值
                            }else
                            {
                                led = 0xff;
                            }    
                        }
                    }

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

        b、加入数码管显示键值
            (1)整个程序分为两部分:第一部分做按键监测并且发出键值;第二部分接收键值并将其显示在数码管上。
            (2)代码:
                (1)
                    #include <REGX51.H>

                    #define key P1
                    #define smg P0
                    //数码管段码表
                    unsigned char duan[16]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
                                             0x80,0x98,0x88,0x83,0xc6,0xa1,0x86,0x8e};
                    //函数声明
                    unsigned char keynumber();
                    void display(unsigned char num);

                    void main(void)
                    {
                        unsigned char num;
                        while(1)
                        {
                            //接收键值并用数码管显示
                            num = keynumber();
                            if(num != 0)
                            {    
                                display(num);
                            }
                            else
                            {
                                display(0xff);
                            }
                        }
                    }
                    //监测按键并发出键值
                    unsigned char keynumber()
                    {
                        unsigned char keynumber;
                        if(key != 0xff)
                        {
                            switch ((~key))
                            {
                                case 0x01:
                                    keynumber = 1;
                                    break;
                                case 0x02:
                                    keynumber = 2;
                                    break;
                                case 0x04:
                                    keynumber = 3;
                                    break;
                                case 0x08:
                                    keynumber = 4;
                                    break;
                                case 0x10:
                                    keynumber = 5;
                                    break;
                                case 0x20:
                                    keynumber = 6;
                                    break;
                                case 0x40:
                                    keynumber = 7;
                                    break;
                                case 0x80:
                                    keynumber = 8;
                                    break;
                                default:
                                    break;
                            }
                        }
                        else
                        {
                            keynumber = 0;
                        }
                        return keynumber;
                    }
                    //数码管显示函数
                    void display(unsigned char num)
                    {
                        if(num != 0xff)
                        {
                            smg = duan[num];
                        }else
                        {
                            smg = 0xff;
                        }            
                    }
                (2)#include <REGX51.H>

                    #define key P1
                    #define smg P0

                    unsigned char duan[16]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x98,0x88,0x83,0xc6,0xa1,0x86,0x8e};

                    //unsigned char keynumber();
                    void display(unsigned char num);

                    void main(void)
                    {
                    //    unsigned char num;
                        unsigned char keynumber, i;
                        while(1)
                        {
                            if(key != 0xff)
                            {
                                for(i=0; i<8; i++)
                                {
                                    if((P1 & (0x01 << i)) == 0x00)
                                    {
                                        keynumber = i+1;
                                    }
                                }
                            }
                            else
                            {
                                keynumber = 0;
                            }
                            display(keynumber);
                        }
                    }
                    void display(unsigned char num)
                    {
                        if(num != 0)
                        {
                            smg = duan[num];
                        }
                        else
                        {
                            smg = 0xff;
                        }
                    }

                 两种代码效果相同,第二种方法更简洁,并且可以检测到所有的按键信息,第一种按下一个按键不松
             手的时候,再按其他按键不会有反应,第二种可以检测到按下了其他按键并显示

    4、按键的消抖
        a、案例:按键按一次数码管显示数字加1 
           代码:
                   #include <REGX51.H>

                #define smg P0
                sbit key1 = P1^0;

                unsigned char duan[16]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x98,0x88,0x83,0xc6,0xa1,0x86,0x8e};
                unsigned char keynumber = 0;

                void Adddisplay(void);
                void delay(unsigned int t);

                void main(void)
                {
                    while(1)
                    {
                            if (key1 == 0)
                            {
                                Adddisplay();
                            }    
                    }
                }
                void Adddisplay(void)
                {
                    keynumber ++;
                    if (keynumber > 15)
                    {
                        keynumber  = 0;
                    }
                    smg = duan[keynumber];
                    delay(20);    
                }
                void delay(unsigned int t)
                {
                    while(t--)
                    {
                        unsigned char i = 2;
                        unsigned char j = 239;
                        do
                        {
                            while(j--);
                        }while(i--);        
                    }
                }

            实验结论:该代码可以是实现功能,但是经常会导致按一下会加好几次。经过研究和资料研究认为这个问
         题有可能是按键抖动导致的。

        b、什么是抖动?
            (1)按键按下和弹起的电平变化图:按键按下的时候电平有高电平变为电平;弹起的时候有低电平变高
         电平。在理论图中按下的瞬间有高电平变低电平;弹起也是瞬间由低电平变为高电平。但是在实际实验中发
         现电平变化不是瞬间完成的,其需要有一个时间来达到稳定的高电平或者低电平。在这个时间中,电平会起
         伏不定,这也是导致在实验中按一下加好几次的原因所在。
            (2)抖动如何产生:通常按键所用的开关都是机械弹性开关,当机械触点断开、闭合时,由于机械触点
         的弹性作用,一个按键开关在闭合时不会马上就稳定的接通,在断开时也不会一下子彻底断开,而是在闭合
         和断开的瞬间伴随了一连串的抖动。
             (3)抖动的危害:在抖动的时间范围内引脚的电平变化是不定的,如果程序在这一段时间范围内去判断
         引脚的电平从而判断有无按键,则有很大可能会发生误判

        c、如何消抖
            (1)硬件消抖,在硬件设计上想办法降低抖动,这是一种主动消抖。
            (2)软件消抖,既然在硬件上不可能完全消抖,软件设计上就要想办法绕开抖动造成的影响,这是一种
         被动(逃避式)的消抖。

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

        b、改良版按键增加数码管显示
            代码:
                #include <REGX51.H>
                #define smg P0

                sbit key1 = P1^0;
                unsigned char duan[16]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x98,0x88,0x83,0xc6,0xa1,0x86,0x8e};
                unsigned char num = 0;
                unsigned char flag = 0;

                void display(void);
                void delay(unsigned int t);
                void keyshut(void);

                void main(void)
                {
                    while(1)
                    {
                        keyshut();
                    }
                }
                void display(void)
                {
                    num++;
                    if (num > 15)
                    {
                        num = 0;
                    }
                    smg = duan[num];
                }
                void keyshut(void)
                {
                    if (key1 == 0)
                    {
                        //发现了一次低电平,有可能是按键按下,也有可能是抖动,软件消抖
                        delay(10);
                        if (key1 == 0)
                        {
                            //进过10ms的延时还是低电平说明按键真的被按下了,不是抖动
                            //这里是一次按下事件
                            if (flag == 0)
                            {
                                flag = 1;
                                if (flag > 0 && flag == 1)
                                {
                                    flag ++;
                                    display();
                                }
                            }
                        }
                    }
                    else
                    {
                        flag = 0;
                    }
                }
                void delay(unsigned int t)
                {
                    while(t--)
                    {
                        unsigned char i = 2;
                        unsigned char j = 239;
                        do
                        {
                            while(j--);
                        }while(i--);        
                    }
                }

            实验结论:在按键按下去就会显示的数字+1,之后不会连续相加。按键弹起再次按下才会+1,实现了预
         期的目标。

    6、中断的引入
        a、任务:独立数码管循环显示0~F,同时按键控制LED亮灭。
            (1)逐步分析能否实现,实践证明可以实现功能,但是按键监测控制LED非常不灵敏。
            (2)逐步认识到单片机只有一个“主线任务”的特点
            (3)多任务时如何及时响应?使用中断来对于多任务的及时响应
            ‘

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

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

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

        b、参考数据手册中示例写代码
            (1)编程使用INT0和INT1处理按键
                代码:
                    #include <REGX51.H>

                    #define smg P0
                    sbit led1 = P2^0;
                    sbit led2 = P2^1;
                    unsigned char duan[16]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x98,0x88,0x83,0xc6,0xa1,0x86,0x8e};

                    void display(void);
                    void delay(unsigned int t);

                    void main(void)
                    {    
                        //开启外部中断0和1
                        IT0 = 1;
                        IT1 = 1;
                        EX0 = 1;
                        EX1 = 1;
                        EA = 1;
                        while(1)
                        {
                            //任务一:数码管循环显示
                            display();
                        }
                    }
                    void display(void)
                    {
                        unsigned int i;
                        for(i=0; i<16; i++)
                        {
                            smg = duan[i];
                            delay(150);
                        }
                    }
                    void delay(unsigned int t)
                    {
                        while(t--)
                        {
                            unsigned char i = 2;
                            unsigned char j = 239;
                            do
                            {
                                while(j--);
                            }while(i--);        
                        }
                    }
                    void led9() interrupt 0
                    {
                        led1 = ~led1;
                    }
                    void led10() interrupt 2
                    {
                        led2 = ~led2;
                    }
                    
            (2)程序解释
                IT0这一位用来设置中断的触发模式:下降沿触发(Falling)或者低电平触发(lowlevel)
                EX0这一位是INT0的开关。如果EX0=0则外部中断在单片机内部被关闭,此时CPU无法收到INT0的
             中断信息所以不会处理INT0;如果需要使用INT0就一定要设置为1。
                 EA是全局的中断开关。EA如果关掉则整个CPU就不能响应中断,所有中断都关掉了。光EA打开也不
              一定可以响应中断,需要具体的中断开关打开才行。

        c、总结
            (1)

    8、矩阵键盘的原理
        a、矩阵键盘的原理图分析
            (1)横向和纵向分割
            (2)按键两端分别接不同的IO引脚
            (3)按键的物理作用不变:按下接通电路,弹起断开电路

        b、矩阵键盘的工作过程
            (1)先送(IO引脚输出)0x0f
            (2)若有按键按下则接收到就不是0x0f,从接收到的数据(IO引脚输入)判断哪一行按下了
            (3)再送(IO引脚输出)0xf0
            (4)从收到的数据(IO引脚输入)判断哪一行按下了
            (5)综合两次得到的行和列的位置就可以计算出键值

        c、矩阵键盘的特点
            (1)优点:节省单片机IO引脚
            (2)缺点:不能同时按下多个按键

    9、矩阵键盘编程原理
        a、实验研究按键按下的规律(LED显示辅助)
            (1)实验一
                    代码:
                        #include <stdio.h>
                        #define key P1
                        #define led P0
                        void main(void)
                        {
                            //第一回合第一步
                            key = 0x0f;                 //从IO口输出,写IO口
                            if (key != 0x0f)            //从IO口输入,读IO口
                            {
                                //读出不是0x0f说明有按键被按下
                                //第一回合第二步:读出端口从读出的值来判断是哪一行
                                led = key;
                            }
                        }
                    现象:
                        按下S1从0000 1111变为0000 1110可以推出bit0对应第一行
                        按下S2从0000 1111变为0000 1101可以推出bit1对应第二行
                        以此类推bit2~3分别对应第三行和第四行。
            (2)实验二
                        代码:
                            #include <stdio.h>
                            #define key P1
                            #define led P0
                            void main(void)
                            {
                                //第一回合第一步
                                key = 0x0f;                 //从IO口输出,写IO口
                                if (key != 0x0f)            //从IO口输入,读IO口
                                {
                                    //读出不是0x0f说明有按键被按下
                                    //第一回合第二步:读出端口从读出的值来判断是哪一行
                                    //led = key;
                                    //第二回合第一步
                                    key = 0xf0;
                                    if (key != 0xf0)
                                    {
                                        led = key;
                                    }
                                }
                            }
                        现象:
                            按下S1从1111 0000变为0111 0000可以推出bit4对应第一列
                            按下S2从1111 0000变为1011 0000可以推出bit5对应第二列
                            以此类推bit6~7分别对应第三列和第四列。


        b、编写键值检验函数
                        代码:
                            unsigned char keynumber(void)
                            {
                                unsigned char keynumber = 0;
                                P1 = 0xf0;
                                if (P1 != 0xf0)
                                {
                                    delay(10);
                                    if(P1 != 0xf0)
                                    {
                                        switch(P1)
                                        {
                                            case 0xe0:    keynumber = 1;        break;
                                            case 0xd0:    keynumber = 2;        break;
                                            case 0xb0:    keynumber = 3;        break;
                                            case 0x70:    keynumber = 4;        break;
                                            default:                            break;
                                        }
                                        P1 = 0x0f;
                                        switch(P1)
                                        {
                                            case 0x0e:    keynumber += 0;        break;
                                            case 0x0d:    keynumber += 4;        break;
                                            case 0x0b:    keynumber += 8;        break;
                                            case 0x07:    keynumber += 12;    break;
                                            default:                                            break;
                                        }
                                    }
                                }
                                return keynumber;
                            }

        c、独立数码管显示键值
                        代码:
                            #include <REGX51.H>

                            #define smg P0
                            sbit led1 = P2^0;
                            sbit led2 = P2^1;
                            unsigned char duan[16]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x98,0x88,0x83,0xc6,0xa1,0x86,0x8e};

                            void display(unsigned char no);
                            void delay(unsigned int t);
                            unsigned char keynumber(void);

                            void main(void)
                            {    
                                unsigned char num = 0;
                                while(1)
                                {
                                    num = keynumber();
                                    if(num != 0)
                                    {
                                        display(num);
                                    }
                                }
                            }
                            void display(unsigned char no)
                            {
                                    smg = duan[no-1];
                            }
                            unsigned char keynumber(void)
                            {
                                unsigned char keynumber = 0;
                                P1 = 0xf0;
                                if (P1 != 0xf0)
                                {
                                    delay(10);
                                    if(P1 != 0xf0)
                                    {
                                        switch(P1)
                                        {
                                            case 0xe0:    keynumber = 1;        break;
                                            case 0xd0:    keynumber = 2;        break;
                                            case 0xb0:    keynumber = 3;        break;
                                            case 0x70:    keynumber = 4;        break;
                                            default:                            break;
                                        }
                                        P1 = 0x0f;
                                        switch(P1)
                                        {
                                            case 0x0e:    keynumber += 0;        break;
                                            case 0x0d:    keynumber += 4;        break;
                                            case 0x0b:    keynumber += 8;        break;
                                            case 0x07:    keynumber += 12;    break;
                                            default:                                            break;
                                        }
                                    }
                                }
                                return keynumber;
                            }
                            void delay(unsigned int t)
                            {
                                while(t--)
                                {
                                    unsigned char i = 2;
                                    unsigned char j = 239;
                                    do
                                    {
                                        while(j--);
                                    }while(i--);        
                                }
                            }

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值