一、按鍵檢測(cè)原理
按鍵檢測(cè)原理比較簡(jiǎn)單,按鍵按下和不按下,其連接引腳的電平是不一樣的,按鍵檢測(cè)正是通過(guò)檢測(cè)按鍵引腳的電平變化來(lái)實(shí)現(xiàn)的。比如按鍵未按下時(shí)引腳電平為高電平,按鍵按下后為低電平。我們?cè)跈z測(cè)按鍵時(shí)只需要檢測(cè)按鍵引腳是否變?yōu)榈碗娖絹?lái)確定按鍵是否按下。
二、硬件連接
按鍵的硬件連接決定了我們?cè)谂渲冒存IIO時(shí)IO的狀態(tài)。以我們使用的普中核心板為例,上面有三個(gè)按鍵
普中核心板按鍵硬件電路圖
其中K1一端接VCC,另一端接單片機(jī)。K2和K3一端接地,另一端接單片機(jī)。硬件電路不同,導(dǎo)致他們?cè)谶M(jìn)行按鍵檢測(cè)時(shí)IO的配置不同。
針對(duì)K1這種按鍵電路,按鍵按下時(shí),單片機(jī)的引腳接到VCC,因此在未按下的情況下該引腳的默認(rèn)電平為低電平,也就是要把IO設(shè)置為輸入下拉模式。同理,對(duì)于K2和K3這種連接方式,對(duì)應(yīng)IO應(yīng)該配置為輸入上拉模式,使得按鍵未被按下時(shí),引腳處于高電平狀態(tài)。
三、程序設(shè)計(jì)
按鍵檢測(cè)主要有以下步驟
- ? 初始化GPIO
- ? 檢測(cè)按下按鍵
- ? 消抖(防誤觸,一般通過(guò)延時(shí)實(shí)現(xiàn))
- ? 松手檢測(cè)
- ? 執(zhí)行按鍵功能
3.1 初始化GPIO
根據(jù)原理圖,譜中的STM32核心板提供了三個(gè)按鍵,我們使用K1和K2來(lái)實(shí)現(xiàn)點(diǎn)亮和關(guān)閉LED的操作。K1對(duì)應(yīng)的IO為PA0,K2對(duì)應(yīng)的IO為PE4。
按鍵對(duì)應(yīng)GPIO
根據(jù)上一節(jié)了解的初始化GPIO程序,初始化按鍵GPIO。
/*
*==============================================================================
*函數(shù)名稱:Drv_KeyGpio_Init
*函數(shù)功能:初始化KEY的GPIO
*輸入參數(shù):無(wú)
*返回值:無(wú)
*備 注:根據(jù)硬件電路確定GPIO模式
*==============================================================================
*/
void Drv_KeyGpio_Init (void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定義結(jié)構(gòu)體
// 開(kāi)啟時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE,ENABLE);
// 配置結(jié)構(gòu)體 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);
// 配置結(jié)構(gòu)體 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 按鍵掃描函數(shù)
按鍵掃描函數(shù)的功能是檢測(cè)是否有按鍵按下,按下的按鍵是哪一個(gè)。檢測(cè)方法上面已經(jīng)敘述,通過(guò)檢測(cè)按鍵引腳的電平。以WK UP按鍵為例。當(dāng)WK UP被按下時(shí),其對(duì)應(yīng)的引腳PA0會(huì)變?yōu)楦唠娖健?/p>
此時(shí)檢測(cè)PA0的輸入電平,如果確實(shí)是低電平,則說(shuō)明WK UP可能被按下。說(shuō)可能是因?yàn)镻A0為低電平不一定是WK UP按下造成,也可能是抖動(dòng),所以這里就需要消抖操作。這里的消抖操作比較簡(jiǎn)單粗暴,直接延時(shí)10ms看該引腳是否依舊是低電平。如果延時(shí)10ms后依舊是高電平,則認(rèn)為確實(shí)是由按鍵按下導(dǎo)致的電平變化,而不是機(jī)械抖動(dòng)。
確定檢測(cè)到按鍵按下后,需要等待按鍵被松開(kāi)在執(zhí)行按鍵功能。為什么需要進(jìn)行松手檢測(cè)?舉個(gè)例子,比如設(shè)置閾值時(shí),按鍵按下閾值加1,如果不進(jìn)行松手檢測(cè),那么按下一次按鍵會(huì)加很多次,因?yàn)樵诓煌5貓?zhí)行按鍵功能。
這里按鍵的松手檢測(cè)也比較簡(jiǎn)單粗暴,用一個(gè)while死循環(huán)等待松手。比如WK UP被按下后,其引腳會(huì)一直保持高電平,也就是PAin(0)一直等于1,此時(shí)用一個(gè)while (PAin(1));來(lái)等待松手,做松手檢測(cè)。
四、按鍵控制LED
這里做一個(gè)小練習(xí),用普中核心板上的按鍵KEY0和KEY1來(lái)控制LED1的亮滅。步驟如下
- ? 初始化LED和KEY的GPIO
- ? 編寫(xiě)LED控制函數(shù)
- ? 編寫(xiě)按鍵檢測(cè)函數(shù)(檢測(cè)按鍵)
- ? 編寫(xiě)按鍵服務(wù)函數(shù)(實(shí)現(xiàn)按鍵功能)
4.1 初始化LED和KEY的GPIO
/*
*==============================================================================
*函數(shù)名稱:Drv_LedGpio_Init
*函數(shù)功能:初始化LED的GPIO
*輸入?yún)?shù):無(wú)
*返回值:無(wú)
*備 注:無(wú)
*==============================================================================
*/
void Drv_LedGpio_Init (void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定義結(jié)構(gòu)體
// 開(kāi)啟時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE,ENABLE);
// 配置結(jié)構(gòu)體 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
// 配置結(jié)構(gòu)體 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
}
/*
*==============================================================================
*函數(shù)名稱:Drv_KeyGpio_Init
*函數(shù)功能:初始化KEY的GPIO
*輸入?yún)?shù):無(wú)
*返回值:無(wú)
*備 注:根據(jù)硬件電路確定GPIO模式
*==============================================================================
*/
void Drv_KeyGpio_Init (void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定義結(jié)構(gòu)體
// 開(kāi)啟時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE,ENABLE);
// 配置結(jié)構(gòu)體 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);
// 配置結(jié)構(gòu)體 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 編寫(xiě)按鍵掃描函數(shù)
```c
/*
*==============================================================================
*函數(shù)名稱:Med_KeyScan
*函數(shù)功能:檢測(cè)按下按鍵
*輸入?yún)?shù):無(wú)
*返回值:按鍵鍵值 0:按鍵WK UP,1:KEY0,2:KEY1
*備 注:無(wú)
*==============================================================================
*/
u8 Med_KeyScan (void)
{
// 按鍵WK UP
if (KEY_UP == 1)
{
delay_ms (10); // 延時(shí)10ms消抖
if (KEY_UP == 1)
{
while (KEY_UP); // 松手檢測(cè)
return 1;
}
}
// 按鍵KEY0
else if (KEY0 == 0)
{
delay_ms (10); // 延時(shí)10ms消抖
if (KEY0 == 0)
{
while (!KEY0); // 松手檢測(cè)
return 2;
}
}
// 按鍵KEY1
else if (KEY1 == 0)
{
delay_ms (10); // 延時(shí)10ms消抖
if (KEY1 == 0)
{
while (!KEY1); // 松手檢測(cè)
return 3;
}
}
// 沒(méi)有按鍵按下
return 0xff; // 用0xff表示沒(méi)有按鍵按下
}
4.2 編寫(xiě)LED控制函數(shù)
/*
*==============================================================================
*函數(shù)名稱:Med_Led_StateCtrl
*函數(shù)功能:控制LED亮滅
*輸入?yún)?shù):
LEDx:可選擇的LED(0~1)
State:LED亮滅狀態(tài)(LED_ON,LED_OFF)
*返回值:無(wú)
*備 注:無(wú)
*==============================================================================
*/
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文件中的一些結(jié)構(gòu)體和宏定義。
// 可選擇的LED
typedef enum
{
LED1 = 0,
LED2
}LED_TypeDef;
// 亮滅電平需要根據(jù)硬件電路確定
#define LED_ON 0
#define LED_OFF 1
4.3 編寫(xiě)按鍵服務(wù)函數(shù)
這里沒(méi)有再單獨(dú)編寫(xiě)按鍵服務(wù)函數(shù),直接在main函數(shù)中編寫(xiě)。KETY0按下點(diǎn)亮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); // 點(diǎn)亮LED1
}
// 按鍵KEY1按下
if (gKeyValue == 3)
{
Med_Led_StateCtrl(LED1,LED_OFF); // 熄滅LED1
}
}
}
至此,實(shí)現(xiàn)了利用KEY0和KEY1控制LED的亮滅狀態(tài)。
五、拓展
5.1 一個(gè)按鍵單獨(dú)控制一個(gè)LED亮滅
單片機(jī)的IO資源是比較珍貴的,在實(shí)際用用時(shí)很少會(huì)用兩個(gè)IO資源來(lái)控制一個(gè)外設(shè)的開(kāi)關(guān),這里介紹一下方法并給出例程。比如使用普中核心板上的WK UP按鍵來(lái)控制LED2的亮滅狀態(tài)?;舅悸肥窃谏厦鎸W(xué)會(huì)按鍵檢測(cè)的基礎(chǔ)上,增加一個(gè)按鍵按下計(jì)次變量。按鍵按下一次,該變量加1。如果檢測(cè)到變量為1,那么點(diǎn)亮LED,如果檢測(cè)到變量為2,那么熄滅LED,同時(shí)將計(jì)數(shù)變量清零。程序設(shè)計(jì)如下
u8 gKeyValue = 0; // 記錄按鍵鍵值變量
u8 gKeyWkUpCunt = 0; // WK UP按下次數(shù)計(jì)數(shù)變量
int main(void)
{
Med_Mcu_Iint(); // 系統(tǒng)初始化
while(1)
{
gKeyValue = Med_KeyScan(); // 獲取按鍵鍵值
// 按鍵WK UP按下
if (gKeyValue == 1)
{
gKeyWkUpCunt = gKeyWkUpCunt + 1; // 按鍵按下次數(shù)計(jì)數(shù)變量加1
// 第一次被按下
if (gKeyWkUpCunt <= 1)
{
Med_Led_StateCtrl(LED2,LED_ON); // 點(diǎn)亮LED2
}
// 不是第一次被按下
else if (gKeyWkUpCunt > 1)
{
gKeyWkUpCunt = 0; // 清空計(jì)數(shù)變量
Med_Led_StateCtrl(LED2,LED_OFF); // 熄滅LED2
}
}
}
}
5.2 按鍵長(zhǎng)短按
除了上面介紹的一些常規(guī)操作外,有時(shí)還會(huì)用到一個(gè)按鍵分長(zhǎng)按和短按。這里給出一種簡(jiǎn)單粗暴的實(shí)現(xiàn)思路。需要檢測(cè)按鍵長(zhǎng)短按時(shí),修改一下松手檢測(cè)的邏輯。延時(shí)10ms后如果按鍵IO依舊保持按下?tīng)顟B(tài),那么確定不是機(jī)械抖動(dòng),此時(shí)在之前的松手檢測(cè)while中進(jìn)行粗略地計(jì)時(shí)。定義一個(gè)計(jì)數(shù)變量,每隔10ms加1。直到按鍵被松開(kāi)。根據(jù)計(jì)數(shù)變量的值來(lái)判斷按鍵被按下的時(shí)間,從而來(lái)區(qū)別長(zhǎng)短按。
這里給出一個(gè)例程,KEY1短按功能為熄滅LED1,長(zhǎng)按功能為L(zhǎng)ED1和LED2交替閃爍兩輪后熄滅。按下持續(xù)時(shí)間在1s內(nèi),認(rèn)為是短按,按下超過(guò)2s認(rèn)為是長(zhǎng)按。短按返回3,長(zhǎng)按返回4。程序設(shè)計(jì)如下
u8 gKeyValue = 0; // 記錄按鍵鍵值變量
u16 gKey1TimeCunt = 0; // 按鍵KEY1的計(jì)時(shí)變量
/*
*==============================================================================
*函數(shù)名稱:Med_KeyScan
*函數(shù)功能:檢測(cè)按下按鍵
*輸入?yún)?shù):無(wú)
*返回值:按鍵鍵值 0:按鍵WK UP,1:KEY0,2:KEY1
*備 注:無(wú)
*==============================================================================
*/
u8 Med_KeyScan (void)
{
// 按鍵WK UP
if (KEY_UP == 1)
{
delay_ms (10); // 延時(shí)10ms消抖
if (KEY_UP == 1)
{
while (KEY_UP); // 松手檢測(cè)
return 1;
}
}
// 按鍵KEY0
else if (KEY0 == 0)
{
delay_ms (10); // 延時(shí)10ms消抖
if (KEY0 == 0)
{
while (!KEY0); // 松手檢測(cè)
return 2;
}
}
// 按鍵KEY1
// 按下1s內(nèi)認(rèn)為是短按,返回3
// 按下超過(guò)2s認(rèn)為是長(zhǎng)按,返回4
else if (KEY1 == 0)
{
delay_ms (10); // 延時(shí)10ms消抖
if (KEY1 == 0)
{
// 等待松手
while (!KEY1)
{
delay_ms (10);
gKey1TimeCunt = gKey1TimeCunt + 1; // 計(jì)時(shí)變量加1
}
}
// 判斷長(zhǎng)短按
if (gKey1TimeCunt <= 99) // 小于等于1s
{
gKey1TimeCunt = 0; // 清零計(jì)時(shí)變量
return 3; // 短按
}
else if (gKey1TimeCunt >= 199) // 大于1s,等于2s
{
gKey1TimeCunt = 0; // 清零計(jì)時(shí)變量
return 4; // 長(zhǎng)按
}
}
// 沒(méi)有按鍵按下
return 0xff; // 用0xff表示沒(méi)有按鍵按下
}
main函數(shù)中添加下述程序
// 長(zhǎng)按KEY1
if (gKeyValue == 4)
{
Med_Led_StateCtrl(LED1,LED_ON); // 點(diǎn)亮LED1
delay_ms (500);
Med_Led_StateCtrl(LED1,LED_OFF); // 熄滅LED1
Med_Led_StateCtrl(LED2,LED_ON); // 點(diǎn)亮LED2
delay_ms (500);
Med_Led_StateCtrl(LED2,LED_OFF); // 熄滅LED2
Med_Led_StateCtrl(LED1,LED_ON); // 點(diǎn)亮LED1
delay_ms (500);
Med_Led_StateCtrl(LED1,LED_OFF); // 熄滅LED1
Med_Led_StateCtrl(LED2,LED_ON); // 點(diǎn)亮LED2
delay_ms (500);
Med_Led_StateCtrl(LED1,LED_OFF); // 熄滅LED1
Med_Led_StateCtrl(LED2,LED_OFF); // 熄滅LED1
}
-
STM32
+關(guān)注
關(guān)注
2272文章
10923瀏覽量
357413 -
GPIO
+關(guān)注
關(guān)注
16文章
1216瀏覽量
52347 -
LED控制器
+關(guān)注
關(guān)注
0文章
67瀏覽量
20676 -
按鍵電路
+關(guān)注
關(guān)注
1文章
35瀏覽量
21817 -
按鍵控制
+關(guān)注
關(guān)注
1文章
44瀏覽量
8828
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論