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