• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

正点原子STM32F103精英版+HAL库实现4×4矩阵按键检测

武飞扬头像
Salon.
帮助1

首先声明,本人小白一枚,所做的工作都是借鉴网上的大佬 自己摸索,但是都是亲测实际有效的。

因为所需要的功能开发板自带按键不够用,所以购买了4×4矩阵按键,当时购买的时候以为一个按键对应一个IO口,后来发现不是这样的,会浪费太多的IO口,4×4矩阵键盘用8个IO口控制16个按键。为了能够用起这块按键,自己到网上学习了很多教程,有很多大佬提供了自己的程序,我自己看着比较简便舒服易懂的是神仙边边发布的按键程序,学习了很多。

 为了帮更多的小白朋友,把做的流程详细说一说。

首先说一下原理,一般情况下是用逐行逐列扫描法(反线法我没看不会)。

逐行逐列扫描法原理

学新通

 如图所示,F3.0~F3.3连接4行,F3.4~F3.7连接四列,每行每列都有一个按键连接,当某个按键被按下,它所对应的行和列就会被接通。比如说,我们将四行设置成上拉输入状态,四列设置成推挽输出,这个时候读F3.0~F3.3的引脚状态应该全是高电平,然后我们按下了F按键,此时我们把每一列对应的引脚挨个输出低电平,再读F3.0~F3.3的引脚状态,就会发现F3.0的引脚变为低电平了(因为F按键是连接F3.0引脚),就可以定位到是哪一行的按键被按下。之后再根据是在哪一列扫描时发生变化的,就可以定位出按键所在列。

端口的选择和配置

了解原理之后,我们要开始做了。需要选择8个IO口,如果用的是现成的开发板,比如原子或者野火,一定要记住,IO口的选择非常重要!一定尽量选择没有链接外设的空闲端口!我一开始就掉坑里去了,根据别人的代码选择了端口,结果这个端口有外部下拉,我设置成上拉输入,怎么都不对,也请教了很多同学,其实他们也不会。后来我自己写了个判断程序,发现这个引脚的状态位一直是低电平,因此在选择的时候需要参考原理图和引脚分配。填上这个坑之后,我选择了空闲引脚PF0~PF7。对了,还要看看自己的键盘行和列是怎么焊接的。在CubeMX里面是这样配置的:

学新通

 PF0~PF3是4列,推挽输出,PF4~PF7是四行,上拉输入。CubeMX会自动进行配置。

在CubeMX里面还需要做其他配置,比如SYS配置,如果是小白,刚好用的原子32精英板,就可以按照我的做:

学新通

 RCC配置:

学新通

 时钟树:学新通

 Progject Manager:学新通

完成之后,点右上角的学新通

配置完之后,在生成的程序里我们可以预定义列的操作,方便编写程序,更加直观。

  1.  
    #define KEY_CLO0_OUT_LOW HAL_GPIO_WritePin(GPIOF,GPIO_PIN_0,GPIO_PIN_RESET)
  2.  
    #define KEY_CLO1_OUT_LOW HAL_GPIO_WritePin(GPIOF,GPIO_PIN_1,GPIO_PIN_RESET)
  3.  
    #define KEY_CLO2_OUT_LOW HAL_GPIO_WritePin(GPIOF,GPIO_PIN_2,GPIO_PIN_RESET)
  4.  
    #define KEY_CLO3_OUT_LOW HAL_GPIO_WritePin(GPIOF,GPIO_PIN_3,GPIO_PIN_RESET)
  5.  
     
  6.  
    #define KEY_CLO0_OUT_HIGH HAL_GPIO_WritePin(GPIOF,GPIO_PIN_0,GPIO_PIN_SET)
  7.  
    #define KEY_CLO1_OUT_HIGH HAL_GPIO_WritePin(GPIOF,GPIO_PIN_1,GPIO_PIN_SET)
  8.  
    #define KEY_CLO2_OUT_HIGH HAL_GPIO_WritePin(GPIOF,GPIO_PIN_2,GPIO_PIN_SET)
  9.  
    #define KEY_CLO3_OUT_HIGH HAL_GPIO_WritePin(GPIOF,GPIO_PIN_3,GPIO_PIN_SET)

这块内容或者其他自己需要的定义,我们可以放在“gpio.h”里面的/* USER CODE BEGIN Private defines */和/* USER CODE END Private defines */之间,其他程序也可以放在类似的地方,这样重新配置端口之后不会删除你编写的程序(即使它是依托答辩)。

函数的编写

