純C語言寫的按鍵驅動,將按鍵邏輯與按鍵處理事件分離~
- 2019 年 10 月 16 日
- 筆記
button drive
傑傑自己寫的一個按鍵驅動,支援單雙擊、連按、長按;採用回調處理按鍵事件(自定義消抖時間),使用只需3步,創建按鍵,按鍵事件與回調處理函數鏈接映射,周期檢查按鍵。
源碼地址:https://github.com/jiejieTop/ButtonDrive。作者:傑傑
前言
前幾天寫了個按鍵驅動,參考了MulitButton的數據結構的用法,邏輯實現並不一樣。
在這裡感謝所有的開源開發者,讓我從中學到了很多,同時網路也是一個好平台,也希望所有的開發者能形成良性循環,從網路中學知識,回饋到網路中去。感謝MulitButton的作者0x1abin,感謝兩位rtt的大佬:大法師、流光。
Button_drive簡介
Button_drive是一個小巧的按鍵驅動,支援單擊、雙擊、長按、連續觸發等(後續可以在按鍵控制塊中添加觸發事件),理論上可無限量擴展Button,Button_drive採用按鍵觸發事件回調方式處理業務邏輯,支援在RTOS中使用,我目前僅在RT-Thread上測試過。
寫按鍵驅動的目的是想要將用戶按鍵邏輯與按鍵處理事件分離,用戶無需處理複雜麻煩的邏輯事件。
Button_drive使用效果
- 單擊與長按
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2nhPnwqF-1571148176402)(https://github.com/jiejieTop/ButtonDrive/blob/master/png/1.png?raw=true)]
- 雙擊
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-wtGvjTjq-1571148176403)(https://github.com/jiejieTop/ButtonDrive/blob/master/png/2.png?raw=true)]
- 連按
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QUypOh2s-1571148176403)(https://github.com/jiejieTop/ButtonDrive/blob/master/png/3.png?raw=true)]
- 連按釋放
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-LfsbuM1W-1571148176404)(https://github.com/jiejieTop/ButtonDrive/blob/master/png/4.png?raw=true)]
使用方法
- 創建按鍵句柄
Button_t Button1; Button_t Button2;
- 創建按鍵,初始化按鍵資訊,包括按鍵名字、按鍵電平檢測函數介面、按鍵觸發電平。
Button_Create("Button1", //按鍵名字 &Button1, //按鍵句柄 Read_Button1_Level, //按鍵電平檢測函數介面 BTN_TRIGGER); //觸發電平 ......
- 按鍵觸發事件與事件回調函數鏈接映射,當按鍵事件被觸發的時候,自動跳轉回調函數中處理業務邏輯。
Button_Attach(&Button1,BUTTON_DOWM,Btn2_Dowm_CallBack); //按鍵單擊 Button_Attach(&Button1,BUTTON_DOUBLE,Btn2_Double_CallBack); //雙擊 Button_Attach(&Button1,BUTTON_LONG,Btn2_Long_CallBack); //長按 .......
- 周期調用回調按鍵處理函數即可,建議調用周期20-50ms。
Button_Process(); //需要周期調用按鍵處理函數
需要用戶實現的 2 個函數:
- 按鍵電平檢測介面:
uint8_t Read_Button1_Level(void) { return GPIO_ReadInputDataBit(BTN1_GPIO_PORT,BTN1_GPIO_PIN); } uint8_t Read_Button2_Level(void) { return GPIO_ReadInputDataBit(BTN2_GPIO_PORT,BTN2_GPIO_PIN); } // 這是我在stm32上簡單測試的偽程式碼,以實際源碼為準
- 按鍵邏輯處理
void Btn1_Dowm_CallBack(void *btn) { PRINT_INFO("Button1 單擊!"); } void Btn1_Double_CallBack(void *btn) { PRINT_INFO("Button1 雙擊!"); } void Btn1_Long_CallBack(void *btn) { PRINT_INFO("Button1 長按!"); Button_Delete(&Button2); PRINT_INFO("刪除Button1"); Search_Button(); }
特點
Button_drive開放源碼,按鍵控制塊採用數據結構方式,按鍵事件採用枚舉類型,確保不會重複,也便於添加用戶需要邏輯,採用宏定義方式定義消抖時間、連按觸發時間、雙擊時間間隔、長按時間等,便於修改。
同時所有被創建的按鍵採用單鏈表方式連擊,用戶只管創建,無需理會按鍵處理,只需調用Button_Process()
即可,在函數中會自動遍歷所有被創建的按鍵。
支援按鍵刪除操作,用戶無需在程式碼中刪除對應的按鍵創建於映射鏈接程式碼,也無需刪除關於按鍵的任何回調事件處理函數,只需調用Button_Delete()
函數即可,這樣子,就不會處理關於被刪除按鍵的任何狀態。當然目前按鍵記憶體不會釋放,如果使用os的話,建議釋放按鍵記憶體。
按鍵控制塊
/* 每個按鍵對應1個全局的結構體變數。 其成員變數是實現消抖和多種按鍵狀態所必須的 */ typedef struct button { /* 下面是一個函數指針,指向判斷按鍵手否按下的函數 */ uint8_t (*Read_Button_Level)(void); /* 讀取按鍵電平函數,需要用戶實現 */ char Name[BTN_NAME_MAX]; uint8_t Button_State : 4; /* 按鍵當前狀態(按下還是彈起) */ uint8_t Button_Last_State : 4; /* 上一次的按鍵狀態,用於判斷雙擊 */ uint8_t Button_Trigger_Level : 2; /* 按鍵觸發電平 */ uint8_t Button_Last_Level : 2; /* 按鍵當前電平 */ uint8_t Button_Trigger_Event; /* 按鍵觸發事件,單擊,雙擊,長按等 */ Button_CallBack CallBack_Function[number_of_event]; uint8_t Button_Cycle; /* 連續按鍵周期 */ uint8_t Timer_Count; /* 計時 */ uint8_t Debounce_Time; /* 消抖時間 */ uint8_t Long_Time; /* 按鍵按下持續時間 */ struct button *Next; }Button_t;
觸發事件
typedef enum { BUTTON_DOWM = 0, BUTTON_UP, BUTTON_DOUBLE, BUTTON_LONG, BUTTON_CONTINUOS, BUTTON_CONTINUOS_FREE, BUTTON_ALL_RIGGER, number_of_event, /* 觸發回調的事件 */ NONE_TRIGGER }Button_Event;
宏定義選擇
#define BTN_NAME_MAX 32 //名字最大為32位元組 /* 按鍵消抖時間40ms, 建議調用周期為20ms 只有連續檢測到40ms狀態不變才認為有效,包括彈起和按下兩種事件 */ #define CONTINUOS_TRIGGER 0 //是否支援連續觸發,連發的話就不要檢測單雙擊與長按了 /* 是否支援單擊&雙擊同時存在觸發,如果選擇開啟宏定義的話,單雙擊都回調,只不過單擊會延遲響應, 因為必須判斷單擊之後是否觸發了雙擊否則,延遲時間是雙擊間隔時間 BUTTON_DOUBLE_TIME。 而如果不開啟這個宏定義,建議工程中只存在單擊/雙擊中的一個,否則,在雙擊響應的時候會觸發一次單擊, 因為雙擊必須是有一次按下並且釋放之後才產生的 */ #define SINGLE_AND_DOUBLE_TRIGGER 1 /* 是否支援長按釋放才觸發,如果打開這個宏定義,那麼長按釋放之後才觸發單次長按, 否則在長按指定時間就一直觸髮長按,觸發周期由 BUTTON_LONG_CYCLE 決定 */ #define LONG_FREE_TRIGGER 0 #define BUTTON_DEBOUNCE_TIME 2 //消抖時間 (n-1)*調用周期 #define BUTTON_CONTINUOS_CYCLE 1 //連按觸發周期時間 (n-1)*調用周期 #define BUTTON_LONG_CYCLE 1 //長按觸發周期時間 (n-1)*調用周期 #define BUTTON_DOUBLE_TIME 15 //雙擊間隔時間 (n-1)*調用周期 建議在200-600ms #define BUTTON_LONG_TIME 50 /* 持續n秒((n-1)*調用周期 ms),認為長按事件 */ #define TRIGGER_CB(event) if(btn->CallBack_Function[event]) btn->CallBack_Function[event]((Button_t*)btn)
例子
Button_Create("Button1", &Button1, Read_KEY1_Level, KEY_ON); Button_Attach(&Button1,BUTTON_DOWM,Btn1_Dowm_CallBack); //單擊 Button_Attach(&Button1,BUTTON_DOUBLE,Btn1_Double_CallBack); //雙擊 Button_Attach(&Button1,BUTTON_CONTINUOS,Btn1_Continuos_CallBack); //連按 Button_Attach(&Button1,BUTTON_CONTINUOS_FREE,Btn1_ContinuosFree_CallBack); //連按釋放 Button_Attach(&Button1,BUTTON_LONG,Btn1_Long_CallBack); //長按 Button_Create("Button2", &Button2, Read_KEY2_Level, KEY_ON); Button_Attach(&Button2,BUTTON_DOWM,Btn2_Dowm_CallBack); //單擊 Button_Attach(&Button2,BUTTON_DOUBLE,Btn2_Double_CallBack); //雙擊 Button_Attach(&Button2,BUTTON_CONTINUOS,Btn2_Continuos_CallBack); //連按 Button_Attach(&Button2,BUTTON_CONTINUOS_FREE,Btn2_ContinuosFree_CallBack); //連按釋放 Button_Attach(&Button2,BUTTON_LONG,Btn2_Long_CallBack); //長按 Get_Button_Event(&Button1); Get_Button_Event(&Button2);
後續
流光大佬的要求,讓我玩一玩RTT的rtkpgs,打算用Button_drive練一練手吧。
ButtonDrive在env使用
目前我已將按鍵驅動做成軟體包(packages),如果使用RT-Thread作業系統的話,可以在env中直接配置使用!
步驟如下:
- 選擇在線軟體包
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gG59d3Yn-1571148176404)(https://github.com/jiejieTop/ButtonDrive/blob/master/png/5.png?raw=true)]
- 選擇軟體包屬性為外設相關
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-amGYFH8P-1571148176404)(https://github.com/jiejieTop/ButtonDrive/blob/master/png/6.png?raw=true)]
- 選擇button_drive
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-O0a0wqdi-1571148176404)(https://github.com/jiejieTop/ButtonDrive/blob/master/png/7.png?raw=true)]
- 進入驅動的選項配置(自帶默認屬性)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-O5sGP0CE-1571148176405)(https://github.com/jiejieTop/ButtonDrive/blob/master/png/8.png?raw=true)]
- 如果不懂按鍵的配置是什麼意思,按下「shift+?」,即可有解釋
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-EYxByBVm-1571148176407)(https://github.com/jiejieTop/ButtonDrive/blob/master/png/9.png?raw=true)]
- 編譯生成mdk/iar工程
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-e0pdTFhz-1571148176407)(https://github.com/jiejieTop/ButtonDrive/blob/master/png/10.png?raw=true)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-lXNX8VeJ-1571148176407)(https://github.com/jiejieTop/ButtonDrive/blob/master/png/11.png?raw=true)]
關於rtkpgs
簡介 (English)
buildpkg 是用於生成 RT-Thread package 的快速構建工具。
一個優秀的 package 應該是這樣的:
- 程式碼優雅, 規範化。
- examples 常式,提供通俗易懂的使用常式。
- SConscript 文件,用於和 RT-Thread 環境一起進行編譯。
- README.md 文檔,向用戶提供必要的功能說明。
- docs 文件夾, 放置除了 README 之外的其他細節文檔。
- license 許可文件,版權說明。
為了方便快速的生成 RT-Thread package 規範化模板 以及 減輕開源倉庫遷移 RT-Thread 的前期準備工作的負擔,基於此目的的 buildpkg 應運而生,為開發 Rt-Thread 的 package 的開發者提供輔助開發工具。
序號 | 支援功能 | 描述 |
---|---|---|
1 | 構建 package 模板 | 創建指定名稱 package , 自動添加 readme /版本號/ github ci腳本/demo/開源協議文件 |
2 | 遷移開源倉庫 | 從指定 git 倉庫構建 package , 自動添加readme/版本號/ github ci腳本/demo/開源協議文件, 但是遷移的倉庫需要用戶自己按照實際情況修改 |
3 | 更新 package | 生成package後可以再次更新之前設定的版本號,開源協議或者scons腳本等 |
使用說明
1. 構建package
buildpkg.exe make pkgdemo
2. 遷移開源倉庫
buildpkg.exe make cstring https://github.com/liu2guang/cstring.git
3. 更新package
buildpkg.exe update pkgname
4. 可選配置
長參數 | 短參數 | 描述 |
---|---|---|
–version=v1.0.0 | -v v1.0.0 | 設置 package 的版本 |
–license=MIT | -l MIT | 設置 package 所遵循的版權協議 |
–submodule | -s | 刪除 git 子模組 |
Windows10 及 Linux 平台的演示動圖
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-CT6Hxh28-1571148176408)(/figures/buildpkg.gif)]
測試平台
序號 | 測試平台 | 測試結果 |
---|---|---|
1 | win10 | exe測試通過, py測試通過 |
2 | win7 | exe待測試, py待測試 |
3 | mac | py腳本不知道是否兼容, 沒有測試條件, 後面維護下 |
4 | linux | py腳本不知道是否兼容, 沒有測試條件, 後面維護下 |
聯繫人
- 郵箱:[email protected]
- 主頁:liu2guang
- 倉庫:Github, Gitee
喜歡就關注我吧!
相關程式碼可以在公眾號後台獲取。
更多資料歡迎關注「物聯網IoT開發」公眾號!