找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 6704|回復(fù): 2
打印 上一主題 下一主題
收起左側(cè)

STM32單片機(jī)按鍵驅(qū)動(dòng)設(shè)計(jì)-多功能模式(單擊、長按、雙擊、三擊等)

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:394949 發(fā)表于 2021-11-19 17:26 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
本帖最后由 田裕中 于 2021-11-20 14:32 編輯

按鍵的多功能觸發(fā)
概述
       本文所介紹的按鍵是基于STM32F系列獨(dú)立按鍵模式,目的是使單個(gè)按鍵具備多種按鍵效果。常見的有單擊、長按等功能。為拓展功能多樣性,對(duì)按鍵驅(qū)動(dòng)進(jìn)行設(shè)計(jì),實(shí)現(xiàn)按鍵功能的多樣性,本設(shè)計(jì)已達(dá)到的功能包括單擊、長按、雙擊、三擊、四擊......等,剩余可由用戶自行添加。按鍵消息通過消息隊(duì)列進(jìn)行傳遞。

按鍵對(duì)象封裝
       類似于面向?qū)ο笾蓄惖姆庋b,該結(jié)構(gòu)體包含按鍵觸發(fā)函數(shù)、硬件接口、計(jì)數(shù)器及按鍵狀態(tài)標(biāo)記。
typedef void (*KEYFUNC) (uint16_t tick_cnt_ptr);
該定義為:聲明一個(gè)函數(shù)指針,指向一個(gè)參數(shù)為uint16_t 類型的變量的函數(shù)。
typedef struct
{
    KEYFUNC key_function;
    KEYFUNC fast_key_fun;
    GPIO_TypeDef* GPIOx;
    uint16_t Pin;
    uint16_t key_cnt;
    uint16_t  Status;
} KEYSTATUS;
按鍵事件注冊
       注冊按鍵的觸發(fā)函數(shù)、初始狀態(tài)State、按鍵初始化等。聲明兩個(gè)枚舉變量進(jìn)行按鍵的開啟與關(guān)閉(DISABLE_KEY、GENERAL_KEY)。
enum
{
    DISABLE_KEY,
    GENERAL_KEY
};申明一個(gè)map對(duì)象,為每個(gè)按鍵引用驅(qū)動(dòng)類:
KEYSTATUS   io_key_map[IOKEY_NUM];在此函數(shù)開啟之前確保按鍵引腳已成功初始化。
void ap_peripheral_key_register(INT8U type)
{
    io_key_num = 0;
    switch(type)
    {
        case DISABLE_KEY:
        {
            //disable all key
        }
            break;
        case GENERAL_KEY:
        {
            #if IO_KEY
            io_key_map[io_key_num].GPIOx = KEY_Pin_GPIO_Port;
            io_key_map[io_key_num].Pin = KEY_Pin_Pin;
            io_key_map[io_key_num].key_function = ap_peripheral_io_key_exe;
            //io_key_map[io_key_num].fast_key_fun = ap_peripheral_io_fast_key_exe;
            io_key_map[io_key_num].key_cnt = 0;
            io_key_map[io_key_num++].Status = 0;
            #endif
            #if CHARGE
            io_key_map[io_key_num].GPIOx = STAT_CHG_GPIO_Port;
            io_key_map[io_key_num].Pin = STAT_CHG_Pin;
            io_key_map[io_key_num].key_function = ap_peripheral_charge_exe;
            io_key_map[io_key_num].key_cnt = 0;
            io_key_map[io_key_num++].Status = 0;
            #endif
            #if IO_USB
            io_key_map[io_key_num].GPIOx = USB_CH_GPIO_Port;
            io_key_map[io_key_num].Pin = USB_CH_Pin;
            io_key_map[io_key_num].key_function = ap_peripheral_io_usb_exe;
            io_key_map[io_key_num].key_cnt = 0;
            io_key_map[io_key_num++].Status = 0;
            #endif
        }
            break;
        default:
            ap_peripheral_key_register(DISABLE_KEY);
            break;
    }
}