1、行扫描函数

  1.  
    /***
  2.  
    *函数名:KEY_ROW_SCAN
  3.  
    *功 能:按键行扫描
  4.  
    *返回值:1~4,对应1~4行按键位置
  5.  
    */
  6.  
    //如果为1,代表没有按键被按下,如果为0,代表有按键被按下
  7.  
    char KEY_ROW_SCAN(void)
  8.  
    {
  9.  
    //读出行扫描状态
  10.  
    Key_row[0] = HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_4)<<3;
  11.  
    Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_5)<<2);
  12.  
    Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_6)<<1);
  13.  
    Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_7));
  14.  
     
  15.  
    if(Key_row[0] != 0x0f) //不是1111,代表肯定有一个0
  16.  
    {
  17.  
    HAL_Delay(10); //消抖
  18.  
    if(Key_row[0] != 0x0f)
  19.  
    //0111 1011 1101 1110
  20.  
    {
  21.  
    //printf("Key_Row_DATA = 0x%x\r\n",Key_row[0]);
  22.  
    switch(Key_row[0])
  23.  
    {
  24.  
    case 0x07: //0111 判断为该列第1行的按键按下
  25.  
    return 1;
  26.  
    case 0x0b: //1011 判断为该列第2行的按键按下
  27.  
    return 2;
  28.  
    case 0x0d: //1101 判断为该列第3行的按键按下
  29.  
    return 3;
  30.  
    case 0x0e: //1110 判断为该列第4行的按键按下
  31.  
    return 4;
  32.  
    default :
  33.  
    return 0;
  34.  
    }
  35.  
    }
  36.  
    else return 0;
  37.  
    }
  38.  
    else return 0;
  39.  
    }
学新通

这个函数是copy的别人的代码,把几个函数换成了HAL库的,直接复制就能用。我把它放在了“gpio.c”的用户自定义区间。

2、扫描函数

  1.  
    /***
  2.  
    *函数名:KEY_SCAN
  3.  
    *功 能:4*4按键扫描
  4.  
    *返回值:0~16,对应16个按键
  5.  
    */
  6.  
    char KEY_SCAN(void)
  7.  
    {
  8.  
    char Key_Num=0; //1-16对应的按键数
  9.  
    char key_row_num=0; //行扫描结果记录
  10.  
     
  11.  
    KEY_CLO0_OUT_LOW;
  12.  
    if( (key_row_num=KEY_ROW_SCAN()) != 0 )
  13.  
    {
  14.  
    while(KEY_ROW_SCAN() != 0); //消抖
  15.  
    Key_Num = 0 key_row_num;
  16.  
    }
  17.  
    KEY_CLO0_OUT_HIGH;
  18.  
     
  19.  
    KEY_CLO1_OUT_LOW;
  20.  
    if( (key_row_num=KEY_ROW_SCAN()) != 0 )
  21.  
    {
  22.  
    while(KEY_ROW_SCAN() != 0);
  23.  
    Key_Num = 4 key_row_num;
  24.  
    //printf("Key_Clo_2\r\n");
  25.  
    }
  26.  
    KEY_CLO1_OUT_HIGH;
  27.  
     
  28.  
    KEY_CLO2_OUT_LOW;
  29.  
    if( (key_row_num=KEY_ROW_SCAN()) != 0 )
  30.  
    {
  31.  
    while(KEY_ROW_SCAN() != 0);
  32.  
    Key_Num = 8 key_row_num;
  33.  
    //printf("Key_Clo_3\r\n");
  34.  
    }
  35.  
    KEY_CLO2_OUT_HIGH;
  36.  
     
  37.  
    KEY_CLO3_OUT_LOW;
  38.  
    if( (key_row_num=KEY_ROW_SCAN()) != 0 )
  39.  
    {
  40.  
    while(KEY_ROW_SCAN() != 0);
  41.  
    Key_Num = 12 key_row_num;
  42.  
    }
  43.  
    KEY_CLO3_OUT_HIGH;
  44.  
     
  45.  
    return Key_Num;
  46.  
    }
学新通

这个直接copy的,改都没改嘿嘿。也是放在了同样的地方。

还要在“gpio.c”的用户代码区间定义一个数组,别忘了。

uint8_t Key_row[1]={0xff};   //定义一个数组,存放行扫描状态

这两个扫描函数还要在“gpio.h”用户代码区间做一个声明,这样子:

  1.  
    char KEY_SCAN(void);
  2.  
    char KEY_ROW_SCAN(void);

这样按键函数就完事了,我们可以放在主程序里用。

  1.  
    int main(void)
  2.  
    {
  3.  
    char key_confirm;
  4.  
     
  5.  
    while(1)
  6.  
    {
  7.  
    key_confirm = KEY_SCAN();
  8.  
    if(key_confirm>0&&key_confirm<17){
  9.  
    printf("Key_NUM = %d \r\n",key_confirm); //按下1-16个按键的操作
  10.  
    }
  11.  
    }

当然了,这也是copy的。你可以用它做你想执行的指令了,我是用来控制电机和电磁阀的,连串口都没整,但是亲测还行。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgabege
系列文章
更多 icon
同类精品
更多 icon
继续加载