[001] [蓝桥杯物联网] 矩阵按键详解

蓝桥杯
Contents
矩阵键盘(2x3)模块
矩阵按键工作原理
矩阵按键HAL库编程
初始化
列引脚电平切换函数
矩阵按键扫描函数

1 矩阵键盘(2x3)模块

image-20220329233506665

行/列 引脚
COL 1 PB1
COL 2 PB0
COL 3 PA8
ROW 1 PB6
ROW 2 PB7

注意PA8引脚为 OLED 显示屏 I2C SCL信号,在同时使用 OLED 屏幕和 2x3 矩阵键盘模块时,在每次矩阵键盘扫描任务前、后,需要重置 PA8 到相应的工作模式和状态;或放弃使用 PA8 对应列(B3/B6按键),使用 2x2 矩阵键盘功能 。

2 矩阵按键工作原理

image-20220330002654935

引脚 引脚
1 P3.0 1 P3.4
2 P3.1 2 P3.5
3 P3.2 3 P3.6
4 P3.3 4 P3.7

行列扫描

  • P3.0~P3.3行引脚全部拉高,P3.4~P3.7列引脚全部拉低
  • 读取引脚电平,若某行有引脚按下,则该行对应的引脚会被与之相连的列引脚拉低
  • P3.4~P3.7列引脚全部拉高,P3.0~P3.3行引脚全部拉低
  • 读取引脚电平,若某列有引脚按下,则该列对应的引脚会被与之相连的行引脚拉低
uint8_t key_scanf(void)
{
    
    
    uint8_t col = 0, row = 0;
    P3 = 0x0f;
    if(P3 != 0x0f)
    {
    
    
        delay_ms(10);
        if(P30 == 0)  row = 1;
        if(P31 == 0)  row = 2;
        if(P32 == 0)  row = 3;
        if(P33 == 0)  row = 4;
    }
    P3 = 0xf0;
    if(P3 != 0xf0)
    {
    
    
        delay_ms(10)
        if(P34 == 0)  col = 1;
        if(P35 == 0)  col = 2;
        if(P36 == 0)  col = 3;
        if(P37 == 0)  col = 4;
    }
    return row ? (row - 1) * 4 + col : 0;
}

逐行/列扫描

以逐行扫描为例(逐列同理)

  • 将第1行引脚拉低,其余引脚全部拉高
  • 读取除第1行引脚外的其他引脚电平,当第1行有按键按下时,相应的列引脚会被拉低
  • 按此方法继续扫描第2~n行,即可确定所有状态
uint8_t key_scanf(void)
{
    
    
    uint8_t col = 0, row = 0;
    for (int i = 0; i < 4; i++)
    {
    
    
    	P3 = 0xff & ~(1 << i);
    	if ( P3 != (0xff & ~(1 << i)) )
    	{
    
    
    		delay_ms(10);
	    	if(P34 == 0)  col = 1;
	        if(P35 == 0)  col = 2;
	        if(P36 == 0)  col = 3;
	        if(P37 == 0)  col = 4;
	        if(col)	  row = i + 1;
    	}
    }
    return row ? (row - 1) * 4 + col : 0;
}

3 矩阵按键HAL库编程

相对51,STM32引脚配置就灵活多了,以逐行扫描为例:

  • 列引脚配置为上拉输入,行引脚初始全部推挽输出高电平(也可以列引脚下拉输入,行引脚推挽输出低电平)
  • 第一行引脚输出低电平,其余行引脚输出高电平,当第一行有按键按下时,读取列引脚电平,若第x列引脚读取到低电平,说明第一行第x列按键被按下。
  • 继续扫描第2~n行,即可确定所有状态

但因为PA8(col3)引脚与oled冲突,因此采用逐列扫描方法,弃用第三列按键避免使用PA8引脚

  • 行引脚配置为上拉输入,列引脚初始全部推挽输出高电平
  • 第一列引脚输出低电平,其余列引脚输出高电平,之后检测方法与逐行扫描同理,不多赘述。

3.1 初始化

// 列引脚
#define COL1_KEY_PORT      GPIOB
#define COL1_KEY_PIN       GPIO_PIN_1
#define COL2_KEY_PORT      GPIOB
#define COL2_KEY_PIN       GPIO_PIN_0
#define COL3_KEY_PORT      GPIOA            // PA8与oled冲突
#define COL3_KEY_PIN       GPIO_PIN_8
// 行引脚
#define ROW1_KEY_PORT      GPIOB
#define ROW1_KEY_PIN       GPIO_PIN_6
#define ROW2_KEY_PORT      GPIOB
#define ROW2_KEY_PIN       GPIO_PIN_7

void key_gpio_init(void)
{
    
    
    GPIO_InitTypeDef GPIO_InitStruct = {
    
    0};

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();

    // 行引脚上拉输入
    GPIO_InitStruct.Pin = ROW1_KEY_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(ROW1_KEY_PORT, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = ROW2_KEY_PIN;
    HAL_GPIO_Init(ROW2_KEY_PORT, &GPIO_InitStruct);

    // 列引脚推挽输出高电平
    GPIO_InitStruct.Pin = COL1_KEY_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(COL1_KEY_PORT, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = COL2_KEY_PIN;
    HAL_GPIO_Init(COL2_KEY_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(COL1_KEY_PORT, COL1_KEY_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(COL2_KEY_PORT, COL2_KEY_PIN, GPIO_PIN_SET);
}

3.2 列引脚电平切换函数

static void keyboard_col(uint8_t col)
{
    
    
    HAL_GPIO_WritePin(COL1_KEY_PORT, COL1_KEY_PIN, col == 1 ? GPIO_PIN_RESET : GPIO_PIN_SET);
    HAL_GPIO_WritePin(COL2_KEY_PORT, COL2_KEY_PIN, col == 2 ? GPIO_PIN_RESET : GPIO_PIN_SET);
}

3.3 矩阵按键扫描函数

void keyboard_scan(void)
{
    
    
    key.value = No_Press;

    // 独立按键USER
    if (HAL_GPIO_ReadPin(USER_KEY_PORT, USER_KEY_PIN) == GPIO_PIN_RESET)
        key.value = USER;

    // 矩阵按键B1~B6
    keyboard_col(1);
    if (HAL_GPIO_ReadPin(ROW1_KEY_PORT, ROW1_KEY_PIN) == GPIO_PIN_RESET)
        key.value  = B1;
    else if (HAL_GPIO_ReadPin(ROW2_KEY_PORT, ROW2_KEY_PIN) == GPIO_PIN_RESET)
        key.value  = B4;

    keyboard_col(2);
    if (HAL_GPIO_ReadPin(ROW1_KEY_PORT, ROW1_KEY_PIN) == GPIO_PIN_RESET)
        key.value  = B2;
    else if (HAL_GPIO_ReadPin(ROW2_KEY_PORT, ROW2_KEY_PIN) == GPIO_PIN_RESET)
        key.value  = B5;
}

关于Key与OLED冲突解决方法:

  1. 不扫描第三列
  2. 扫描第三列前切换PA8为推挽输出模式,扫描结束后切换回复用开漏输出模式(硬件IIC),如果是软件IIC则不用切换,都是推挽输出(若按键扫描函数放中断,需要用中断锁保护OLED的IIC传输不得被打断)

END

猜你喜欢

转载自blog.csdn.net/kouxi1/article/details/123877712
001