色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

按鍵操作的驅動設計與實現

CHANBAEK ? 來源:木南創智 ? 作者:尹家軍 ? 2022-12-08 10:57 ? 次閱讀

按鍵在我們的項目中是經常使用到的組件。一般來說,我們都是在用到按鍵時直接針對編碼,但這樣每次都做很多重復性的工作。所以在這里我們考慮做一般性抽象得到一個可應用于按鍵操作的通用性驅動程序。

1、功能概述

按鍵操作在我們的產品種經常用到,一般都是在特定的應用環境中直接有針對性的操作。但這些按鍵的操作往往有很多的共性,這就為代碼復用提供了可能。

1.1、按鍵的定義

在開始考慮按鍵操作之前,我們先來分析一下究竟什么是按鍵。按鍵一般來講就是用于信號輸入的按鈕,通過響應它的操作我們可以實現想要的功能。但我們這里所說的按鍵不僅包括普通的單體按鍵,還包括如組合鍵、鍵盤等。

對于這些種類的按鍵它們的形態、功能或許有較大的差異,但我們可以對它們所進行的操作卻很類似。這也是我們能夠統一考慮它們的基礎。

1.2、原理分析

我們已經給我們要操作的按鍵劃分了范圍,在此基礎上我們簡單分析實現按鍵操作的基本原理。

首先我們來考慮按鈕操作的原理,其實很簡單,無非就是按下或者彈起兩種狀態。至于按鈕本身是常開或者常閉,是低電平有效還是高電平有效都沒有問題,我們只要能檢測出其狀態就可以了。我們考慮按鍵的按下、彈起、連擊和長按等狀態,如下圖:

Dingtalk_20221206154648.jpg

其次我們來考慮按鍵狀態的存儲。在系統中的多個按鍵需要操作時,如何處理響應事件就會是一個問題。我們考慮以先入先出隊列來存儲按鍵的狀態,進而根據狀態進行操作。我們需要設計一個隊列,這是一個先入先出的隊列,擁有一定的存儲空間和讀寫操作指針,具體如下圖所示:

Dingtalk_20221206154648.jpg

在上圖中,當讀指針與寫指針一樣時,則表示隊列為空。當寫入一個數據,則寫指針加一;當讀出一個數據,則讀指針加一;當讀指針遇到寫指針則表示在沒有數據了。

最后來說一說按鍵狀態的響應。所謂響應其實就是對不同的狀態我們來處理不同的事件。對于每個按鍵我們根據其狀態定義事件。在不同的事件中處理我們需要的功能。

Dingtalk_20221206154648.jpg

在上圖中,狀態和時間都可以在我們的對象中聲明,但具體的實現形式在應用中完成。

2、驅動設計與實現

我們已經簡單分析了按鍵的基本操作原理,接下來我們將以此為基礎來分析并設計按鍵操作的通用驅動方法。

2.1、對象定義

我們依然采用基于對象的操作方式。當然前提是我們得到了可用于操作的對象,所以我們先來分析一下如何抽象面向按鍵操作的對象。

2.1.1、定義對象類型

一般來講,一個對象會包括屬性和操作。接下來我們就從這兩個方面來考慮按鍵對象問題。

首先我們來考慮按鍵對象的屬性問題。我們的系統中總有多個按鍵,為了區分這些按鍵我們為每一個按鍵分配一個ID,用于區別這些按鍵。所以我們將按鍵ID作為其一個屬性。對于按鍵操作我們一般都會有軟件濾波來實現消抖,我們一如一個濾波計數用以實現這一過程,我們將濾波計數也當作它的一個屬性。長按鍵我們需要預設檢測時長,同時需要一個計數來記錄這一過程,所以我們將其設為屬性。同樣連續按鍵的周期需要預設,而且需要計數來記錄過程,所以也將這兩個作為屬性。當然按鍵當前的狀態,我們也可能需要記錄一下,按鍵按下時的有效電平,我們也需要分辨,這些我們也都將其作為屬性。綜上所述按鍵對象的類型定義如下:

/*定義按鍵對象類型*/
    typedef struct KeyObject {
     uint8_t id;//按鍵的ID
     uint8_t Count;//濾波器計數器
     uint16_t LongCount;//長按計數器
     uint16_t LongTime;  //按鍵按下持續時間, 0 表示不檢測長按
     uint8_t  State;//按鍵當前狀態(按下還是彈起)
     uint8_t  RepeatPeriod;//連續按鍵周期
     uint8_t  RepeatCount;//連續按鍵計數器
     uint8_t ActiveLevel;//激活電平
    }KeyObjectType;

