1. 程式人生 > >第13章 GPIO輸入—按鍵檢測

第13章 GPIO輸入—按鍵檢測

text () ext logs 要點 講解 測試 typedef 寫入

本章參考資料:《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”文件,這些文件也可根據您的喜好命名,這些文件不屬於

STM32 HAL庫的內容,是由我們自己根據應用需要編寫的。

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,低電平返回0Key_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輸入—按鍵檢測