第13章 GPIO輸入—按鍵檢測
本章參考資料:《STM32F76xxx參考手冊》、庫幫助文檔《STM32F779xx_User_Manual.chm》。
按鍵檢測使用到GPIO外設的基本輸入功能,本章中不再贅述GPIO外設的概念,如您忘記了,可重讀前面“GPIO框圖剖析”小節,STM32 HAL庫中GPIO初始化結構體GPIO_TypeDef的定義與“定義引腳模式的枚舉類型”小節中講解的相同。
13.1 硬件設計
按鍵機械觸點斷開、閉合時,由於觸點的彈性作用,按鍵開關不會馬上穩定接通或一下子斷開,使用按鍵時會產生圖 13-1中的帶波紋信號,需要用軟件消抖處理濾波,不方便輸入檢測。本實驗板連接的按鍵帶硬件消抖功能,見圖 13-2,它利用電容充放電的延時,消除了波紋,從而簡化軟件的處理,軟件只需要直接檢測引腳的電平即可。
圖 13-1 按鍵抖動說明圖
圖 13-2 按鍵原理圖
從按鍵的原理圖可知,這些按鍵在沒有被按下的時候,GPIO引腳的輸入狀態為低電平(按鍵所在的電路不通,引腳接地),當按鍵按下時,GPIO引腳的輸入狀態為高電平(按鍵所在的電路導通,引腳接到電源)。只要我們檢測引腳的輸入電平,即可判斷按鍵是否被按下。
若您使用的實驗板按鍵的連接方式或引腳不一樣,只需根據我們的工程修改引腳即可,程序的控制原理相同。
12.2 軟件設計
同LED的工程,為了使工程更加有條理,我們把按鍵相關的代碼獨立分開存儲,方便以後移植。在“工程模板”之上新建“bsp_key.c”及“bsp_key.h”文件,這些文件也可根據您的喜好命名,這些文件不屬於
12.2.1 編程要點
1. 使能GPIO端口時鐘;
2. 初始化GPIO目標引腳為輸入模式(引腳默認電平受按鍵電路影響,浮空/上拉/下拉均沒有區別);
3. 編寫簡單測試程序,檢測按鍵的狀態,實現按鍵控制LED燈。
12.2.2 代碼分析
1. 按鍵引腳宏定義
同樣,在編寫按鍵驅動時,也要考慮更改硬件環境的情況。我們把按鍵檢測引腳相關的宏定義到 “bsp_key.h”文件中,見代碼清單 12-1。
代碼清單 13-1 按鍵檢測引腳相關的宏
1 //引腳定義 2 /*******************************************************/ 3 #define KEY1_PIN GPIO_Pin_0 4 #define KEY1_GPIO_PORT GPIOA 5 #define KEY1_GPIO_CLK () __GPIOA_CLK_ENABLE() 6 7 #define KEY2_PIN GPIO_Pin_13 8 #define KEY2_GPIO_PORT GPIOC 9 #define KEY2_GPIO_CLK() __GPIOC_CLK_ENABLE() 10 /*******************************************************/
以上代碼根據按鍵的硬件連接,把檢測按鍵輸入的GPIO端口、GPIO引腳號以及GPIO端口時鐘封裝起來了。
2. 按鍵 GPIO初始化函數
利用上面的宏,編寫按鍵的初始化函數,見代碼清單 13-2。
代碼清單 13-2 按鍵GPIO初始化函數
1 /** 2 * @brief 配置按鍵用到的I/O口 3 * @param 無 4 * @retval 無 5 */ 6 void Key_GPIO_Config(void) 7 { 8 GPIO_InitTypeDef GPIO_InitStructure; 9 10 /*開啟按鍵GPIO口的時鐘*/ 11 KEY1_GPIO_CLK_ENABLE(); 12 KEY2_GPIO_CLK_ENABLE(); 13 /*選擇按鍵的引腳*/ 14 GPIO_InitStructure.Pin = KEY1_PIN; 15 16 /*設置引腳為輸入模式*/ 17 GPIO_InitStructure.Mode = GPIO_MODE_INPUT; 18 19 /*設置引腳不上拉也不下拉*/ 20 GPIO_InitStructure.Pull = GPIO_NOPULL; 21 22 /*使用上面的結構體初始化按鍵*/ 23 HAL_GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure); 24 25 26 /*選擇按鍵的引腳*/ 27 GPIO_InitStructure.Pin = KEY2_PIN; 28 29 /*使用上面的結構體初始化按鍵*/ 30 HAL_GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure); 31 32 }
同為GPIO的初始化函數,初始化的流程與“LED GPIO初始化函數”章節中的類似,主要區別是引腳的模式。函數執行流程如下:
(1) 使用GPIO_InitTypeDef定義GPIO初始化結構體變量,以便下面用於存儲GPIO配置。
(2) 調用宏定義函數KEY1_GPIO_CLK_ENABLE(),KEY2_GPIO_CLK_ENABLE()來使能按鍵的GPIO端口時鐘。
(3) 向GPIO初始化結構體賦值,把引腳初始化成浮空輸入模式,其中的Pin使用宏“KEYx_PIN”來賦值,使函數的實現方便移植。由於引腳的默認電平受按鍵電路影響,所以設置成“浮空/上拉/下拉”模式均沒有區別。
(4) 使用以上初始化結構體的配置,調用HAL_GPIO_Init函數向寄存器寫入參數,完成GPIO的初始化,這裏的GPIO端口使用“KEYx_GPIO_PORT”宏來賦值,也是為了程序移植方便。
(5) 使用同樣的初始化結構體,只修改控制的引腳和端口,初始化其它按鍵檢測時使用的GPIO引腳。
3. 檢測按鍵的狀態
初始化按鍵後,就可以通過檢測對應引腳的電平來判斷按鍵狀態了,見代碼清單 13-3。
代碼清單 13-3 檢測按鍵的狀態
1 /** 按鍵按下標置宏 2 * 按鍵按下為高電平,設置 KEY_ON=1, KEY_OFF=0 3 * 若按鍵按下為低電平,把宏設置成KEY_ON=0 ,KEY_OFF=1 即可 4 */ 5 #define KEY_ON 1 6 #define KEY_OFF 0 7 8 /** 9 * @brief 檢測是否有按鍵按下 10 * @param GPIOx:具體的端口, x可以是(A...K) 11 * @param GPIO_PIN:具體的端口位, 可以是GPIO_PIN_x(x可以是0...15) 12 * @retval 按鍵的狀態 13 * @arg KEY_ON:按鍵按下 14 * @arg KEY_OFF:按鍵沒按下 15 */ 16 uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin) 17 { 18 /*檢測是否有按鍵按下 */ 19 if (HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == KEY_ON ) { 20 /*等待按鍵釋放 */ 21 while (HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == KEY_ON); 22 return KEY_ON; 23 } else 24 return KEY_OFF; 25 }
在這裏我們定義了一個Key_Scan函數用於掃描按鍵狀態。GPIO引腳的輸入電平可通過讀取IDR寄存器對應的數據位來感知,而STM32 HAL庫提供了庫函數HAL_GPIO_ReadPin來獲取位狀態,該函數輸入GPIO端口及引腳號,函數返回該引腳的電平狀態,高電平返回1,低電平返回0。Key_Scan函數中以HAL_GPIO_ReadPin的返回值與自定義的宏“KEY_ON”對比,若檢測到按鍵按下,則使用while循環持續檢測按鍵狀態,直到按鍵釋放,按鍵釋放後Key_Scan函數返回一個“KEY_ON”值;若沒有檢測到按鍵按下,則函數直接返回“KEY_OFF”。若按鍵的硬件沒有做消抖處理,需要在這個Key_Scan函數中做軟件濾波,防止波紋抖動引起誤觸發。
4. 主函數
接下來我們使用主函數編寫按鍵檢測流程,見代碼清單 13-4。
代碼清單 13-4 按鍵檢測主函數
1 /** 2 * @brief 主函數 3 * @param 無 4 * @retval 無 5 */ 6 int main(void) 7 { 8 /* 系統時鐘初始化成216 MHz */ 9 SystemClock_Config(); 10 /* LED 端口初始化 */ 11 LED_GPIO_Config(); 12 13 /*初始化按鍵*/ 14 Key_GPIO_Config(); 15 16 17 /* 輪詢按鍵狀態,若按鍵按下則反轉LED */ 18 while (1) { 19 if ( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON ) { 20 /*LED1反轉*/ 21 LED1_TOGGLE; 22 } 23 24 if ( Key_Scan(KEY2_GPIO_PORT,KEY2_PIN) == KEY_ON ) { 25 /*LED2反轉*/ 26 LED2_TOGGLE; 27 } 28 } 29 }
代碼中設置系統時鐘為216MHz,初始化LED燈及按鍵後,在while函數裏不斷調用Key_Scan函數,並判斷其返回值,若返回值表示按鍵按下,則反轉LED燈的狀態。
13.1.1 下載驗證
把編譯好的程序下載到開發板並復位,按下按鍵可以控制LED燈亮、滅狀態。
第13章 GPIO輸入—按鍵檢測