做網(wǎng)站排名費用多少百度風(fēng)云榜明星
STM32學(xué)習(xí)7 按鍵掃描
- 一、實驗電路介紹
- 二、按鍵GPIO初始化
- 三、掃描原理
- 1. GPIO引腳配置
- 2. 狀態(tài)輪詢
- 3. 按鍵狀態(tài)檢測
- 4. 循環(huán)掃描的優(yōu)缺點
- 優(yōu)點:
- 缺點:
- 四、一次掃描與持續(xù)掃描
- 五、代碼實現(xiàn)
- 1. 頭文件定義
- 2. 函數(shù)實現(xiàn)
- 3. 主體函數(shù)
一、實驗電路介紹
本實驗使用普中STM32-F1開發(fā)板,芯片型號是STM32F103ZET6。
其按鍵電路如下:
對應(yīng)的芯片引腳:
從電路可以看出,鍵盤的 KEY_UP 鍵如果接通,會連接高電平 。
其它幾個按鍵在按下的時候連接低電平,對應(yīng)的GPIO口:
- KEY_UP:GPIOA GPIO_Pin0 引腳
- KEY_LEFT:GPIOE GPIO_Pin2 引腳
- KEY_RIGHT:GPIOE_GPIO_Pin4 引腳
- KEY_DOWN:GPIOE_GPIO_Pin3 引腳
二、按鍵GPIO初始化
按鍵 KEY_UP 和其它三個按鍵的接法不同,需要不同的配置方式。
其中 KEY_UP 按下后接高電平,在默認情況下需要置低,初始化時設(shè)置為輸入下拉,代碼如下:
// 開啟GPIOA的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 設(shè)置上引腳GPIO_InitStructure.GPIO_Pin = KEY_UP_PIN;// 設(shè)置輸入下拉模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;GPIO_Init(KEY_UP_PORT, &GPIO_InitStructure); // 初始化GPIOA
其它三個按鍵,按下時接低電平,默認置高,初始化設(shè)置為輸入上拉,代碼如下:
// 開 E 時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);// 設(shè)置下、左、右引腳GPIO_InitStructure.GPIO_Pin = KEY_DOWN_PIN | KEY_LEFT_PIN | KEY_RIGHT_PIN;// 設(shè)置輸入上拉模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(KEY_DOWN_PORT, &GPIO_InitStructure); // 初始化GPIOE
三、掃描原理
1. GPIO引腳配置
首先,需要將用于連接按鍵的GPIO引腳配置為輸入模式。
2. 狀態(tài)輪詢
然后輪詢每個按鍵的狀態(tài),以確定按鍵是否被按下或釋放。輪詢掃描可以通過在主循環(huán)中定期檢查每個按鍵的狀態(tài)來實現(xiàn)。例如,在每次主循環(huán)迭代中,都檢查一次按鍵的狀態(tài)。
3. 按鍵狀態(tài)檢測
一般來說,按鍵有兩種狀態(tài):按下和釋放。在檢測按鍵狀態(tài)時,需要注意去除按鍵的抖動干擾。抖動是指在按鍵被按下或釋放時,由于機械接觸或物理特性導(dǎo)致的瞬間狀態(tài)變化。為了應(yīng)對抖動,可以采用軟件方法或硬件濾波器。
本示例采用延時10ms讀取值的方法來去抖,示例:
if(key_up_value == 1 || key_down_value ==0 || key_left_value ==0 || key_right_value ==0){delay_ms(10);
}
硬件方法去抖可以參考實現(xiàn):SR觸發(fā)器去抖
4. 循環(huán)掃描的優(yōu)缺點
優(yōu)點:
-
簡單直觀: 在循環(huán)中進行按鍵掃描的方法簡單易懂,邏輯清晰,易于理解和實現(xiàn)。
-
靈活性: 可以根據(jù)具體需求靈活調(diào)整掃描的頻率和方式,滿足不同場景下的要求。
-
適用性廣: 適用于小型嵌入式系統(tǒng)或者對按鍵響應(yīng)速度要求不高的場景,適用性廣泛。
-
資源消耗低: 相比于中斷方式,循環(huán)掃描不需要額外的中斷處理函數(shù),減少了系統(tǒng)資源的占用。
缺點:
-
效率低下: 在循環(huán)中進行按鍵掃描會占用 CPU 的時間片,降低了系統(tǒng)的處理效率,特別是當(dāng)系統(tǒng)有其他緊急任務(wù)需要處理時,會影響響應(yīng)速度和實時性。
-
實時性差: 循環(huán)掃描需要不斷地遍歷所有按鍵狀態(tài),導(dǎo)致按鍵的檢測周期相對較長,實時性差,無法滿足對按鍵響應(yīng)速度要求較高的場景。
-
占用 CPU 資源: 循環(huán)掃描需要持續(xù)占用 CPU 資源,特別是在大型系統(tǒng)中,可能會影響其他任務(wù)的執(zhí)行,降低系統(tǒng)的整體性能。
-
功耗高: 循環(huán)掃描需要 CPU 不斷地處于工作狀態(tài),會增加系統(tǒng)的功耗,對于對功耗要求較高的場景不太適用。
后面學(xué)習(xí)中會采用中斷的方式來讀取鍵盤。
四、一次掃描與持續(xù)掃描
這里的一次掃描,是指按下按鍵后,如果不松開,鍵盤的掃描函數(shù)不會繼續(xù)輸出所按鍵值。
而持續(xù)掃描,在按下按鍵后,如果手不松開,鍵盤的掃描函數(shù)仍會持續(xù)輸出按鍵值。
五、代碼實現(xiàn)
為方便看到演示效果,示例的代碼在獲取到掃描的按鍵后,會在數(shù)碼管顯示不同的數(shù)值。
- 上:顯示0
- 下:顯示1
- 左:顯示2
- 右:顯示3
1. 頭文件定義
key_utils.h
#ifndef __KEY_UTILS_H__
#define __KEY_UTILS_H__
#include "stm32f10x.h"// 引腳和端口
#define KEY_UP_PIN GPIO_Pin_0
#define KEY_UP_PORT GPIOA
#define KEY_LEFT_PIN GPIO_Pin_2
#define KEY_LEFT_PORT GPIOE
#define KEY_DOWN_PIN GPIO_Pin_3
#define KEY_DOWN_PORT GPIOE
#define KEY_RIGHT_PIN GPIO_Pin_4
#define KEY_RIGHT_PORT GPIOE// 讀取引腳狀態(tài)
#define key_up_value GPIO_ReadInputDataBit(KEY_UP_PORT, KEY_UP_PIN)
#define key_down_value GPIO_ReadInputDataBit(KEY_DOWN_PORT, KEY_DOWN_PIN)
#define key_left_value GPIO_ReadInputDataBit(KEY_LEFT_PORT, KEY_LEFT_PIN)
#define key_right_value GPIO_ReadInputDataBit(KEY_RIGHT_PORT, KEY_RIGHT_PIN)// 按鍵
#define KEY_UP 0
#define KEY_DOWN 1
#define KEY_LEFT 2
#define KEY_RIGHT 3
#define KEY_NONE 4void key_init(void);
u8 key_scan(u8 mode);
#endif
2. 函數(shù)實現(xiàn)
#include "key_utils.h"
#include "sys_tick_utils.h"void key_init(void)
{GPIO_InitTypeDef GPIO_InitStructure; // 定義GPIO初始化結(jié)構(gòu)體// 開啟GPIOA的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 設(shè)置上引腳GPIO_InitStructure.GPIO_Pin = KEY_UP_PIN;// 設(shè)置輸入下拉模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;GPIO_Init(KEY_UP_PORT, &GPIO_InitStructure); // 初始化GPIOA// 開 E 時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);// 設(shè)置下、左、右引腳GPIO_InitStructure.GPIO_Pin = KEY_DOWN_PIN | KEY_LEFT_PIN | KEY_RIGHT_PIN;// 設(shè)置輸入上拉模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(KEY_DOWN_PORT, &GPIO_InitStructure); // 初始化GPIOE}
static u8 key_read(void){if(key_up_value == 1 || key_down_value ==0 || key_left_value ==0 || key_right_value ==0){delay_ms(10);if(key_up_value == 1){return KEY_UP;}else if(key_down_value == 0){return KEY_DOWN;}else if(key_left_value == 0){return KEY_LEFT;}else if(key_right_value == 0){return KEY_RIGHT;}}return KEY_NONE;
}
u8 last_key;
/*** @brief 按鍵掃描函數(shù)* @param mode: 0 單次掃描 1: 連續(xù)掃描*/
u8 key_scan(u8 mode)
{if(mode==0){u8 key = key_read();if(key != KEY_NONE){if(key == last_key){return KEY_NONE;}else{last_key = key;return key;}}else{last_key = KEY_NONE;}}else{return key_read();}return KEY_NONE;
}
3. 主體函數(shù)
#include "gpio_utils.h"
#include "rcc_utils.h"
#include "stm32f10x.h"
#include "sys_tick_utils.h"
#include "led_utils.h"
#include "key_utils.h"// 主函數(shù)
int main(void)
{GPIO_Configuration(); //調(diào)用GPIO配置函數(shù)sys_tick_init(72);led_all_off();key_init();while (1) //無限循環(huán){delay_ms(10);u8 key = key_scan(0);if(key==KEY_UP){led_lightn(0);}else if(key==KEY_DOWN){led_lightn(1);}else if(key==KEY_LEFT){led_lightn(2);}else if(key==KEY_RIGHT){led_lightn(3);}else{led_all_off();}}
}
本文源碼地址:
https://gitee.com/xundh/stm32_arm_learn/tree/master/lesson7_key