嵌入式(單片機)裸機 C 語言開發 + 按鍵掃描(模塊分層/非阻塞式/面向對象)

目錄

前言

設計需求

軟件功能需求

軟件設計需求

設計思路

模塊分層

非阻塞式

面向對象

設計方案

接口函數及變量定義

key_drv

key_cfg

key_core

key_user

按鍵動作識別

短按模式

長按模式


前言

本文目的是講述一個按鍵掃描處理的面向對象開發的設計思想,適用於裸機開發,通過按鍵掃描,檢測到按鍵是否按下,鬆開等狀態,並將該狀態通過其他形式反饋給其他模塊進行處理。初次使用按鍵時,最常用的辦法就是如以下代碼一樣,硬延時抖動濾波,等待確認後做相應的處理。

void KEY_Scan(void)
{	   
    if(KEY0 == 0)
    {
        delay_ms(10);//去抖動 

        if(KEY0 == 0)
        {
            // 處理想做的事情
        }
    }	
}

以上方式最大的弊端就是硬延時去抖動,極大的浪費了 CPU 資源,對熟悉阻塞式和非阻塞式程序開發的人來說,這種寫法是十分不可取的;程序框架稍微好的,會摒棄此種做法,採用分時調度(時間片論法)的形式按時(比如10ms)調用該函數,通過一定累計次數確定按鍵是否有動作,並處理相關數據。


設計需求

軟件功能需求

  1. 支持六種狀態:沒有按下、首次按下、短按持續按下、短按鬆開、長按持續按下和長按鬆開,其中後兩種可以使能或禁止。
  2. 支持獨立設置每個按鍵的模式和時間:短按模式和長按模式(區分短按和長按兩種狀態),且時間也可獨立設置。
  3. 支持大部分形式的按鍵輸入,只要滿足按鍵操作爲 0 和 1 兩種邏輯狀態,如以下幾種均支持,且兼容各種形式按鍵組合:
    1. 矩形按鍵:通過多組 I/O 識別到對應 key 按下;
    2. 三態開關:一個開關包含兩個 key1 和 key2,高電平爲 key1 按下,低電平爲 key2 按下;
    3. 模擬量開關:在一定範圍表示 key1 按下、key2 按下等;
    4. 數字組合開關:如 0x01 代表 key1 按下,0x02 代表 key2 按下,0x04 代表key3 按下(如解析紅外線遙控器的接收端 I/O 數據);
    5. ....等等只要最終表現滿足 0 和 1 兩種邏輯狀態均支持;
  4. 模塊和分層化,因此支持良好的移植性、維護性和可配置性等。
  5. 代碼量小,便於理解及維護。
  6. 其他想到再說。

軟件設計需求

  • 模塊分層:便於在不同系列 MCU 或開發平臺上移植,並提高維護性;
  • 非阻塞式:按鍵掃描任務中不允許內部有任何的延時函數進行去抖動;
  • 面向對象:將按鍵的掃描過程封裝起來,對外提供統一的接口,用來執行按鍵掃描任務和獲取按鍵的操作狀態等。

設計思路

模塊分層

大致將按鍵功能模塊分爲四類文件(一類文件含 .c 和 .h )

  • key_drv:底層驅動初始化、底層驅動信號輸入,移植時可根據原理圖等初始化硬件資源,並將硬件驅動輸入信號轉換爲 0 和 1 兩種邏輯狀態。
  • key_core:按鍵操作狀態識別的核心代碼,通過配置信息完成按鍵狀態的識別和相關 API 函數,移植時不需要修改。
  • key_cfg:按鍵相關配置,如按鍵模式、按鍵有效操作時間等,移植時根據需要對按鍵進行初始化配置。
  • key_user:按鍵自定義處理,通過調用 key_core 中的相關 API 函數完成按鍵初始化和掃描任務,並可增加其他按鍵形式,如編碼開關等最終表現形式不滿足 0 和 1 兩種邏輯關係的按鍵開關。