按鍵回調(diào)函數(shù)
       按鍵事件注冊后,每個(gè)按鍵引腳功能編寫對(duì)應(yīng)的回調(diào)函數(shù),為事件注冊使用。說明:按鍵消息是基于消息隊(duì)列進(jìn)行消息傳遞,可根據(jù)用戶的需求自行設(shè)計(jì)回調(diào)函數(shù)功能!
第一個(gè)按鍵回調(diào)函數(shù):
void ap_peripheral_io_key_exe(INT16U tick_cnt_ptr)
{
    if(tick_cnt_ptr >= LONG_KEY_TIME)
    {
        msgQSend(APQ, MSG_APQ_LONG_KEY,MSG_PRI_NORMAL);
    }
    else if(tick_cnt_ptr == TWO_KEY_VALUE)
    {
        msgQSend(APQ, MSG_APQ_TWO_KEY,MSG_PRI_NORMAL);
    }
    else if(tick_cnt_ptr == THREE_KEY_VALUE)
    {
        msgQSend(APQ, MSG_APQ_THREE_KEY,MSG_PRI_NORMAL);
    }
    else if(tick_cnt_ptr == FOUR_KEY_VALUE)
    {
        msgQSend(APQ, MSG_APQ_FOUR_KEY,MSG_PRI_NORMAL);
    }
    else if(tick_cnt_ptr == FIVE_KEY_VALUE)
    {
        msgQSend(APQ, MSG_APQ_FIVE_KEY,MSG_PRI_NORMAL);
    }
    else
    {
        msgQSend(APQ, MSG_APQ_CLICK_KEY,MSG_PRI_NORMAL);
    }
}
其他按鍵的回調(diào)函數(shù)與其相似,可自行設(shè)計(jì)......

按鍵掃描
       按鍵掃描通過定時(shí)器中斷進(jìn)行掃描,優(yōu)點(diǎn)是有穩(wěn)定的掃描時(shí)間,可計(jì)算按鍵的持續(xù)時(shí)間及消抖,本設(shè)計(jì)是20ms掃描一次。此函數(shù)中,由按鍵事件注冊函數(shù)提供一個(gè)io_key_num為其提供掃描范圍:
  1. void ap_peripheral_key_judge(void)
  2. {
  3. #if IOKEY_NUM
  4.     INT8U i,status;
  5.     static INT8U short_key_pressed = 0,key_cnt = 0;
  6.     static INT32U last_time = 0,loosen_time = 0;
  7.     INT32U current_time;
  8.     for(i = 0; i < io_key_num; i++)
  9.     {
  10.         status = HAL_GPIO_ReadPin(io_key_map[i].GPIOx,io_key_map[i].Pin);
  11.         #if IO_KEY
  12.         if(io_key_map[i].Pin == KEY_Pin_Pin)
  13.         {   
  14.             if(status == KEY_VALUE)//按下
  15.             {
  16.                 if(!io_key_map[i].Status)
  17.                 {   
  18.                     io_key_map[i].key_cnt += 1;
  19.                     if(io_key_map[i].key_cnt >= LONG_KEY_TIME)//長按
  20.                     {
  21.                         io_key_map[i].Status = 1;
  22.                         io_key_map[i].key_function(io_key_map[i].key_cnt);//長按回調(diào)
  23.                     }
  24.                 }
  25.                 else
  26.                 {
  27.                     io_key_map[i].key_cnt = 0;
  28.                 }
  29.                 if(io_key_map[i].key_cnt == 65535)
  30.                 {
  31.                     io_key_map[i].key_cnt = 16;
  32.                 }
  33.             }
  34.             else//松開
  35.             {
  36.             if(io_key_map[i].key_cnt > 2)
  37.             {
  38.                 current_time = HAL_GetTick();
  39.                 if(key_cnt==0)
  40.                 {
  41.                     last_time = current_time;//記錄第一次松開的時(shí)間
  42.                 }
  43.                 if(current_time - last_time <= INTERVAL_TIME)
  44.                 {
  45.                     key_cnt++;
  46.                     last_time = current_time;//記錄最后一次松開的時(shí)間
  47.                 }
  48.             }
  49.             if(key_cnt&&(HAL_GetTick()-last_time>INTERVAL_TIME))
  50.             {               
  51.                 io_key_map[i].key_function(key_cnt);               
  52.                 key_cnt = 0;
  53.             }
  54.             io_key_map[i].key_cnt = 0;
  55.             io_key_map[i].Status = 0;
  56.             }
  57.         }
  58.         #endif
  59.         #if CHARGE
  60.         if(io_key_map[i].Pin == STAT_CHG_Pin)
  61.         {
  62.             if(status == CHARGE_VALUE)
  63.             {
  64.                 if(!io_key_map[i].Status)
  65.                 {
  66.                     io_key_map[i].key_cnt++;
  67.                     if(io_key_map[i].key_cnt>200)
  68.                     {
  69.                         io_key_map[i].Status = 1;
  70.                         io_key_map[i].key_function(1);
  71.                     }
  72.                 }
  73.                 else
  74.                 {
  75.                     io_key_map[i].key_cnt = 0;
  76.                 }
  77.                 if(io_key_map[i].key_cnt == 65535)
  78.                 {
  79.                     io_key_map[i].key_cnt = 8;
  80.                 }
  81.             }
  82.             else
  83.             {
  84.                 if(io_key_map[i].Status)
  85.                 {
  86.                     io_key_map[i].key_function(0);
  87.                     io_key_map[i].Status = 0;
  88.                     io_key_map[i].key_cnt = 0;
  89.                 }
  90.             }
  91.         }
  92.         #endif
  93.         #if IO_USB
  94.         if(io_key_map[i].Pin == USB_CH_Pin)
  95.         {
  96.             if(status == USB_VALUE)
  97.             {
  98.                 if(!io_key_map[i].Status)
  99.                 {
  100.                     io_key_map[i].key_cnt++;
  101.                     if(io_key_map[i].key_cnt>5)
  102.                     {
  103.                         io_key_map[i].Status = 1;
  104.                         io_key_map[i].key_function(1);
  105.                     }
  106.                 }
  107.                 else
  108.                 {
  109.                     io_key_map[i].key_cnt = 0;
  110.                 }
  111.                 if(io_key_map[i].key_cnt == 65535)
  112.                 {
  113.                     io_key_map[i].key_cnt = 8;
  114.                 }
  115.             }
  116.             else
  117.             {
  118.                 if(io_key_map[i].Status)
  119.                 {
  120.                     io_key_map[i].key_function(0);
  121.                     io_key_map[i].Status = 0;
  122.                     io_key_map[i].key_cnt = 0;
  123.                 }
  124.             }
  125.         }
  126.         #endif
  127.     }
  128. #endif
  129. }
復(fù)制代碼
總結(jié)
       該獨(dú)立按鍵的多種功能設(shè)計(jì),目的是實(shí)現(xiàn)單個(gè)按鍵實(shí)現(xiàn)多種功能,就好比遙控器的上、下、左、右、確定、返回等,通過單個(gè)按鍵就可實(shí)現(xiàn)其所有功能。該按鍵功能的實(shí)現(xiàn),依賴于消息隊(duì)列進(jìn)行按鍵消息傳遞,項(xiàng)目引入消息隊(duì)列后,極大方便了按鍵回調(diào)函數(shù)的設(shè)計(jì)實(shí)現(xiàn)。
個(gè)人筆記總結(jié),若有問題,歡迎留言、批評(píng)指正!

公眾號(hào)為個(gè)人資料總結(jié),內(nèi)有資料文檔!

評(píng)分

參與人數(shù) 2黑幣 +65 收起 理由
heicad + 15 贊一個(gè)!
admin + 50 共享資料的黑幣獎(jiǎng)勵(lì)!

查看全部評(píng)分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏18 分享淘帖 頂1 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:914666 發(fā)表于 2021-11-20 14:13 | 只看該作者
51hei有你更精彩
回復(fù)

使用道具 舉報(bào)

板凳
ID:394949 發(fā)表于 2021-11-22 09:52 | 只看該作者

給個(gè)評(píng)分唄
回復(fù)

使用道具 舉報(bào)

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表