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

第12章 GPIO輸入—按鍵檢測

2.2.3 硬件設計 按鍵 反轉 oid ref type 下拉 12.1

第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無法進行其它操作,降低效率。嘗試修改按鍵檢測的方式,避免阻塞等待。

第12章 GPIO輸入—按鍵檢測