除了按鍵對象,其實我們還需要定義一個數據隊列的對象。者如我們前面所說,隊列除了一個數據存儲區外還需要讀寫指針。我們定義如下:

/*定義鍵值存儲隊列的類型*/
    typedef struct KeyStateQueue{
      uint8_t queue[KEY_FIFO_SIZE];//鍵值存儲隊列
     uint8_t pRead;//讀隊列指針
     uint8_t pWrite;//寫隊列指針
    }KeyStateQueueType;

2.1.2、對象初始化配置

對象定義之后并不能立即使用我們還需要對其進行初始化。所以這里我們來考慮按鍵對象的初始化函數。關于對象的初始化,初始化函數需要處理幾個方面的問題。一是檢查輸入參數是否合理;二是為對象的屬性賦初值;三是對對象作必要的初始化配置。據此思路我們設計按鍵對象的初始化函數如下:

/*按鍵讀取初始化*/
    void KeysInitialization(KeyObjectType *pKey,uint8_t id,uint16_t longTime, uint8_t repeatPeriod,KeyActiveLevelType level)
    {
     if(pKey==NULL)
     {
     return;
     }
     
     pKey->id=id;
     pKey->Count=0;
     pKey->LongCount=0;
     pKey->RepeatCount=0;
     pKey->State=0;
     
     pKey->ActiveLevel=level;
     
     pKey->LongTime=longTime;
     pKey->RepeatPeriod=repeatPeriod;
    }

2.2、對象操作

我們已經抽象了按鍵對象類型,也設計了對象的初始化函數。接下來我們需要考慮使用對象如何實現操作。根據我們前面的分析,操作可分為量個部分:按鍵狀態的檢測和鍵值隊列的操作。

2.2.1、按鍵狀態檢測

需要周期性的檢測按鍵的狀態以便我們響應按鍵的操作。我們一般10ms檢測一次狀態,并持續一定的濾波周期用于消抖。我們檢測到按鍵的不同狀態后將狀態存入到相關的鍵值隊列中。

/*按鍵周期掃描程序*/
    void KeyValueDetect(KeyObjectType *pKey)
    {
     
     if (CheckKeyDown(pKey))
     {
     if (pKey->Count < KEY_FILTER_TIME)
     {
     pKey->Count = KEY_FILTER_TIME;
     }
     else if(pKey->Count < 2 * KEY_FILTER_TIME)
     {
     pKey->Count++;
     }
     else
     {
     if (pKey->State == 0)
     {
     pKey->State = 1;
     
     /*發送按鍵按下事件消息*/
     KeyValueEnQueue((uint8_t)((pKey->id<<2) + KeyDown));
     }
     
     if (pKey->LongTime > 0)
     {
     if (pKey->LongCount < pKey->LongTime)
     {
     /* 發送按建持續按下的事件消息 */
     if (++pKey->LongCount == pKey->LongTime)
     {
     /* 鍵值放入按鍵FIFO */
     KeyValueEnQueue((uint8_t)((pKey->id<<2) + KeyLong));
     }
     }
     else
     {
     if (pKey->RepeatPeriod > 0)
     {
     if (++pKey->RepeatCount >= pKey->RepeatPeriod)
     {
     pKey->RepeatCount = 0;
     /*長按鍵后,每隔10ms發送1個按鍵*/
     KeyValueEnQueue((uint8_t)((pKey->id<<2) + KeyDown));
     }
     }
     }
     }
     }
     }
     else
     {
     if(pKey->Count > KEY_FILTER_TIME)
     {
     pKey->Count = KEY_FILTER_TIME;
     }
     else if(pKey->Count != 0)
     {
     pKey->Count--;
     }
     else
     {
     if (pKey->State == 1)
     {
     pKey->State = 0;
     
     /*發送按鍵彈起事件消息*/
     KeyValueEnQueue((uint8_t)((pKey->id<<2)+ KeyUP));
     }
     }
     
     pKey->LongCount = 0;
     pKey->RepeatCount = 0;
     }
    }

2.2.2、鍵值隊列的操作

鍵值隊列的操作就簡單了,主要包括數據的寫入、讀出、清空隊列以及隊列是否為空。需要說的是鍵值的存儲,包括量方面類容:按鍵的ID和按鍵的狀態。我們使用一個字節來存儲這些信息,前六個位存儲ID,后兩位存儲狀態。具體如下圖所示:

Dingtalk_20221206154648.jpg

這樣一種存儲格式,我們最多可以存儲64個按鍵和4種狀態,當然這還要看隊列的大小。

/*鍵值出隊列程序*/
    uint8_t KeyValueDeQueue(void)
    {
     uint8_t result; 
     
     if(keyState.pRead==keyState.pWrite)
     {
     result=0;
     }
     else
     {
     result=keyState.queue[keyState.pRead];
     
     if(++keyState.pRead>=KEY_FIFO_SIZE)
     {
     keyState.pRead=0;
     }
     }
     return result;
    }
     
    /*鍵值入隊列程序*/
    void KeyValueEnQueue(uint8_t keyCode)
    {
     keyState.queue[keyState.pWrite]=keyCode;
     
     if(++keyState.pWrite >= KEY_FIFO_SIZE)
     {
     keyState.pWrite=0;
     }
    }

3、驅動的使用

我們已經設計了按鍵操作的驅動程序,還需要對這一設計進行驗證。這一節我們將以前面的設計為基礎,用一個簡單的應用來驗證。我們設計4個單體按鍵,并由它們生出兩組組合鍵,所以我們的應用程序就是面向這6個按鍵對象進行操作。

3.1、聲明并初始化對象

在開始面向一個對象的操作之前,我們需要得到這個對象的一個實例。那么我們要先聲明對象。我們前面已經定義了按鍵對象類型KeyObjectType和存儲鍵值的隊列類型KeyStateQueueType。我們使用這兩個類型先聲明兩個對象變量如下:

KeyObjectType keys[6];

KeyStateQueueType keyState;

聲明了對象還需要對變量進行初始化。在驅動的設計中我們已經設計了初始化函數,對象變量的初始化操作就通過這一函數來實現。初始化函數需要一些輸入參數:

KeyObjectType *pKey,按鍵對象

uint8_t id,按鍵ID

uint16_t longTime,長按有效時間

uint8_t repeatPeriod,連按間隔周期

KeyActiveLevelType level,按鍵按下有效電平

在這些參數中pKey為按鍵對象,是我們要初始化的對象。而其它參數只需要根據實際設置輸入就可以了。說一初始化函數可調用為:

/*按鍵硬件初始化配置*/
    static void Key_Init_Configuration(void)
    {
      KeyIDType id;
      for(id=KEY1;idid++)
      {
     KeysInitialization(&keys[id],id,KEY_LONG_TIME,0,KeyHighLevel);
      }
    }

關于按鍵ID,我們使用枚舉來定義。與我們前面定義的按鍵對象數組配合能夠起到很好的效果。在這一我們定義按鍵ID為:

/*定義按鍵枚舉*/
    typedef enum KeyID {
     KEY1,
     KEY2,
     KEY3,
     KEY4,
    KEY1KEY2,
     KEY3KEY4,
     KEYNUM
    }KeyIDType;

按鍵ID作為作為按鍵的唯一標識,不但在我們的按鍵狀態記錄中要使用到,同時也可作為我們按鍵對象數組的下標來使用。

3.2、基于對象進行操作

我們定義了對象,接下來就可以基于對象實現我們的應用。對于按鍵操作我們需要考慮2個方面的事情:一是周期型的檢查按鍵狀態并壓如隊列;二是讀取隊列中的按鍵狀態觸發不同的操作。

首先我們來說一說周期型的檢查按鍵的狀態。我們采用10ms的周期來檢查按鍵,所以我們需要使用定時中端的方式來實現,將如下函數加入到10ms定時中端即可。

/*按鍵掃描程序*/
    void KeyScanHandle(void)
    {
      KeyIDType id;
      for(id=KEY1;idid++)
      {
         KeyValueDetect(&keys[id]);
      }
    }

其實還有一個回調函數需要實現,其原型如下:

/*檢查某個ID的按鍵(包括組合鍵)是否按下*/
    __weak uint8_t CheckKeyDown(KeyObjectType *pKey)

根據我們定義的按鍵對象和ID枚舉我們實現這個回調函數并不困難,我們實現其如下:

/*檢查某個ID的按鍵(包括組合鍵)是否按下*/
    uint8_t CheckKeyDown(KeyObjectType *pKey)
    {
     /* 實體單鍵 */
     if (pKey->id < KEY1KEY2)
     {
     uint8_t i;
     uint8_t count = 0;
     uint8_t save = 255;
     
     /* 判斷有幾個鍵按下 */
      for (i = 0; i < KEY1KEY2; i++)
     {
     if (KeyPinActive(pKey)) 
     {
     count++;
     save = i;
     }
     }
     
     if (count == 1 && save == pKey->id)
     {
     return 1;/* 只有1個鍵按下時才有效 */
     }
     return 0;
     }
     
     /* 組合鍵 K1K2 */
     if (pKey->id == KEY1KEY2)
     {
     if (KeyPinActive(&keys[KEY1]) && KeyPinActive(&keys[KEY2]))
     {
     return 1;
     }
     else
     {
     return 0;
     }
     }
     
      /* 組合鍵 K3K4 */
     if (pKey->id == KEY3KEY4)
     {
     if (KeyPinActive(&keys[KEY3]) && KeyPinActive(&keys[KEY4]))
     {
     return 1;
     }
     else
     {
     return 0;
     }
     }
     return 0;
    }

