STM32 實現 4*4 矩陣鍵盤掃描(HAL庫、標准庫 都適用)


  本文實現的代碼是基於STM32HAL庫的基礎上的,不過標准庫也可以用,只是調用的庫函數不同,邏輯跟配置是一樣的,按我這里的邏輯來配置即可。

 

1、鍵盤原理圖:

 

  原理舉例:先把 F0-F7 內部拉高,這樣這個8個引腳都是高電平,然后就進行列掃描。例如:假如按下3按鈕,Y3 列掃描,把F4先拉低,然后讀取F0-F3的狀態,就會讀出為1110,這就可  以知道是F3行拉低了,同時這時候是程序控制F4拉低的,這樣就可以知道是F4列導致它轉態變化了的,這樣就可以定位出是F4列F3行的按鍵按下了;其他的列也是這樣子掃描,就可以實現了。

2、STM32 cubemx 引腳配置圖:

  

  這里用外部晶振內部晶振都可以,時鍾對這個沒什么影響,不用開中斷,所以其他的配置就不細說了,下面再說一下這8個GPIO的配置。

  

  4個引腳配推挽輸出,這4個配輸出的引腳內部上下拉不用配置;另外4個配成輸入,內部上拉

3、生成代碼后,開始編寫邏輯:

  編寫之前我們先做一下頭文件的定義,把一些要用到的宏定義好:

#ifndef __HW_key_H__
#define __HW_key_H__

#include "main.h"
#include "stm32f1xx_hal.h"
#include <string.h>

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

#define KEY_CLO0_OUT_LOW  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_7,GPIO_PIN_RESET) 
#define KEY_CLO1_OUT_LOW  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_8,GPIO_PIN_RESET)
#define KEY_CLO2_OUT_LOW  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_RESET)
#define KEY_CLO3_OUT_LOW  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_10,GPIO_PIN_RESET)

#define KEY_CLO0_OUT_HIGH  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_7,GPIO_PIN_SET) 
#define KEY_CLO1_OUT_HIGH  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_8,GPIO_PIN_SET)
#define KEY_CLO2_OUT_HIGH  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_SET)
#define KEY_CLO3_OUT_HIGH  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_10,GPIO_PIN_SET)

#endif

  然后包含頭文件以及定義一些要用到的變量數組:

#include "HW_key.h"
uint8_t Key_row[1]={0xff};   //保存按鍵行掃描情況的狀態數組

  接着可以寫掃描邏輯了,先編寫橫掃描的代碼:

/***
 *函數名:KEY_ROW_SCAN
 *功  能:按鍵行掃描
 *返回值:1~4,對應1~4行按鍵位置
 */
char KEY_ROW_SCAN(void)
{
    //讀出行掃描狀態
    Key_row[0] = HAL_GPIO_ReadPin(GPIOE,KEY_row0_Pin)<<3;
    Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOE,KEY_row1_Pin)<<2);
    Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOE,KEY_row2_Pin)<<1);
    Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOE,KEY_row3_Pin));
    
    if(Key_row[0] != 0x0f)         //行掃描有變化,判斷該列有按鍵按下
    {
      HAL_Delay(10);                    //消抖
      if(Key_row[0] != 0x0f)
        {   
                //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;
}

  這個函數,可以判斷哪一行有按鍵按下,並返回有按鍵按下的行數。

  接着編寫列掃描的代碼,這里的思想是,先掃描第一列,接着判斷第一列有沒有行被按下,有的話就可以直接定位到這一列的哪一行,其他4列邏輯一樣,這樣就可以定位到哪個按鍵按下了。

/***
 *函數名: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;
        //printf("Key_Clo_1\r\n");
    }
    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 )
    {
//        Key_row[0] = HAL_GPIO_ReadPin(GPIOE,KEY_col0_Pin)<<3;
//        Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOE,KEY_col1_Pin)<<2);
//        Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOE,KEY_col2_Pin)<<1);
//        Key_row[0] = Key_row[0] | (HAL_GPIO_ReadPin(GPIOE,KEY_col3_Pin));
//        printf("Key_Clo4_DATA = 0x%x\r\n",Key_row[0]);
        while(KEY_ROW_SCAN() != 0);
        Key_Num = 12 + key_row_num;
        //printf("Key_Clo_4\r\n");
    }
    KEY_CLO3_OUT_HIGH;
    
    return Key_Num;
}

 這個函數就可以直接返回1-16個按鍵的按鍵數了,按下第一個按鍵就返回1,第2個就返回2,以此類推。下面可以調用這個函數做按鍵按下的操作了:

/***
 *函數名:KEY_ROW_SCAN
 *功  能:執行按下按鍵后的操作
 *返回值:無
 */
void HW_KEY_FUNCTION(void)
{
    char key_confirm;
    key_confirm = KEY_SCAN();
    if( 0 < key_confirm  && key_confirm < 17 )
    {
        printf("Key_NUM = %d \r\n",key_confirm); //按下1-16個按鍵的操作
        printf("= = = = = = = = = = = \r\n");
    }
}

  我這里就是用串口助手打印出來查看哪個按鍵按下的,實測可用。

  

 

4、總結:

(1)先配置8個引腳,4個配置輸入,上拉;4個配置成推挽(PP)輸出,不用上下拉,輸出高電平;

(2)軟件邏輯:

  a. 先說一下行掃描的原理,因為如果有按鍵按下的話,某一個輸入的引腳就會跟對應的輸出引腳連接,因為輸出為高電平,所以對應的輸入引腳會被拉高,讀取引腳的狀態,判斷哪個引腳被拉高就可以知道哪一行有按鍵按下了;總的來說是通過高四位輸出高電平來對矩陣鍵盤進行逐行掃描,當低四位接收到的數據不全為1的時候,說明有按鍵按下,然后通過接收到的數據是哪一位為0來判斷是哪一行按鍵被按下

  b. 列掃描原理:思路是先把第一列輸出低電平,接着讀取高4位的電平轉態,單不全為1時,說明這一列有按鍵按下,同時結合行掃描判斷出來的行數定位到按下的按鍵。程序里是掃描第一列的時候第一列給低電平,接着進行行掃描判斷,因為輸入輸出引腳都是高電平了,只有第一列的引腳是低電平,所以當第一列有按鍵按下的時候,行掃描讀到的4個引腳就不全為1,這時因為第一列的電平是我們自己給的,所以就可以直接判斷這一列有按鍵按下;接着利用行掃描原理定位哪一行有按鍵按下,這樣就可以判斷出第一列的某一行的按鍵被按下了,其他3列同理,然后輪流掃描4列就可以判斷16個按鍵了。

 

  通俗點說,就是如果我給這一列低電平,造成了行掃描有變化,那就直接知道這一列有按鍵按下,接着查看行變化的電平變化,推算出哪一行變化了,就可以知道這一列的第幾個按鍵被按下了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM