按键驱动设计
文章目錄
- 1 按鍵驅動設計
1 按鍵驅動設計
硬件結構如下:
按鍵驅動綜合使用了軟定時器 + 狀態機 + 郵箱數據通信。
- 軟定時器負責周期性地調用按鍵檢測掃描函數。
- 狀態機負責處理按鍵按下、彈起等狀態的判斷與處理。
- 郵箱負責處理定時器掃描任務與調用驅動的任務之間的按鍵值數據通信。
驅動初始化后,軟定時器會自動掃描按鍵,然后結合狀態機判斷哪個按鍵按下或彈起,一旦檢查到按鍵按下,則將鍵值投遞到郵箱中。需要讀取鍵值的任務會在郵箱上等待鍵值。
實現如下:
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 按鍵驅動* @details* @author 01課堂 李述銅 http://01ketang.cc* @date 2017-06-01* @version 1.0* @copyright 版權所有,禁止用于商業用途*/ #ifndef BUTTON_H #define BUTTON_H#include "appConfig.h"#define BUTTON_MSG_NR 10 // 按鍵消息緩存數量 #define BUTTON_SCAN_INTERVAL 20 // 安鍵掃描的時間間隔, 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 按鍵驅動* @details* @author 01課堂 李述銅 http://01ketang.cc* @date 2017-06-01* @version 1.0* @copyright 版權所有,禁止用于商業用途*/ #include "tinyOS.h" #include "button.h" #include "stm32f10x.h"// 按鍵存儲消息緩沖區 static tMbox buttonMbox; static void * buttonMsgBuffer[BUTTON_MSG_NR];static tTimer buttonTimer; // 按鍵掃描定時器/*** 初始化按鍵硬件*/ 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); }/*** 獲取哪一個按鍵按下*/ 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; }/*** 按鍵定時器掃描處理, 按鍵處理譯碼* @param arg*/ static void timerFunc (void * arg) {static ButtonId prePressedButton = ButtonNone;static enum {NO_DOWN,RECHECK_DOWN,CONFIRMED_DOWN,RECHECK_UP} scanState = NO_DOWN;// 讀取當前硬件狀態ButtonId currPressedButton = ButtonGetWhichPress();switch (scanState) {case NO_DOWN:// 有按鍵按下,進入第1次確認檢查狀態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) {// 發消息通知,根據按鍵類型,決定如何處理. 可以考慮從某個配置表中查,這樣可配置多個按鍵事件的通知行為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:// 進入這個狀態后,要做的是檢查按鍵是否重復按下或者等待按鍵釋.這里簡單起見,等待按鍵釋放if (currPressedButton == ButtonNone) {// 發現按鍵釋放?進一步確認。但這里要return,否則prePressedButton會在后面設置NonescanState = RECHECK_UP;DEBUG_PRINT("CONFIRMED_DOWN -> RECHECK_UP:%d\n", currPressedButton);return;} else if (currPressedButton != prePressedButton) {// 發現不一樣的按鍵按下,重新確認scanState = RECHECK_DOWN;DEBUG_PRINT("CONFIRMED_DOWN -> RECHECK_DOWN:%d\n", currPressedButton);}break;case RECHECK_UP:if (currPressedButton == ButtonNone) {// 確認沒有按鍵按下,已經釋放scanState = NO_DOWN;DEBUG_PRINT("RECHECK_UP -> NO_DOWN:%d\n", currPressedButton);} else if (currPressedButton != prePressedButton) {// 發現不一樣的按鍵按下,重新確認是否按下scanState = RECHECK_DOWN;DEBUG_PRINT("RECHECK_UP -> scanState:%d\n", currPressedButton);} else if (currPressedButton == prePressedButton) {// 相同的鍵,噢,前一次可能是誤觸發,再次重新檢查scanState = CONFIRMED_DOWN;DEBUG_PRINT("RECHECK_UP -> CONFIRMED_DOWN:%d\n", currPressedButton);}break;}// 記錄當前結果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 按鍵是否按下的狀態* @return 非0,有錯誤發生;0無錯誤*/ uint32_t ButtonWaitPress (ButtonId * id) {uint32_t err = 0;err = tMboxWait(&buttonMbox, (void **)id, 0);return err; }app.c:
/*** @brief tOS應用示例* @details* @author 01課堂 李述銅 http://01ketang.cc* @date 2017-06-01* @version 1.0* @copyright 版權所有,禁止用于商業用途*/ #include "tinyOS.h" #include "app.h" #include "hal.h"#include "button.h"static tTaskStack task1Env[TASK1_ENV_SIZE]; // 任務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)); }參考資料:
總結