此外,我們還需要讀取按鍵的狀態并進行相應的響應。我們實現一個簡單的處理函數如下:

/*按鍵處理函數*/
    static void KeyProcessing(void)
    {
      uint8_t keyCode;
     keyCode=KeyValueDeQueue();
    
     if(keyCode==((keys[KEY1].id<<2)+KeyDown))
     {
     //key1按下時觸發的事件
     }
     else if(keyCode==((keys[KEY1].id<<2)+KeyUP))
     {
     //key1彈起時觸發的事件
     }
    }

4、應用總結

我們已經實現了按鍵對象的操作,并在次基礎上實現了簡單的驗證。操作的結果符合我們的期望。而且擴展性也很強。

按照我們對信息存儲方式和消息隊列的設計,最多可以存儲64個按鍵和4中狀態,當然這需要看定義的隊列的大小。隊列不應太小,太小有可能會造成某些按鍵操不會響應;也不應太大,太大可能會造成操作遲緩和空間浪費。

在應用中,我們建議定義按鍵ID時最好使用枚舉,使用枚舉的好處有幾點。一是不會出現重復,每個按鍵能保證有唯一的ID值。二是便于與按鍵對象數組組合操作,簡化編碼。三是使用枚舉擴展很方便,代碼改動比較小。當然,枚舉值最好是連續的而且從0開始。

在使用驅動是還需要注意,檢測按鍵操作是只對個體單鍵的硬件有效,如果可能也使用數組操作,能與ID枚舉配合使用簡化操作。對于組合鍵要檢測多個物理硬件,但也是對這些但體檢的檢測,所以在硬件上不需要定義。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 按鍵
    +關注

    關注

    4

    文章

    223

    瀏覽量

    57619
  • 對象
    +關注

    關注

    1

    文章

    38

    瀏覽量

    17405
  • 驅動設計
    +關注

    關注

    1

    文章

    111

    瀏覽量

    15292
