使用正点原子STM32F103精英版HAL库实现4×4矩阵按键检测方法详解

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

因为所需要的功能开发板自带按键不够用,所以购买了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:

完成之后,点右上角的

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

#define KEY_CLO0_OUT_LOW  HAL_GPIO_WritePin(GPIOF,GPIO_PIN_0,GPIO_PIN_RESET)
#define KEY_CLO1_OUT_LOW  HAL_GPIO_WritePin(GPIOF,GPIO_PIN_1,GPIO_PIN_RESET)
#define KEY_CLO2_OUT_LOW  HAL_GPIO_WritePin(GPIOF,GPIO_PIN_2,GPIO_PIN_RESET)
#define KEY_CLO3_OUT_LOW  HAL_GPIO_WritePin(GPIOF,GPIO_PIN_3,GPIO_PIN_RESET)

#define KEY_CLO0_OUT_HIGH  HAL_GPIO_WritePin(GPIOF,GPIO_PIN_0,GPIO_PIN_SET)
#define KEY_CLO1_OUT_HIGH  HAL_GPIO_WritePin(GPIOF,GPIO_PIN_1,GPIO_PIN_SET)
#define KEY_CLO2_OUT_HIGH  HAL_GPIO_WritePin(GPIOF,GPIO_PIN_2,GPIO_PIN_SET)
#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、行扫描函数

/***
 *函数名:KEY_ROW_SCAN
 *功  能:按键行扫描
 *返回值:1~4,对应1~4行按键位置
 */
//如果为1,代表没有按键被按下,如果为0,代表有按键被按下
char KEY_ROW_SCAN(void)
{
    //读出行扫描状态
    Key_row[0] = HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_4)<<3;
    Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_5)<<2);
    Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_6)<<1);
    Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_7));
    
	if(Key_row[0] != 0x0f)         //不是1111,代表肯定有一个0行
    {
      HAL_Delay(10);                   //消抖
      if(Key_row[0] != 0x0f)
		  //0111 1011 1101 1110
        {   
                //printf("Key_Row_DATA = 0x%x\r\n",Key_row[0]);
                switch(Key_row[0])
                {
                    case 0x07:         //0111 判断为该列第1行的按键按下
                        return 1;
                    case 0x0b:         //1011 判断为该列第2行的按键按下
                        return 2;
                    case 0x0d:         //1101 判断为该列第3行的按键按下
                        return 3;
                    case 0x0e:         //1110 判断为该列第4行的按键按下
                        return 4;
                    default :
                        return 0;
                }
        }
        else return 0;
    }
    else return 0;
}

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

2、扫描函数

/***
 *函数名:KEY_SCAN
 *功  能:4*4按键扫描
 *返回值:0~16,对应16个按键
 */
char KEY_SCAN(void)
{    
    char Key_Num=0;            //1-16对应的按键数
    char key_row_num=0;        //行扫描结果记录
    
    KEY_CLO0_OUT_LOW;        
    if( (key_row_num=KEY_ROW_SCAN()) != 0 )
    { 
        while(KEY_ROW_SCAN() != 0);  //消抖
        Key_Num = 0 + key_row_num;
    }
    KEY_CLO0_OUT_HIGH;
    
    KEY_CLO1_OUT_LOW;        
    if( (key_row_num=KEY_ROW_SCAN()) != 0 )
    { 
        while(KEY_ROW_SCAN() != 0);
        Key_Num = 4 + key_row_num;
        //printf("Key_Clo_2\r\n");
    }
    KEY_CLO1_OUT_HIGH;
    
    KEY_CLO2_OUT_LOW;    
    if( (key_row_num=KEY_ROW_SCAN()) != 0 )
    { 
        while(KEY_ROW_SCAN() != 0);
    Key_Num = 8 + key_row_num;
        //printf("Key_Clo_3\r\n");
    }
    KEY_CLO2_OUT_HIGH;
    
    KEY_CLO3_OUT_LOW;    
    if( (key_row_num=KEY_ROW_SCAN()) != 0 )
    {
        while(KEY_ROW_SCAN() != 0);
        Key_Num = 12 + key_row_num;
    }
    KEY_CLO3_OUT_HIGH;
    
    return Key_Num;
}

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

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

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

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

char KEY_SCAN(void);
char KEY_ROW_SCAN(void);

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

int main(void)
{
	char key_confirm;

	while(1)
	{
		key_confirm = KEY_SCAN();
		if(key_confirm>0&&key_confirm<17){
			printf("Key_NUM = %d \r\n",key_confirm); //按下1-16个按键的操作
		}	
	}	

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

物联沃分享整理
物联沃-IOTWORD物联网 » 使用正点原子STM32F103精英版HAL库实现4×4矩阵按键检测方法详解

1 评论

  1. 感谢UP主 学会了!

发表评论