純C語言寫的按鍵驅動,將按鍵邏輯與按鍵處理事件分離~

来源:https://www.cnblogs.com/iot-dev/archive/2019/10/16/11688807.html
-Advertisement-
Play Games

button drive 傑傑自己寫的一個按鍵驅動,支持單雙擊、連按、長按;採用回調處理按鍵事件(自定義消抖時間),使用只需3步,創建按鍵,按鍵事件與回調處理函數鏈接映射,周期檢查按鍵。 源碼地址: "https://github.com/jiejieTop/ButtonDrive" 。作者: "傑 ...


ButtonDrive

自己寫的一個按鍵驅動,支持單雙擊、連按、長按;採用回調處理按鍵事件(自定義消抖時間),使用只需3步,創建按鍵,按鍵事件與回調處理函數鏈接映射,周期檢查按鍵。
源碼地址:https://github.com/jiejieTop/ButtonDrive

前言

前幾天寫了個按鍵驅動,參考了MulitButton的數據結構的用法,邏輯實現並不一樣。
在這裡感謝所有的開源開發者,讓我從中學到了很多,同時網路也是一個好平臺,也希望所有的開發者能形成良性迴圈,從網路中學知識,回饋到網路中去。感謝MulitButton的作者0x1abin,感謝兩位rtt的大佬:大法師流光

Button_drive簡介

Button_drive是一個小巧的按鍵驅動,支持單擊、雙擊、長按、連續觸發等(後續可以在按鍵控制塊中添加觸發事件),理論上可無限量擴展Button,Button_drive採用按鍵觸發事件回調方式處理業務邏輯,支持在RTOS中使用,我目前僅在RT-Thread上測試過。
寫按鍵驅動的目的是想要將用戶按鍵邏輯與按鍵處理事件分離,用戶無需處理複雜麻煩的邏輯事件。

Button_drive使用效果

  1. 單擊與長按

單擊與長按

  1. 雙擊

雙擊

  1. 連按

連按

  1. 連按釋放

連按釋放

使用方法

  1. 創建按鍵句柄
Button_t Button1;
Button_t Button2; 
  1. 創建按鍵,初始化按鍵信息,包括按鍵名字、按鍵電平檢測函數介面、按鍵觸發電平。
  Button_Create("Button1",              //按鍵名字
                &Button1,               //按鍵句柄
                Read_Button1_Level,     //按鍵電平檢測函數介面
                BTN_TRIGGER);           //觸發電平
                
                ......
  1. 按鍵觸發事件與事件回調函數鏈接映射,當按鍵事件被觸發的時候,自動跳轉回調函數中處理業務邏輯。
  Button_Attach(&Button1,BUTTON_DOWM,Btn2_Dowm_CallBack);       //按鍵單擊
  Button_Attach(&Button1,BUTTON_DOUBLE,Btn2_Double_CallBack);   //雙擊
  Button_Attach(&Button1,BUTTON_LONG,Btn2_Long_CallBack);       //長按
                
                .......
  1. 周期調用回調按鍵處理函數即可,建議調用周期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中直接配置使用!

步驟如下:

  1. 選擇線上軟體包

  1. 選擇軟體包屬性為外設相關

  1. 選擇button_drive

  1. 進入驅動的選項配置(自帶預設屬性)

  1. 如果不懂按鍵的配置是什麼意思,按下“shift+?”,即可有解釋

  1. 編譯生成mdk/iar工程

關於rtkpgs

簡介 (English)

buildpkg 是用於生成 RT-Thread package 的快速構建工具。

一個優秀的 package 應該是這樣的:

  1. 代碼優雅, 規範化。
  2. examples 常式,提供通俗易懂的使用常式。
  3. SConscript 文件,用於和 RT-Thread 環境一起進行編譯。
  4. README.md 文檔,向用戶提供必要的功能說明。
  5. docs 文件夾, 放置除了 README 之外的其他細節文檔。
  6. 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 平臺的演示動圖

buildpkg

測試平臺

序號 測試平臺 測試結果
1 win10 exe測試通過, py測試通過
2 win7 exe待測試, py待測試
3 mac py腳本不知道是否相容, 沒有測試條件, 後面維護下
4 linux py腳本不知道是否相容, 沒有測試條件, 後面維護下

聯繫人


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 信號量 信號量( )在操作系統中是一種實現系統中任務與任務、任務與中斷間同步或者臨界資源互斥保護的機制。在多任務系統中,各任務之間常需要同步或互斥,信號量就可以為用戶提供這方面的支持。 抽象來說,信號量是一個非負整數,每當信號量被獲取( )時,該整數會減一,當該整數的值為 時,表示信號量處於無效狀態 ...
  • 消息隊列 在前一篇文章中 "【TencentOS tiny學習】源碼分析(3)——隊列" 我們描述了TencentOS tiny的隊列實現,同時也點出了TencentOS tiny的隊列是依賴於消息隊列的,那麼我們今天來看看消息隊列的實現。 其實消息隊列是TencentOS tiny的一個 基礎組件 ...
  • 隊列基本概念 隊列是一種常用於任務間通信的數據結構,隊列可以在 傳遞消息,實現了任務接收來自其他任務或中斷的不固定長度的消息,任務能夠從隊列裡面讀取消息,當隊列中的消息是空時,讀取消息的任務將被阻塞,用戶還可以指定任務等待消息的時間 ,在這段時間中,如果隊列為空,該任務將 狀態以等待隊列數據有效。當 ...
  • 溫馨提示:本文不描述與浮點相關的寄存器的內容,如需瞭解自行查閱(畢竟我自己也不懂) 調度器的基本概念 中提供的任務調度器是基於優先順序的全搶占式調度,在系統運行過程中,當有比當前任務優先順序更高的任務就緒時,當前任務將立刻被 ,高優先順序任務 處理器運行。 內核中也允許創建相同優先順序的任務。相同優先順序的任 ...
  • 任務的基本概念 從系統的角度看,任務是競爭系統資源的最小運行單元。TencentOS tiny是一個支持多任務的操作系統,任務可以使用或等待CPU、使用記憶體空間等系統資源,並獨立於其它任務運行,理論上任何數量的任務都可以共用同一個優先順序,這樣子處於就緒態的多個相同優先順序任務將會以時間片切換的方式共用 ...
  • 移植前的準備工作 1. 獲取STM32的裸機工程模板 STM32的裸機工程模板直接使用野火STM32開發板配套的固件庫常式即可。可以從我 上獲取 "https://github.com/jiejieTop/TencentOS Demo" 下載TencentOS tiny 源碼 TencentOS t ...
  • 新聞 2019年9月18日,騰訊宣佈將 `自主研發 輕量級`物聯網實時操作系統 TencentOS tiny 。相比市場上其它系統,騰訊TencentOS tiny在資源占用、設備成本、功耗管理以及安全穩定等層面極具競爭力。該系統的開源可大幅降低物聯網應用開發成本,提升開發效率,同時支持一鍵上雲,對 ...
  • 今天,需要再本地使用git管理代碼,但是當代碼創建好的時候,想發佈到github上面的私有倉庫中,但是沒有提前創建遠端倉庫,所以需要把本地git倉庫推送到遠端另外一個倉庫了,下麵進行簡要記錄,剛剛經過的過程,方便之後再次使用的時候,不會重覆勞動: git 設置 git config user.nam ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...