第12章 GPIO輸入—按鍵檢測
全套200集視頻教程和1000頁PDF教程請到秉火論壇下載:www.firebbs.cn
野火視頻教程優酷觀看網址:http://i.youku.com/firege
本章參考資料:《STM32F4xx參考手冊》、庫幫助文檔《stm32f4xx_dsp_stdperiph_lib_um.chm》。
按鍵檢測使用到GPIO外設的基本輸入功能,本章中不再贅述GPIO外設的概念,如您忘記了,可重讀前面"GPIO框圖剖析"小節,STM32標准庫中GPIO初始化結構體GPIO_TypeDef的定義與"定義引腳模式的枚舉類型"小節中講解的相同。
12.1 硬件設計
按鍵機械觸點斷開、閉合時,由於觸點的彈性作用,按鍵開關不會馬上穩定接通或一下子斷開,使用按鍵時會產生圖 121中的帶波紋信號,需要用軟件消抖處理濾波,不方便輸入檢測。本實驗板連接的按鍵帶硬件消抖功能,見圖 122,它利用電容充放電的延時,消除了波紋,從而簡化軟件的處理,軟件只需要直接檢測引腳的電平即可。
圖 121 按鍵抖動說明圖
圖 122 按鍵原理圖
從按鍵的原理圖可知,這些按鍵在沒有被按下的時候,GPIO引腳的輸入狀態為低電平(按鍵所在的電路不通,引腳接地),當按鍵按下時,GPIO引腳的輸入狀態為高電平(按鍵所在的電路導通,引腳接到電源)。只要我們檢測引腳的輸入電平,即可判斷按鍵是否被按下。
若您使用的實驗板按鍵的連接方式或引腳不一樣,只需根據我們的工程修改引腳即可,程序的控制原理相同。
12.2 軟件設計
同LED的工程,為了使工程更加有條理,我們把按鍵相關的代碼獨立分開存儲,方便以后移植。在"工程模板"之上新建"bsp_key.c"及"bsp_key.h"文件,這些文件也可根據您的喜好命名,這些文件不屬於STM32標准庫的內容,是由我們自己根據應用需要編寫的。
12.2.1 編程要點
1. 使能GPIO端口時鍾;
2. 初始化GPIO目標引腳為輸入模式(引腳默認電平受按鍵電路影響,浮空/上拉/下拉均沒有區別);
3. 編寫簡單測試程序,檢測按鍵的狀態,實現按鍵控制LED燈。
12.2.2 代碼分析
1. 按鍵引腳宏定義
同樣,在編寫按鍵驅動時,也要考慮更改硬件環境的情況。我們把按鍵檢測引腳相關的宏定義到"bsp_key.h"文件中,見代碼清單 111。
代碼清單 121 按鍵檢測引腳相關的宏
1 //引腳定義
2 /*******************************************************/
3 #define KEY1_PIN GPIO_Pin_0
4 #define KEY1_GPIO_PORT GPIOA
5 #define KEY1_GPIO_CLK RCC_AHB1Periph_GPIOA
6
7 #define KEY2_PIN GPIO_Pin_13
8 #define KEY2_GPIO_PORT GPIOC
9 #define KEY2_GPIO_CLK RCC_AHB1Periph_GPIOC
10 /*******************************************************/
以上代碼根據按鍵的硬件連接,把檢測按鍵輸入的GPIO端口、GPIO引腳號以及GPIO端口時鍾封裝起來了。
2. 按鍵 GPIO初始化函數
利用上面的宏,編寫按鍵的初始化函數,見代碼清單 122。
代碼清單 122 按鍵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 RCC_AHB1PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE);
12
13 /*選擇按鍵的引腳*/
14 GPIO_InitStructure.GPIO_Pin = KEY1_PIN;
15
16 /*設置引腳為輸入模式*/
17 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
18
19 /*設置引腳不上拉也不下拉*/
20 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
21
22 /*使用上面的結構體初始化按鍵*/
23 GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);
24
25 /*選擇按鍵的引腳*/
26 GPIO_InitStructure.GPIO_Pin = KEY2_PIN;
27
28 /*使用上面的結構體初始化按鍵*/
29 GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);
30 }
同為GPIO的初始化函數,初始化的流程與"LED GPIO初始化函數"章節中的類似,主要區別是引腳的模式。函數執行流程如下:
(1) 使用GPIO_InitTypeDef定義GPIO初始化結構體變量,以便下面用於存儲GPIO配置。
(2) 調用庫函數RCC_AHB1PeriphClockCmd來使能按鍵的GPIO端口時鍾,調用時我們使用"|"操作同時配置兩個按鍵的時鍾。
(3) 向GPIO初始化結構體賦值,把引腳初始化成浮空輸入模式,其中的GPIO_Pin使用宏"KEYx_PIN"來賦值,使函數的實現方便移植。由於引腳的默認電平受按鍵電路影響,所以設置成"浮空/上拉/下拉"模式均沒有區別。
(4) 使用以上初始化結構體的配置,調用GPIO_Init函數向寄存器寫入參數,完成GPIO的初始化,這里的GPIO端口使用"KEYx_GPIO_PORT"宏來賦值,也是為了程序移植方便。
(5) 使用同樣的初始化結構體,只修改控制的引腳和端口,初始化其它按鍵檢測時使用的GPIO引腳。
3. 檢測按鍵的狀態
初始化按鍵后,就可以通過檢測對應引腳的電平來判斷按鍵狀態了,見代碼清單 123。
代碼清單 123 檢測按鍵的狀態
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 (GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON ) {
20 /*等待按鍵釋放 */
21 while (GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);
22 return KEY_ON;
23 } else
24 return KEY_OFF;
25 }
在這里我們定義了一個Key_Scan函數用於掃描按鍵狀態。GPIO引腳的輸入電平可通過讀取IDR寄存器對應的數據位來感知,而STM32標准庫提供了庫函數GPIO_ReadInputDataBit來獲取位狀態,該函數輸入GPIO端口及引腳號,函數返回該引腳的電平狀態,高電平返回1,低電平返回0。Key_Scan函數中以GPIO_ReadInputDataBit的返回值與自定義的宏"KEY_ON"對比,若檢測到按鍵按下,則使用while循環持續檢測按鍵狀態,直到按鍵釋放,按鍵釋放后Key_Scan函數返回一個"KEY_ON"值;若沒有檢測到按鍵按下,則函數直接返回"KEY_OFF"。若按鍵的硬件沒有做消抖處理,需要在這個Key_Scan函數中做軟件濾波,防止波紋抖動引起誤觸發。
4. 主函數
接下來我們使用主函數編寫按鍵檢測流程,見代碼清單 124。
代碼清單 124 按鍵檢測主函數
1 /**
2 * @brief 主函數
3 * @param 無
4 * @retval 無
5 */
6 int main(void)
7 {
8 /* LED 端口初始化 */
9 LED_GPIO_Config();
10
11 /*初始化按鍵*/
12 Key_GPIO_Config();
13
14 /* 輪詢按鍵狀態,若按鍵按下則反轉LED */
15 while (1) {
16 if ( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON ) {
17 /*LED1反轉*/
18 LED1_TOGGLE;
19 }
20
21 if ( Key_Scan(KEY2_GPIO_PORT,KEY2_PIN) == KEY_ON ) {
22 /*LED2反轉*/
23 LED2_TOGGLE;
24 }
25 }
26 }
代碼中初始化LED燈及按鍵后,在while函數里不斷調用Key_Scan函數,並判斷其返回值,若返回值表示按鍵按下,則反轉LED燈的狀態。
12.2.3 下載驗證
把編譯好的程序下載到開發板並復位,按下按鍵可以控制LED燈亮、滅狀態。
12.3 每課一問
1. 工程中的Key_Scan函數使用while循環來阻塞檢測,等待按鍵釋放,若按鍵一直被按下,會導致CPU無法進行其它操作,降低效率。嘗試修改按鍵檢測的方式,避免阻塞等待。