本文實現的代碼是基於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個按鍵了。
通俗點說,就是如果我給這一列低電平,造成了行掃描有變化,那就直接知道這一列有按鍵按下,接着查看行變化的電平變化,推算出哪一行變化了,就可以知道這一列的第幾個按鍵被按下了。