第10章 矩阵按键实验

第十章 矩阵按键实验

1. 矩阵按键介绍

通过上一章我们知道了:独立按键在于单片机连接的时候,每一个按键都需要单片机的一个I/O口,若某单片机系统需较多按键, 如果用独立按键便会占用过多的 I/O 口资源。 单片机系统中 I/O 口资源往往比较宝贵, 当用到多个按键时为了减少 I/O 口引脚, 引入了矩阵按键。

本章以 4*4 矩阵键盘为例讲解其工作原理和检测方法。开发板上将 16 个按键排成 4 行 4 列, 第一行将每个按键的一端连接在一起构成行线, 第一列将每个按键的另一端连接在一起构成列线, 这样便一共有 4 行 4 列共 8 根线, 我们将这 8 根线连接到单片机的 8 个 I/O 口上, 通过程序扫描键盘就可检测 16 个键。

无论是对立按键还是矩阵键盘,检测是否按下都是一样的(检测该端口是否为低电平)

关于矩阵按键更多参考:矩阵键盘_百度百科 (baidu.com)

独立键盘有一端固定为低电平, 此种方式编程比较简单。 而矩阵键盘两端都与单片机 I/O 口相连, 因此在检测时需编程通过单片机 I/O 口送出低电平。 检测方法有多种, 最常用的是行列扫描和线翻转法。

本章我们使用行列扫描法

2. 硬件设计

本实验使用到硬件资源如下:

  • 静态数码管

  • 矩阵按键

静态数码管模块我们已经介绍过了,请看矩阵按键模块:

我们可以看到该模块独立,4*4 矩阵按键引出的 8 根控制管脚并未直接连接到 51 单片机的 IO 上, 而是连接到 JP3 端子上。Hx(14)代表的是行,Lx(14)代表的是列。(4行4列)

参考代码使用 P1 口来检测 4*4 矩阵按键, 使用 P0 口控制静态数码管。 单片机的 P17 口连接矩阵键盘的第 1 行, P13 口连接矩阵键盘第 1列。

3. 软件设计

本章我们要实现的功能是:通过数码管显示矩阵按键S1~S16按下后显示0-F

#include <REGX52.H>

// 共阴极段码数据
unsigned char gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
                0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};    
// 延时函数
void delay_us(unsigned int ten_us)
{
    while(ten_us--);    
}

// 扫描按键函数
int key_scan()
{
    unsigned int key_value; // 定义键值

    // 我们使用的是行列扫描法
    P1 = 0xf7; // 给第一列赋值0,其余全为1

    /* 
    为什么可以实现这个功能?
    这行代码是将 P1 的值设为 0xF7,即二进制形式为 11110111。
    11110111即代表了P1.7~P1.1,意味着,P1.7~P1.4和P1.2~P1.0被赋值为1,而P1.3被赋值为0
    看我们的接线就知道-P1.3确实是接在L1上的-即代表了第一列
    从而我们就完成了给第一列赋0(即输出模式)的操作-其他位全不打开
    */

    if(P1 != 0xf7) // 判断第一列按键是否按下
    {
        delay_us(1000);// 消抖
        switch(P1) // 保存第一列按键按下后的键值    
        {
        /*
        下面对比段码值,再返回就行了,注意要返回第一列的数据嗷
        我们举一个例子 1234
                     5678
                     9ABC
                     DF
        比如第一列的5,通过查询段码值,是0xb7
        那么我们直接写case 0xb7 让循环找这个数据,如果找到了,就返回5,然后数码管就显示5了
        */
            case 0x77: 
                key_value = 1;
                break;
            case 0xb7: 
                key_value = 5;
                break;
            case 0xd7: 
                key_value = 9;
                break;
            case 0xe7: 
                key_value = 13;
                break;
        }
    }while(P1 != 0xf7); // 等待按键松开    

    P1 = 0xfb; // 给第二列赋值0,其余全为1
    if(P1 != 0xfb) // 判断第二列按键是否按下
    {
        delay_us(1000);//消抖
        switch(P1)//保存第二列按键按下后的键值    
        {
            case 0x7b: 
                key_value = 2;
                break;
            case 0xbb: 
                key_value = 6;
                break;
            case 0xdb: 
                key_value = 10;
                break;
            case 0xeb: 
                key_value = 14;
                break;
        }
    }while(P1 != 0xfb);//等待按键松开    

    P1 = 0xfd; // 给第三列赋值0,其余全为1
    if(P1 != 0xfd) // 判断第三列按键是否按下
    {
        delay_us(1000); // 消抖
        switch(P1) // 保存第三列按键按下后的键值    
        {
            case 0x7d: 
                key_value = 3;
                break;
            case 0xbd: 
                key_value = 7;
                break;
            case 0xdd: 
                key_value = 11;
                break;
            case 0xed: 
                key_value = 15;
                break;
        }
    }while(P1 != 0xfd);//等待按键松开    

    P1 = 0xfe;//给第四列赋值0,其余全为1
    if(P1 != 0xfe)//判断第四列按键是否按下
    {
        delay_us(1000);//消抖
        switch(P1)//保存第四列按键按下后的键值    
        {
            case 0x7e: 
                key_value = 4;
                break;
            case 0xbe: 
                key_value = 8;
                break;
            case 0xde: 
                key_value = 12;
                break;
            case 0xee: 
                key_value = 16;
                break;
        }
    }while(P1 != 0xfe);//等待按键松开

    return key_value; // 返回我们保存的值    
}


// 主函数
void main()
{    
    int key; // 定义需要返回的值

    while(1)
    {
        key = key_scan(); // 返回的值
        if(key != 0)
        // 如果key不等于0,即k=1~f
        P0 = ~gsmg_code[key-1]; // 得到的按键值减1换算成数组下标对应0-F段码
        // 为什么需要减1呢?(1~f)    、
        // 因为学过C语言知道,数组的下标是从0开始的,而键值是从1开始的,所以要key要减1才能对得上数组
    }        
}

4. 小结

可以看到本章明显难了很多,代码量加大,其实还好,内容有很多重复的,理解了第一列后面就好做了,再来复盘一下整个程序

  • 头文件、数码管段码、延时函数:这些不必解释看过很多了

  • 扫描函数:重点,先理解第一列是怎么实现的,首先我们给P1赋值(即单片机与矩阵键盘连接)为0xf7(十六进制转化为二进制1111 0111)这时候,P1.3就是0了(即与L1连接的)低电平输出,其他关闭

  • 那么怎么判断L1是否按下就是判断P1等不等于1111 0111 ,不等于就代表按下,那么我们就开始记录按下的什么:用switch循环,通过对比段码值,然后返回相应的值(1 5 9 13)

  • 后面L2、L3、L4仿照L1来就行了

  • 既然我们有了返回的值,就用主函数来表示:先判断key是不是等于0,如果不是P0(数码管与单片机相连)就等于key-1,我们知道数码管是一个数组,但是我们返回的整数,那么返回1【key-1】= 0,数组的第0位就是第一个数字0

我们这里使用的行列扫描法:通过代码我们可以看出来,怎么使用?先给第一列全低电平,也就是全输出,当该列有一行按下,就会根据键值返回我们要显示的数字,以此类推,L2,L3,L4

当然不要忘记了,最后数组内的数字要减1,因为C语言中数组下标要从0开始


2024.6.25 第一次修订

2024.8.19 第二次修订,后期不再维护

posted @ 2024-08-19 10:09  hazy1k  阅读(15)  评论(0编辑  收藏  举报