一、按鍵檢測原理
按鍵檢測原理比較簡單,按鍵按下和不按下,其連接引腳的電平是不一樣的,按鍵檢測正是通過檢測按鍵引腳的電平變化來實現的。比如按鍵未按下時引腳電平為高電平,按鍵按下后為低電平。我們在檢測按鍵時只需要檢測按鍵引腳是否變為低電平來確定按鍵是否按下。
二、硬件連接
按鍵的硬件連接決定了我們在配置按鍵IO時IO的狀態。以我們使用的普中核心板為例,上面有三個按鍵
普中核心板按鍵硬件電路圖
其中K1一端接VCC,另一端接單片機。K2和K3一端接地,另一端接單片機。硬件電路不同,導致他們在進行按鍵檢測時IO的配置不同。
針對K1這種按鍵電路,按鍵按下時,單片機的引腳接到VCC,因此在未按下的情況下該引腳的默認電平為低電平,也就是要把IO設置為輸入下拉模式。同理,對于K2和K3這種連接方式,對應IO應該配置為輸入上拉模式,使得按鍵未被按下時,引腳處于高電平狀態。
三、程序設計
按鍵檢測主要有以下步驟
- ? 初始化GPIO
- ? 檢測按下按鍵
- ? 消抖(防誤觸,一般通過延時實現)
- ? 松手檢測
- ? 執行按鍵功能
3.1 初始化GPIO
根據原理圖,譜中的STM32核心板提供了三個按鍵,我們使用K1和K2來實現點亮和關閉LED的操作。K1對應的IO為PA0,K2對應的IO為PE4。
按鍵對應GPIO
根據上一節了解的初始化GPIO程序,初始化按鍵GPIO。
/*
*==============================================================================
*函數名稱:Drv_KeyGpio_Init
*函數功能:初始化KEY的GPIO
*輸入參數:無
*返回值:無
*備 注:根據硬件電路確定GPIO模式
*==============================================================================
*/
void Drv_KeyGpio_Init (void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定義結構體
// 開啟時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE,ENABLE);
// 配置結構體 WK UP
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 輸入下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置結構體 KEY0,KEY1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 輸入上拉
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
3.2 按鍵掃描函數
按鍵掃描函數的功能是檢測是否有按鍵按下,按下的按鍵是哪一個。檢測方法上面已經敘述,通過檢測按鍵引腳的電平。以WK UP按鍵為例。當WK UP被按下時,其對應的引腳PA0會變為高電平。
此時檢測PA0的輸入電平,如果確實是低電平,則說明WK UP可能被按下。說可能是因為PA0為低電平不一定是WK UP按下造成,也可能是抖動,所以這里就需要消抖操作。這里的消抖操作比較簡單粗暴,直接延時10ms看該引腳是否依舊是低電平。如果延時10ms后依舊是高電平,則認為確實是由按鍵按下導致的電平變化,而不是機械抖動。
確定檢測到按鍵按下后,需要等待按鍵被松開在執行按鍵功能。為什么需要進行松手檢測?舉個例子,比如設置閾值時,按鍵按下閾值加1,如果不進行松手檢測,那么按下一次按鍵會加很多次,因為在不停地執行按鍵功能。
這里按鍵的松手檢測也比較簡單粗暴,用一個while死循環等待松手。比如WK UP被按下后,其引腳會一直保持高電平,也就是PAin(0)一直等于1,此時用一個while (PAin(1));來等待松手,做松手檢測。
四、按鍵控制LED
這里做一個小練習,用普中核心板上的按鍵KEY0和KEY1來控制LED1的亮滅。步驟如下
- ? 初始化LED和KEY的GPIO
- ? 編寫LED控制函數
- ? 編寫按鍵檢測函數(檢測按鍵)
- ? 編寫按鍵服務函數(實現按鍵功能)
4.1 初始化LED和KEY的GPIO
/*
*==============================================================================
*函數名稱:Drv_LedGpio_Init
*函數功能:初始化LED的GPIO
*輸入參數:無
*返回值:無
*備 注:無
*==============================================================================
*/
void Drv_LedGpio_Init (void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定義結構體
// 開啟時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE,ENABLE);
// 配置結構體 LED0
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽式輸出
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_5); // 熄滅LED
// 配置結構體 LED1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽式輸出
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_SetBits(GPIOE,GPIO_Pin_5); // 熄滅LED
}
/*
*==============================================================================
*函數名稱:Drv_KeyGpio_Init
*函數功能:初始化KEY的GPIO
*輸入參數:無
*返回值:無
*備 注:根據硬件電路確定GPIO模式
*==============================================================================
*/
void Drv_KeyGpio_Init (void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定義結構體
// 開啟時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE,ENABLE);
// 配置結構體 WK UP
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 輸入下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置結構體 KEY0,KEY1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 輸入上拉
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
4.2 編寫按鍵掃描函數
```c
/*
*==============================================================================
*函數名稱:Med_KeyScan
*函數功能:檢測按下按鍵
*輸入參數:無
*返回值:按鍵鍵值 0:按鍵WK UP,1:KEY0,2:KEY1
*備 注:無
*==============================================================================
*/
u8 Med_KeyScan (void)
{
// 按鍵WK UP
if (KEY_UP == 1)
{
delay_ms (10); // 延時10ms消抖
if (KEY_UP == 1)
{
while (KEY_UP); // 松手檢測
return 1;
}
}
// 按鍵KEY0
else if (KEY0 == 0)
{
delay_ms (10); // 延時10ms消抖
if (KEY0 == 0)
{
while (!KEY0); // 松手檢測
return 2;
}
}
// 按鍵KEY1
else if (KEY1 == 0)
{
delay_ms (10); // 延時10ms消抖
if (KEY1 == 0)
{
while (!KEY1); // 松手檢測
return 3;
}
}
// 沒有按鍵按下
return 0xff; // 用0xff表示沒有按鍵按下
}
4.2 編寫LED控制函數
/*
*==============================================================================
*函數名稱:Med_Led_StateCtrl
*函數功能:控制LED亮滅
*輸入參數:
LEDx:可選擇的LED(0~1)
State:LED亮滅狀態(LED_ON,LED_OFF)
*返回值:無
*備 注:無
*==============================================================================
*/
void Med_Led_StateCtrl (LED_TypeDef LEDx,u8 State)
{
switch (LEDx)
{
case 0:
PBout(5) = State;
break;
case 1:
PEout(5) = State;
break;
default:
break;
}
}
下面是.h文件中的一些結構體和宏定義。
// 可選擇的LED
typedef enum
{
LED1 = 0,
LED2
}LED_TypeDef;
// 亮滅電平需要根據硬件電路確定
#define LED_ON 0
#define LED_OFF 1
4.3 編寫按鍵服務函數
這里沒有再單獨編寫按鍵服務函數,直接在main函數中編寫。KETY0按下點亮LED1,KEY1按下,熄滅LED1。
u8 gKeyValue = 0; // 記錄按鍵鍵值變量
int main(void)
{
Med_Mcu_Iint(); // 系統初始化
while(1)
{
gKeyValue = Med_KeyScan(); // 獲取按鍵鍵值
// 按鍵KEY0按下
if (gKeyValue == 2)
{
Med_Led_StateCtrl(LED1,LED_ON); // 點亮LED1
}
// 按鍵KEY1按下
if (gKeyValue == 3)
{
Med_Led_StateCtrl(LED1,LED_OFF); // 熄滅LED1
}
}
}
至此,實現了利用KEY0和KEY1控制LED的亮滅狀態。
五、拓展
5.1 一個按鍵單獨控制一個LED亮滅
單片機的IO資源是比較珍貴的,在實際用用時很少會用兩個IO資源來控制一個外設的開關,這里介紹一下方法并給出例程。比如使用普中核心板上的WK UP按鍵來控制LED2的亮滅狀態。基本思路是在上面學會按鍵檢測的基礎上,增加一個按鍵按下計次變量。按鍵按下一次,該變量加1。如果檢測到變量為1,那么點亮LED,如果檢測到變量為2,那么熄滅LED,同時將計數變量清零。程序設計如下
u8 gKeyValue = 0; // 記錄按鍵鍵值變量
u8 gKeyWkUpCunt = 0; // WK UP按下次數計數變量
int main(void)
{
Med_Mcu_Iint(); // 系統初始化
while(1)
{
gKeyValue = Med_KeyScan(); // 獲取按鍵鍵值
// 按鍵WK UP按下
if (gKeyValue == 1)
{
gKeyWkUpCunt = gKeyWkUpCunt + 1; // 按鍵按下次數計數變量加1
// 第一次被按下
if (gKeyWkUpCunt <= 1)
{
Med_Led_StateCtrl(LED2,LED_ON); // 點亮LED2
}
// 不是第一次被按下
else if (gKeyWkUpCunt > 1)
{
gKeyWkUpCunt = 0; // 清空計數變量
Med_Led_StateCtrl(LED2,LED_OFF); // 熄滅LED2
}
}
}
}
5.2 按鍵長短按
除了上面介紹的一些常規操作外,有時還會用到一個按鍵分長按和短按。這里給出一種簡單粗暴的實現思路。需要檢測按鍵長短按時,修改一下松手檢測的邏輯。延時10ms后如果按鍵IO依舊保持按下狀態,那么確定不是機械抖動,此時在之前的松手檢測while中進行粗略地計時。定義一個計數變量,每隔10ms加1。直到按鍵被松開。根據計數變量的值來判斷按鍵被按下的時間,從而來區別長短按。
這里給出一個例程,KEY1短按功能為熄滅LED1,長按功能為LED1和LED2交替閃爍兩輪后熄滅。按下持續時間在1s內,認為是短按,按下超過2s認為是長按。短按返回3,長按返回4。程序設計如下
u8 gKeyValue = 0; // 記錄按鍵鍵值變量
u16 gKey1TimeCunt = 0; // 按鍵KEY1的計時變量
/*
*==============================================================================
*函數名稱:Med_KeyScan
*函數功能:檢測按下按鍵
*輸入參數:無
*返回值:按鍵鍵值 0:按鍵WK UP,1:KEY0,2:KEY1
*備 注:無
*==============================================================================
*/
u8 Med_KeyScan (void)
{
// 按鍵WK UP
if (KEY_UP == 1)
{
delay_ms (10); // 延時10ms消抖
if (KEY_UP == 1)
{
while (KEY_UP); // 松手檢測
return 1;
}
}
// 按鍵KEY0
else if (KEY0 == 0)
{
delay_ms (10); // 延時10ms消抖
if (KEY0 == 0)
{
while (!KEY0); // 松手檢測
return 2;
}
}
// 按鍵KEY1
// 按下1s內認為是短按,返回3
// 按下超過2s認為是長按,返回4
else if (KEY1 == 0)
{
delay_ms (10); // 延時10ms消抖
if (KEY1 == 0)
{
// 等待松手
while (!KEY1)
{
delay_ms (10);
gKey1TimeCunt = gKey1TimeCunt + 1; // 計時變量加1
}
}
// 判斷長短按
if (gKey1TimeCunt <= 99) // 小于等于1s
{
gKey1TimeCunt = 0; // 清零計時變量
return 3; // 短按
}
else if (gKey1TimeCunt >= 199) // 大于1s,等于2s
{
gKey1TimeCunt = 0; // 清零計時變量
return 4; // 長按
}
}
// 沒有按鍵按下
return 0xff; // 用0xff表示沒有按鍵按下
}
main函數中添加下述程序
// 長按KEY1
if (gKeyValue == 4)
{
Med_Led_StateCtrl(LED1,LED_ON); // 點亮LED1
delay_ms (500);
Med_Led_StateCtrl(LED1,LED_OFF); // 熄滅LED1
Med_Led_StateCtrl(LED2,LED_ON); // 點亮LED2
delay_ms (500);
Med_Led_StateCtrl(LED2,LED_OFF); // 熄滅LED2
Med_Led_StateCtrl(LED1,LED_ON); // 點亮LED1
delay_ms (500);
Med_Led_StateCtrl(LED1,LED_OFF); // 熄滅LED1
Med_Led_StateCtrl(LED2,LED_ON); // 點亮LED2
delay_ms (500);
Med_Led_StateCtrl(LED1,LED_OFF); // 熄滅LED1
Med_Led_StateCtrl(LED2,LED_OFF); // 熄滅LED1
}
-
STM32
+關注
關注
2266文章
10871瀏覽量
354786 -
GPIO
+關注
關注
16文章
1196瀏覽量
51909 -
LED控制器
+關注
關注
0文章
63瀏覽量
20623 -
按鍵電路
+關注
關注
1文章
35瀏覽量
21756 -
按鍵控制
+關注
關注
1文章
44瀏覽量
8769
發布評論請先 登錄
相關推薦
評論