按键驱动设计
文章目錄
- 1 按鍵驅(qū)動(dòng)設(shè)計(jì)
1 按鍵驅(qū)動(dòng)設(shè)計(jì)
硬件結(jié)構(gòu)如下:
按鍵驅(qū)動(dòng)綜合使用了軟定時(shí)器 + 狀態(tài)機(jī) + 郵箱數(shù)據(jù)通信。
- 軟定時(shí)器負(fù)責(zé)周期性地調(diào)用按鍵檢測(cè)掃描函數(shù)。
- 狀態(tài)機(jī)負(fù)責(zé)處理按鍵按下、彈起等狀態(tài)的判斷與處理。
- 郵箱負(fù)責(zé)處理定時(shí)器掃描任務(wù)與調(diào)用驅(qū)動(dòng)的任務(wù)之間的按鍵值數(shù)據(jù)通信。
驅(qū)動(dòng)初始化后,軟定時(shí)器會(huì)自動(dòng)掃描按鍵,然后結(jié)合狀態(tài)機(jī)判斷哪個(gè)按鍵按下或彈起,一旦檢查到按鍵按下,則將鍵值投遞到郵箱中。需要讀取鍵值的任務(wù)會(huì)在郵箱上等待鍵值。
實(shí)現(xiàn)如下:
appConfig.h:
// // Created by lishutong on 2018/2/5. //#ifndef PROJECT_APPCONFIG_H #define PROJECT_APPCONFIG_H#include "hal.h"#define EN_DEBUG_PRINT 1#if EN_DEBUG_PRINT == 0 #define DEBUG_PRINT(str, arg) #else #define DEBUG_PRINT(str, arg) xprintf(str, arg) #endif#endif //PROJECT_APPCONFIG_Hbutton.h:
/*** @brief 按鍵驅(qū)動(dòng)* @details* @author 01課堂 李述銅 http://01ketang.cc* @date 2017-06-01* @version 1.0* @copyright 版權(quán)所有,禁止用于商業(yè)用途*/ #ifndef BUTTON_H #define BUTTON_H#include "appConfig.h"#define BUTTON_MSG_NR 10 // 按鍵消息緩存數(shù)量 #define BUTTON_SCAN_INTERVAL 20 // 安鍵掃描的時(shí)間間隔, ms為單位typedef enum {ButtonNone,ButtonStartStop,Button1,Button2,Button3, }ButtonId;typedef enum {ButtonDown,ButtonUp, }ButtonState;void ButtonInit (void); uint32_t ButtonWaitPress (ButtonId * id);#endif //BUTTON_Hbutton.c:
/*** @brief 按鍵驅(qū)動(dòng)* @details* @author 01課堂 李述銅 http://01ketang.cc* @date 2017-06-01* @version 1.0* @copyright 版權(quán)所有,禁止用于商業(yè)用途*/ #include "tinyOS.h" #include "button.h" #include "stm32f10x.h"// 按鍵存儲(chǔ)消息緩沖區(qū) static tMbox buttonMbox; static void * buttonMsgBuffer[BUTTON_MSG_NR];static tTimer buttonTimer; // 按鍵掃描定時(shí)器/*** 初始化按鍵硬件*/ static void ButtonHalInit (void) {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(GPIOB, &GPIO_InitStructure); }/*** 獲取哪一個(gè)按鍵按下*/ static ButtonId ButtonGetWhichPress (void) {uint8_t state;ButtonId buttonId = ButtonNone;state = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1);if (state == Bit_SET) {buttonId = Button1;}state = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2);if (state == Bit_SET) {buttonId = Button2;}state = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_3);if (state == Bit_SET) {buttonId = Button3;}state = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0);if (state == Bit_SET) {buttonId = ButtonStartStop;}return buttonId; }/*** 按鍵定時(shí)器掃描處理, 按鍵處理譯碼* @param arg*/ static void timerFunc (void * arg) {static ButtonId prePressedButton = ButtonNone;static enum {NO_DOWN,RECHECK_DOWN,CONFIRMED_DOWN,RECHECK_UP} scanState = NO_DOWN;// 讀取當(dāng)前硬件狀態(tài)ButtonId currPressedButton = ButtonGetWhichPress();switch (scanState) {case NO_DOWN:// 有按鍵按下,進(jìn)入第1次確認(rèn)檢查狀態(tài)if (currPressedButton != ButtonNone) {scanState = RECHECK_DOWN;DEBUG_PRINT("NO_DOWN -> RECHECK_DOWN:%d\n", currPressedButton);}break;case RECHECK_DOWN:if (currPressedButton == ButtonNone) {scanState = NO_DOWN;DEBUG_PRINT("RECHECK_DOWN -> NO_DOWN:%d\n", currPressedButton);} else if (prePressedButton == currPressedButton) {// 發(fā)消息通知,根據(jù)按鍵類型,決定如何處理. 可以考慮從某個(gè)配置表中查,這樣可配置多個(gè)按鍵事件的通知行為uint32_t notifyOption = (currPressedButton == ButtonStartStop)? tMBOXSendFront : tMBOXSendNormal;tMboxNotify(&buttonMbox, (void *)currPressedButton, notifyOption);scanState = CONFIRMED_DOWN;DEBUG_PRINT("RECHECK_DOWN -> CONFIRMED_DOWN:%d\n", currPressedButton);}break;case CONFIRMED_DOWN:// 進(jìn)入這個(gè)狀態(tài)后,要做的是檢查按鍵是否重復(fù)按下或者等待按鍵釋.這里簡單起見,等待按鍵釋放if (currPressedButton == ButtonNone) {// 發(fā)現(xiàn)按鍵釋放?進(jìn)一步確認(rèn)。但這里要return,否則prePressedButton會(huì)在后面設(shè)置NonescanState = RECHECK_UP;DEBUG_PRINT("CONFIRMED_DOWN -> RECHECK_UP:%d\n", currPressedButton);return;} else if (currPressedButton != prePressedButton) {// 發(fā)現(xiàn)不一樣的按鍵按下,重新確認(rèn)scanState = RECHECK_DOWN;DEBUG_PRINT("CONFIRMED_DOWN -> RECHECK_DOWN:%d\n", currPressedButton);}break;case RECHECK_UP:if (currPressedButton == ButtonNone) {// 確認(rèn)沒有按鍵按下,已經(jīng)釋放scanState = NO_DOWN;DEBUG_PRINT("RECHECK_UP -> NO_DOWN:%d\n", currPressedButton);} else if (currPressedButton != prePressedButton) {// 發(fā)現(xiàn)不一樣的按鍵按下,重新確認(rèn)是否按下scanState = RECHECK_DOWN;DEBUG_PRINT("RECHECK_UP -> scanState:%d\n", currPressedButton);} else if (currPressedButton == prePressedButton) {// 相同的鍵,噢,前一次可能是誤觸發(fā),再次重新檢查scanState = CONFIRMED_DOWN;DEBUG_PRINT("RECHECK_UP -> CONFIRMED_DOWN:%d\n", currPressedButton);}break;}// 記錄當(dāng)前結(jié)果prePressedButton = currPressedButton; }/*** 按鍵初始化*/ void ButtonInit (void) {ButtonHalInit();tMboxInit(&buttonMbox, buttonMsgBuffer, BUTTON_MSG_NR);tTimerInit(&buttonTimer, 0, BUTTON_SCAN_INTERVAL / TINYOS_SYSTICK_MS, timerFunc, 0, TIMER_CONFIG_TYPE_SOFT);tTimerStart(&buttonTimer); }/*** 等待任意按鍵按下* @param id 按鍵是否按下的狀態(tài)* @return 非0,有錯(cuò)誤發(fā)生;0無錯(cuò)誤*/ uint32_t ButtonWaitPress (ButtonId * id) {uint32_t err = 0;err = tMboxWait(&buttonMbox, (void **)id, 0);return err; }app.c:
/*** @brief tOS應(yīng)用示例* @details* @author 01課堂 李述銅 http://01ketang.cc* @date 2017-06-01* @version 1.0* @copyright 版權(quán)所有,禁止用于商業(yè)用途*/ #include "tinyOS.h" #include "app.h" #include "hal.h"#include "button.h"static tTaskStack task1Env[TASK1_ENV_SIZE]; // 任務(wù)1的堆棧空間 static tTask task1;void task1Entry (void * param) {ButtonId buttonId;for (;;) {ButtonWaitPress(&buttonId);xprintf("Button press: %d\n", buttonId);} }/*** App的初始化*/ void tInitApp (void) {halInit();ButtonInit();tTaskInit(&task1, task1Entry, (void *) 0x0, TASK1_PRIO, task1Env, sizeof(task1Env)); }參考資料:
總結(jié)
- 上一篇: 实践案例说明
- 下一篇: bt3u盘怎么进入 bt3u盘如何使用