收藏 人收藏

    評論

    相關推薦

    Linux下如何使用中斷的方式來驅動按鍵

    Linux下的按鍵輸入驅動開發模板一文中介紹了基本的按鍵輸入捕獲流程,這里將進一步介紹如何使用中斷的方式來驅動按鍵,同時通過定時器
    發表于 07-29 08:59 ?983次閱讀

    linux系統中裸機按鍵中斷的驅動?方法

    今天主要和大家聊一聊,如何實現按鍵中斷的驅動方法。
    發表于 12-09 11:59 ?686次閱讀

    EmbeddedButton嵌入式按鍵驅動設計實現

    EmbeddedButton是一個輕量級簡單易用的嵌入式按鍵驅動模塊,可無限拓展按鍵,支持多連擊、長按、短按長按等多種按鍵事件;該模塊通過異步回調方式來簡化程序結構,根據幾個簡單原則完
    的頭像 發表于 08-28 15:47 ?1253次閱讀
    EmbeddedButton嵌入式<b class='flag-5'>按鍵</b><b class='flag-5'>驅動</b>設計<b class='flag-5'>實現</b>

    開發第1個LVGL程序與實現按鍵操作

    開發第1個LVGL程序與實現按鍵操作
    的頭像 發表于 01-07 13:49 ?310次閱讀
    開發第1個LVGL程序與<b class='flag-5'>實現</b><b class='flag-5'>按鍵</b><b class='flag-5'>操作</b>

    如何使用軟件Proteus和Keil uVision4實現多個按鍵操作

    如何使用軟件Proteus和Keil uVision4實現多個按鍵操作
    發表于 10-20 07:22

    如何使用STM32擴展板實現按鍵驅動

    樹莓派和STM32之間如何連線?如何使用STM32擴展板實現按鍵驅動
    發表于 01-17 07:46

    使用單片機C語言實現獨立按鍵檢測與矩陣鍵盤操作的資料和程序

    所有的電子產品幾乎到涉及到按鍵操作。所以微控制器是如何識別一個按鍵是否被按下,按下后又該如何做出反應,又如何防止按鍵抖動呢?更深入一點,微控制器又是如何識別矩陣鍵盤的?本文將詳細闡述如
    發表于 07-16 17:39 ?2次下載
    使用單片機C語言<b class='flag-5'>實現</b>獨立<b class='flag-5'>按鍵</b>檢測與矩陣鍵盤<b class='flag-5'>操作</b>的資料和程序

    基于鴻蒙OS的按鍵驅動

    按鍵作為常用的輸入系統,如何準確并高效的獲取按鍵值,是一個經常要面對的問題,今天我們看看在鴻蒙系統中,如何得到獨立按鍵按鍵值。 實現目標
    發表于 11-11 10:03 ?726次閱讀

    使用單片機實現2按鍵加減操作的C語言實例免費下載

    本文檔的主要內容詳細介紹的是使用單片機實現2按鍵加減操作的C語言實例免費下載。
    發表于 11-18 17:44 ?18次下載

    嵌入式LinuxQT操作自定義按鍵

    嵌入式Linux系統中,用QT做的應用層程序,需要檢測自定義的按鍵狀態。使用的QT的按鍵事件,驅動層使用的Linux的input子系統。環境如下:硬件:Imx6ullQT版本:5.5在QT中使用
    發表于 10-20 19:21 ?9次下載
    嵌入式LinuxQT<b class='flag-5'>操作</b>自定義<b class='flag-5'>按鍵</b>

    MCU之按鍵驅動 -剝離按鍵驅動和事件處理

    ButtonDrive 自己寫的一個按鍵驅動,支持單雙擊、連按、長按;采用回調處理按鍵事件(自定義消抖時間),使用只需3步,創建按鍵按鍵
    發表于 10-28 19:21 ?18次下載
    MCU之<b class='flag-5'>按鍵</b><b class='flag-5'>驅動</b> -剝離<b class='flag-5'>按鍵</b><b class='flag-5'>驅動</b>和事件處理

    Nand Flash驅動(實現初始化以及讀操作)

    Nand Flash驅動(實現初始化以及讀操作)
    發表于 12-02 12:36 ?11次下載
    Nand Flash<b class='flag-5'>驅動</b>(<b class='flag-5'>實現</b>初始化以及讀<b class='flag-5'>操作</b>)

    按鍵驅動的實驗

    按鍵驅動實驗與LED以及Beep在整體使用邏輯上一樣,只是按鍵是輸入模式。
    的頭像 發表于 03-02 16:25 ?762次閱讀
    <b class='flag-5'>按鍵</b><b class='flag-5'>驅動</b>的實驗

    基于狀態機的按鍵驅動設計

    按鍵作為單片機的輸入設備,可以向單片機輸入數據、傳輸命令等,是設置參數和控制設備的常用接口。所以,學會按鍵驅動也是初學者必不可少的能力。說到按鍵驅動
    的頭像 發表于 07-04 11:43 ?1458次閱讀
    基于狀態機的<b class='flag-5'>按鍵</b><b class='flag-5'>驅動</b>設計

    如何在FPGA中實現按鍵消抖

    按鍵操作。因此,實現有效的按鍵消抖機制對于提高系統的穩定性和可靠性至關重要。以下是在FPGA中實現按鍵
    的頭像 發表于 08-19 18:15 ?2067次閱讀
    主站蜘蛛池模板: 精品欧美一区二区三区四区 | 精品久久久麻豆国产精品| 亚洲国产精品一区二区三区在线观看 | 美女夫妻内射潮视频| a视频免费看| 色怕怕| 九九精品国产亚洲A片无码 | 插骚妇好爽好骚| 午夜看片福利在线观看| 精品亚洲AV无码蜜芽麻豆| 99久久精品国产亚洲AV| 婷婷六月激情综合一区| 极品内射少妇精品无码视频| 2018久久视频在线视频观看| 日韩爽爽影院在线播放| 精品免费在线视频| 99久久就热视频精品草| 婷婷精品国产亚洲AV在线观看| 精品免费久久久久久影院| aaaaaaa一级毛片| 亚洲AV色香蕉一区二区三区| 麻豆精品传媒2021网站入口| 高h乱np甄宓| 一区二区三区四区国产| 日本工口生肉全彩大全| 久久99热只有频精品| 电影果冻传媒在线播放| 伊人青青久久| 视频一区亚洲视频无码| 久久伊人电影| 国产精品无码AV天天爽人妻蜜桃| 综合网伊人| 亚洲 自拍 欧洲 视频二区| 欧美性xxx免费看片| 九九99国产香蕉视频| 成人毛片一区二区三区| 中文字幕日本在线mv视频精品| 性888xxxx入欧美| 日本熟妇多毛XXXXX视频| 快播金瓶梅| 激情A片久久久久久久|