非阻塞式

由於按鍵不能通過軟件延時去抖動的方式,則需:

  • 計時器:按鍵掃描採樣時間的計時器,當檢測到按鍵首次按下時,則觸發計時器計數,當滿足去抖動時間時,則表示按鍵正常按下。

面向對象

將按鍵掃描過程封裝起來,則表示需要將過程和數據(操作狀態)分離:

  • 操作狀態:通過按鍵掃描,可識別沒有按下、首次按下、短按持續按下、短按鬆開、長按持續按下和長按鬆開六種簡單狀態。
  • 分離方式:通常做法是當檢測到按鍵某種狀態時,需要調用相關回調函數進行處理(若封裝則只能通過回調函數處理,否則涉及到修改按鍵模塊源代碼);我的做法是,當檢測到按鍵操作時,先記錄按鍵操作狀態,之後可通過調用讀取按鍵操作狀態接口函數返回記錄的按鍵操作狀態,這樣做法的好處在按鍵掃描任務執行時間不會由於回調函數處理的時間過長而變長。

設計方案

接口函數及變量定義

key_drv

  • KEY_Initialize():按鍵驅動的初始化,如相關的 GPIO 初始化;
  • KEY_InputHandle():對按鍵的輸入信號處理,完成對按鍵輸入信號轉變爲 0 或 1 的邏輯狀態關係;
  • KEY_GetInputState():獲取單個指定按鍵的邏輯狀態值;

key_cfg

  • 不提供相關函數 ,只提供配置信息結構體的定義和配置信息結構體變量,通過寫入初始值完成所有按鍵的配置。
  • 短按模式:支持沒有按下、首次按下、短按持續按下和短按鬆開四種按鍵狀態;
  • 長按模式:支持沒有按下、首次按下、短按持續按下、短按鬆開、長按持續按下和長按鬆開六種狀態;
  • 短按和長按模式可獨立設置按下有效時間。

key_core

  • KEY_ConfigInit():按鍵配置信息的初始化,如按鍵的計時器等運行過程中使用的變量初始化;
  • KEY_ConfigHandle():按鍵配置信息的處理,根據按鍵的邏輯狀態值和配置信息識別並記錄按鍵操作狀態;
  • KEY_ReadAction():讀取按鍵記錄的動作狀態,通過調用該函數,依次返回記錄的操作狀態(從按下到鬆開的過程狀態 );
  • KEY_ResetAction():復位按鍵的動作狀態,清除記錄的動作狀態;
  • 其他函數:可修改按鍵的相關配置信息,如按鍵模式,按鍵有效時間等;

key_user

  • KEY_UserInit():調用底層驅動初始化和配置信息初始化,完成按鍵功能模塊的初始化;
  • KEY_ScanTask():調用按鍵的輸入信號處理和按鍵配置信息的處理,完成按鍵功能模塊的按鍵動作狀態識別;
  • 其他函數:由於以上只適用於 0 和 1 兩種邏輯狀態的按鍵開關表現形式且通過查詢非中斷的檢測方式,則可添加不滿足 0 和 1 兩種邏輯狀態的按鍵開關,如編碼器,需要通過中斷觸發的形式完成,此時就不適用與 key_core  文件中的處理,因此在 key_user 文件下可添加該編碼器開關的功能,通過按鍵功能模塊統一管理所有按鍵。

按鍵動作識別

短按模式

在禁止長按模式下的按鍵從按下到鬆開一系列動作識別過程如下圖(短按有效時間中包含了去抖動時間):

長按模式

在使能長按模式下的按鍵動作識別過程有兩種情況:

1、按下時間沒有滿足長按時間鬆開,和禁止長按模式的識別過程一致,如下圖(短按有效時間中包含了去抖動時間):

2、按下時間滿足了長按時間鬆開,識別過程如下圖(短按有效時間中包含了去抖動時間):

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章