STM32F103按鍵操作的另一種實現——狀態機
#ifndef _KEY_H_ #define _KEY_H_ #include "HAL_gpio.h" // 換成STM32F103對應的GPIO庫 #include "type.h" // type.h主要是一些型別的重新命名 #define KEY_UP_GRP GPIOA #define KEY_UP_IDX GPIO_Pin_9 #define KEY_UP_IS_DOWN() GPIO_ReadInputDataBit(KEY_UP_GRP, KEY_UP_IDX) #define KEY_UP_CONFIG() GPIOConfig(KEY_UP_GRP, KEY_UP_IDX, GPIO_Mode_IPU) // 這個函式我在之前帖子裡面寫過 #define KEY_DOWN_GRP GPIOA #define KEY_DOWN_IDX GPIO_Pin_10 #define KEY_DOWN_IS_DOWN() GPIO_ReadInputDataBit(KEY_DOWN_GRP, KEY_DOWN_IDX) #define KEY_DOWN_CONFIG() GPIOConfig(KEY_DOWN_GRP, KEY_DOWN_IDX, GPIO_Mode_IPU) #define KEY_FUNC_GRP GPIOA #define KEY_FUNC_IDX GPIO_Pin_11 #define KEY_FUNC_IS_DOWN() GPIO_ReadInputDataBit(KEY_FUNC_GRP, KEY_FUNC_IDX) #define KEY_FUNC_CONFIG() GPIOConfig(KEY_FUNC_GRP, KEY_FUNC_IDX, GPIO_Mode_IPU) #define KEY_TURN_GRP GPIOA #define KEY_TURN_IDX GPIO_Pin_12 | GPIO_Pin_13 #define KEY_TURN_IS_DOWN() GPIO_ReadInputDataBit(KEY_TURN_GRP, KEY_TURN_IDX) #define KEY_TURN_CONFIG() GPIOConfig(KEY_TURN_GRP, KEY_TURN_IDX, GPIO_Mode_IPU) //==================================================================================== typedef enum { CONFIRM_KEY = 1, FUNC_KEY, UP_KEY, DOWN_KEY } key_event_t; #define state_keyUp 0 //初始狀態,未按鍵 #define state_keyDown 1 //鍵被按下 #define state_keyLong 2 //長按 #define state_keyTime 3 //按鍵計時態 #define return_keyUp 0x00 //初始狀態 #define return_keyPressed 0x01 //鍵被按過,普通按鍵 #define return_keyLong 0x02 //長按 #define return_keyAuto 0x04 //自動連發 #define key_down 0 //按下 #define key_up 0xf0 //未按時的key有效位鍵值 #define key_longTimes 100 //10ms一次,200次即2秒,定義長按的判定時間 #define key_autoTimes 20 //連發時間定義,20*10=200,200毫秒發一次 #define KEYS1_VALUE 0xe0 //keyS1 按下 #define KEYS2_VALUE 0xd0 //keyS2 按下 #define KEYS3_VALUE 0xb0 //keyS3 按下 #define KEYS4_5_VALUE 0x70 //keyS4_5 按下 //==================================================================================== void KeyProcess(void); //T0定時器呼叫的工作函式 void KeyTimerInit(void); #endif /* _KEY_H_ */
#include <stdio.h> #include "key.h" #include "timer.h" // STM32F103定時器的配置 static uint8_t key_get(void) //獲取P3口值 { if(KEY_UP_IS_DOWN() == key_down) { return KEYS1_VALUE; } if(KEY_DOWN_IS_DOWN() == key_down) { return KEYS2_VALUE; } if(KEY_FUNC_IS_DOWN() == key_down) { return KEYS3_VALUE; } if(KEY_TURN_IS_DOWN() == key_down) { return KEYS4_5_VALUE; } return key_up ; //0xf0 沒有任何按鍵 } //函式每20ms被呼叫一次,而我們彈性按鍵過程時一般都20ms以上 //所以每次按鍵至少呼叫本函式2次 static uint8_t key_read(uint8_t* pKeyValue) { static uint8_t s_u8keyState = 0; //未按,普通短按,長按,連發等狀態 static uint16_t s_u16keyTimeCounts = 0; //在計時狀態的計數器 static uint8_t s_u8LastKey = key_up ; //儲存按鍵釋放時的P3口資料 uint8_t keyTemp = 0; //鍵對應io口的電平 int8_t key_return = 0; //函式返回值 keyTemp = key_up & key_get(); //提取所有的key對應的io口 switch(s_u8keyState) //這裡檢測到的是先前的狀態 { case state_keyUp: //如果先前是初始態,即無動作 { if(key_up != keyTemp) //如果鍵被按下 { s_u8keyState = state_keyDown; //更新鍵的狀態,普通被按下 } } break; case state_keyDown: //如果先前是被按著的 { if(key_up != keyTemp) //如果現在還被按著 { s_u8keyState = state_keyTime; //轉換到計時態 s_u16keyTimeCounts = 0; s_u8LastKey = keyTemp; //儲存鍵值 } else { s_u8keyState = state_keyUp; //鍵沒被按著,回初始態,說明是干擾 } } break; case state_keyTime: //如果先前已經轉換到計時態(值為3) { //如果真的是手動按鍵,必然進入本程式碼塊,並且會多次進入 if(key_up == keyTemp) //如果未按鍵 { s_u8keyState = state_keyUp; key_return = return_keyPressed; //返回1,一次完整的普通按鍵 //程式進入這個語句塊,說明已經有2次以上10ms的中斷,等於已經消抖 //那麼此時檢測到按鍵被釋放,說明是一次普通短按 } else //在計時態,檢測到鍵還被按著 { if(++s_u16keyTimeCounts > key_longTimes) //時間達到2秒 { s_u8keyState = state_keyLong; //進入長按狀態 s_u16keyTimeCounts = 0; //計數器清空,便於進入連發重新計數 key_return = return_keyLong; //返回state_keyLong } //程式碼中,在2秒內如果我們一直按著key的話,返回值只會是0,不會識別為短按或長按的 } } break; case state_keyLong: //在長按狀態檢測連發 ,每0.2秒發一次 { if(key_up == keyTemp) { s_u8keyState = state_keyUp; } else //按鍵時間超過2秒時 { if(++s_u16keyTimeCounts > key_autoTimes)//10*20=200ms { s_u16keyTimeCounts = 0; key_return = return_keyAuto; //每0.2秒返回值的第2位置位(1<<2) }//連發的時候,肯定也伴隨著長按 } key_return |= return_keyLong; //0x02是肯定的,0x04|0x02是可能的 } break; default: break; } *pKeyValue = s_u8LastKey ; //返回鍵值 return key_return; }
主要是描述一下按鍵狀態機的思維,使用定時器中斷的方法,按鍵按下將其加入佇列中,在主函式的迴圈中實現出隊。親測可用。// 這個函式就是要在中斷中呼叫的。主要是使用事件佇列的方式。 void KeyProcess(void) { uint8_t key_stateValue; uint8_t keyValue = 0; uint8_t* pKeyValue = &keyValue; key_stateValue = key_read(pKeyValue); if ((return_keyPressed == key_stateValue) && (*pKeyValue == KEYS1_VALUE)) { //短按keyS1時改變對時狀態,將其加入佇列,佇列的基本操作在將佇列的時候寫過。 if(QueueEventIsEmpty(g_state_manager.process->key_event) || (!QueueEventIsEmpty(g_state_manager.process->key_event) && !QUEUE_EVENT_IS_EQUEL(g_state_manager.process->key_event, UP_KEY))) { QueueEventPush(g_state_manager.process->key_event, UP_KEY); } } } //====================================================================================== void KeyTimerInit(void) { KEY_UP_CONFIG(); KEY_DOWN_CONFIG(); KEY_FUNC_CONFIG(); KEY_TURN_CONFIG(); // 20ms TimerConfig(KEY_TIMER, KEY_TIMER_DIV, KEY_TIMER_PERIOD); TimerDisable(KEY_TIMER); TimerEnable(KEY_TIMER); }
相關推薦
STM32F103按鍵操作的另一種實現——狀態機
#ifndef _KEY_H_ #define _KEY_H_ #include "HAL_gpio.h" // 換成STM32F103對應的GPIO庫 #include "type.h" // type.h主要是一些型別的重新命名 #define KEY_U
Java模版方法的另一種實現
pan strategy 全部 相關 必須 rod () 抽象方法 rate 面試荔枝FM杯具,遂死磕AQS途中發現一個有趣的模版用法,記下來。 模版方法是很重要的設計模式,在數據訪問層、眾多的插件接口都可見其影子,一般的實現都是在模版中定義抽象方法並使用其方法進行
吸頂效果的另一種實現
前面介紹過一篇文章,是使用ItemDecoration來實現吸頂效果,使用起來很解耦,簡單,方便,但是優缺點是拓展性比較差,今天就通過另一種方式來實現吸頂效果,並且吸頂欄可以高度制定佈局和互動,步入正題,下面來實現它,先看看效果圖: 一、實現原理 頭部的內容位於R
101889I (LCA的另一種實現)
題意: Q次詢問,每次詢問必須包含特定邊的最小生成樹。 思路: 考慮 最淳樸的最小生成樹,如果加了一條特定邊,肯定是構成了一個環,那麼環外的邊肯定是不變的,要不然,根本就不可能選外面的那些邊了, 所以我們現在就是求這個環上的最小 生成樹,肯定是找樹上之前的 兩個點之
利用IAT匯出OpenGL函式:OpenGL Loader的另一種實現辦法
利用這種辦法可以用50KB的DLL匯出OpenGL 3.3版本所有Core Profile函式, DLL比GLEW小很多, 根據glcorearb.h自動生成的程式碼與glLoadGen生成的程式碼差不多, 這部分程式碼包含的函式都是空實現,結合__declspec(dllexport,
載入一個類時,其內部類是否同時被載入?引申出單例模式的另一種實現方式...
載入一個類時,其內部類是否同時被載入?下面我們做一個實驗來看一下。public class Outer { static { System.out.println("load outer class..."); } //靜態內部類 sta
載入一個類時,其內部類是否同時被載入?引申出單例模式的另一種實現方式
載入一個類時,其內部類是否同時被載入?下面我們做一個實驗來看一下。 Java程式碼 1. public class Outer { 2. static { 3. System.out.println("load o
分頁的另一種實現-不用額外請求
情景:千里碼有些最優化題目的旁邊會有一個排行榜,用來展示不同的答案。比如[Uber打車匹配](http://www.qlcoder.com/task/7596) 這裡的答題人數並不多,但是[老王
java中NIO程式設計另一種實現超實用
除了普通的Socket與ServerSocket實現的阻塞式通訊外,Java提供了非阻塞式通訊的NIO API。先看一下NIO的實現原理。 從圖中可以看出,伺服器上所有Channel(包括ServerSocketChannel和SocketCha
另一種實現非阻塞網路通訊的方法———使用libev
背景:最近終於開始了我的實習生之路,本來在進公司之前還比較緊張,儘管拿到了offer,因為畢竟這是一個新的起點,一開始從學生到員工這個身份的轉變讓我有些不太適應,但是還好在公司裡遇到了人超級好的軟體經理Alex以及其他精明能幹的小夥伴們,所以這個過渡時間也很快。 一開始Al
嘗試新思路——CError的另一種實現方式
程式碼如下: #ifndef __MYERROR_H__ #define __MYERROR_H__ #include "Error.h" #include <map> #include
Sticky Header的另一種實現方法
使用Sticky Header的list單個item一般情況下使用的資料結構是 {data:"what's inside", category:"section name"} 這樣儲存其實是浪費了很多的空間,因為category的名字被儲存的多次。在移動環境
縮放到選中的另一種實現
Dim pDoc As IMxDocumentSet pDoc = ThisDocumentDim pMap As IMapSet pMap = pDoc.FocusMapDim pLayer As IFeatureLayerDim pFSel As IFeatureSele
代理的另一種實現方式
代理相信大家都很熟悉了。不過還是說下吧。 舉個例子: // // A.h // Created by XX // @protocol SomeDelegate <NSObject> - (void)someMethod:(UIVi
頭部底部固定的另一種實現方式
需求:點選按鈕 頁面左側或右側滑出一個小視窗,頭部,底部固定,中間內容區域可以滑動滾動條檢視資料,這種視窗 我一般的做法是視窗fixed定位,頭部底部也fixed定位或者絕對定位都可以實現,今天看到一種新的實現方式,自己研究了一下,寫了一個demo記錄一下,也不知這樣寫好不
JPA複合主鍵另一種實現--聯合約束
前言 關於複合主鍵一般是三種方式,但必須建立複合主鍵類,然後通過註解的方式完成,這三種方式網上很容易找到,這裡主要記錄自己使用時的一些坑和專案中的特殊需求。 結合JPA使用時,關於Repository類中第二個引數不再是Long(主鍵id型別),而是複合主
另一種實現“飢漢”與“懶漢”
一般實現飢漢用的是靜態關鍵字。在類載入時,提前載入靜態區的內容。而懶漢則是實現單例模式。靜態內容,在呼叫時才載入。 關於類載入,還可以用class.getInstance(); 和 類.class; 此兩者區別就是靜態載入的時間機。看下程式碼: public class
Linux避免多次sudo時重複輸入密碼的另一種實現
之所以稱為”另一種實現”是因為在網路上搜索類似的標題時,幾乎所有的建議都是去修改/etc/sudoers這個檔案,加上NOPASSWORD這個引數來搞定。 在我們的系統中,因為某種安全原因,只允許使用者在某些情況下輸入密碼來獲取root許可權,但不能總有超級使用者的許可權
利用Guzzle實現另一種PHP異步發送郵件(laravel5.4)
dot 博文 接下來 lar 時間 重點 5.4 targe 占用 前言:第二種實現方法 方法的思路: 此方法的實現需要借助Guzzle這個PHP的HTTP客戶端,用來輕而易舉地發送請求,並集成到我們的WEB服務上(laravel中如何引入guzzle不多說) 使用該方
字符設備驅動另一種寫法—mmap方法操作LED
一個 控制寄存器 abs 提交數據 函數參數 功能 控制 讀取 調用方法 最近在看韋老師的視頻,講解了很多種字符設備的驅動寫法。經過自己的研究之後,我發現還有另外一種寫法,直接在應用層操作,省去了內核中的地址映射部分,使得用戶可以在應用層直接操作LED。 mm