一、按鍵檢測原理
按鍵檢測原理比較簡單,按鍵按下和不按下,其連接引腳的電平是不一樣的,按鍵檢測正是通過檢測按鍵引腳的電平變化來實現的。比如按鍵未按下時引腳電平為高電平,按鍵按下后為低電平。我們在檢測按鍵時只需要檢測按鍵引腳是否變?yōu)榈碗娖絹泶_定按鍵是否按下。
二、硬件連接
按鍵的硬件連接決定了我們在配置按鍵IO時IO的狀態(tài)。以我們使用的普中核心板為例,上面有三個按鍵
普中核心板按鍵硬件電路圖
其中K1一端接VCC,另一端接單片機。K2和K3一端接地,另一端接單片機。硬件電路不同,導致他們在進行按鍵檢測時IO的配置不同。
針對K1這種按鍵電路,按鍵按下時,單片機的引腳接到VCC,因此在未按下的情況下該引腳的默認電平為低電平,也就是要把IO設置為輸入下拉模式。同理,對于K2和K3這種連接方式,對應IO應該配置為輸入上拉模式,使得按鍵未被按下時,引腳處于高電平狀態(tài)。
三、程序設計
按鍵檢測主要有以下步驟
- ? 初始化GPIO
- ? 檢測按下按鍵
- ? 消抖(防誤觸,一般通過延時實現)
- ? 松手檢測
- ? 執(zhí)行按鍵功能
3.1 初始化GPIO
根據原理圖,譜中的STM32核心板提供了三個按鍵,我們使用K1和K2來實現點亮和關閉LED的操作。K1對應的IO為PA0,K2對應的IO為PE4。
按鍵對應GPIO
根據上一節(jié)了解的初始化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會變?yōu)楦唠娖健?/p>
此時檢測PA0的輸入電平,如果確實是低電平,則說明WK UP可能被按下。說可能是因為PA0為低電平不一定是WK UP按下造成,也可能是抖動,所以這里就需要消抖操作。這里的消抖操作比較簡單粗暴,直接延時10ms看該引腳是否依舊是低電平。如果延時10ms后依舊是高電平,則認為確實是由按鍵按下導致的電平變化,而不是機械抖動。
確定檢測到按鍵按下后,需要等待按鍵被松開在執(zhí)行按鍵功能。為什么需要進行松手檢測?舉個例子,比如設置閾值時,按鍵按下閾值加1,如果不進行松手檢測,那么按下一次按鍵會加很多次,因為在不停地執(zhí)行按鍵功能。
這里按鍵的松手檢測也比較簡單粗暴,用一個while死循環(huán)等待松手。比如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亮滅狀態(tài)(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(); // 系統(tǒng)初始化
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的亮滅狀態(tài)。
五、拓展
5.1 一個按鍵單獨控制一個LED亮滅
單片機的IO資源是比較珍貴的,在實際用用時很少會用兩個IO資源來控制一個外設的開關,這里介紹一下方法并給出例程。比如使用普中核心板上的WK UP按鍵來控制LED2的亮滅狀態(tài)?;舅悸肥窃谏厦鎸W會按鍵檢測的基礎上,增加一個按鍵按下計次變量。按鍵按下一次,該變量加1。如果檢測到變量為1,那么點亮LED,如果檢測到變量為2,那么熄滅LED,同時將計數變量清零。程序設計如下
u8 gKeyValue = 0; // 記錄按鍵鍵值變量
u8 gKeyWkUpCunt = 0; // WK UP按下次數計數變量
int main(void)
{
Med_Mcu_Iint(); // 系統(tǒng)初始化
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 按鍵長短按
除了上面介紹的一些常規(guī)操作外,有時還會用到一個按鍵分長按和短按。這里給出一種簡單粗暴的實現思路。需要檢測按鍵長短按時,修改一下松手檢測的邏輯。延時10ms后如果按鍵IO依舊保持按下狀態(tài),那么確定不是機械抖動,此時在之前的松手檢測while中進行粗略地計時。定義一個計數變量,每隔10ms加1。直到按鍵被松開。根據計數變量的值來判斷按鍵被按下的時間,從而來區(qū)別長短按。
這里給出一個例程,KEY1短按功能為熄滅LED1,長按功能為LED1和LED2交替閃爍兩輪后熄滅。按下持續(xù)時間在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
+關注
關注
2270文章
10900瀏覽量
355982 -
GPIO
+關注
關注
16文章
1204瀏覽量
52091 -
LED控制器
+關注
關注
0文章
67瀏覽量
20639 -
按鍵電路
+關注
關注
1文章
35瀏覽量
21774 -
按鍵控制
+關注
關注
1文章
44瀏覽量
8778
發(fā)布評論請先 登錄
相關推薦
評論