久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 10512|回復(fù): 26
收起左側(cè)

單片機(jī)基于事件的按鍵處理編程思想(原創(chuàng))

  [復(fù)制鏈接]
ID:471574 發(fā)表于 2021-9-25 09:39 | 顯示全部樓層 |閱讀模式
本帖最后由 zyhlove813 于 2021-9-25 10:57 編輯

單片機(jī)處理按鍵網(wǎng)上的思路也是五花八門(mén)。入門(mén)的,可能是直接判斷端口,老手的,可能是通過(guò)鍵值計(jì)算;不管是誰(shuí)學(xué)單片機(jī),都逃不了做按鍵處理的程序。我在做項(xiàng)目的過(guò)程中,參考一些網(wǎng)上的思路,結(jié)合自己的算法,通過(guò)項(xiàng)目調(diào)試和驗(yàn)證,終于做出了比較優(yōu)化和滿意的按鍵處理程序,功能有如下幾個(gè)方面:
1、多鍵掃描處理,提高處理速度
2、支持長(zhǎng)按處理(單次觸發(fā)或一直觸發(fā))
3、支持按下、彈起、按住、松開(kāi)、長(zhǎng)按的事件
4、項(xiàng)目中只需要修改掃描鍵值,然后在各事件中判斷對(duì)應(yīng)鍵值(單鍵或多鍵)
主要編程思路如下:
1、變量的說(shuō)明
  1. //長(zhǎng)按鍵的時(shí)長(zhǎng)
  2. #define longkey_times 2000
  3. //長(zhǎng)按單次模式定義,如果要長(zhǎng)按時(shí)一直執(zhí)行,請(qǐng)注釋下一行
  4. #define LONG_PROCESS_ONCE
  5. uint8_t  KEY_PRESS;  //當(dāng)前按下的鍵值
  6. uint8_t  KEY_NOT_PRESS;  //當(dāng)前未被按的鍵值
  7. uint8_t  KEY_LAST;  //上一次的鍵值
  8. uint8_t  KEY_LONG;  //長(zhǎng)按的鍵值
  9. uint8_t  KEY_DOWN;  //按下的鍵值
  10. uint8_t  KEY_UP; //彈起的鍵值
  11. uint8_t  KEY_UP_NL; //彈起的鍵值不帶長(zhǎng)按鍵
  12. uint32_t KEY_TICKS;  //按鍵時(shí)間,用于長(zhǎng)按計(jì)時(shí)
復(fù)制代碼

2、按鍵相關(guān)函數(shù)說(shuō)明
  1. //按鍵處理程序
  2. void JUDGE_KEY(bool SINGLE_KEY); //鍵值掃描及邏輯處理
  3. void KEY_LONG_PROCESS(void);  //長(zhǎng)按事件
  4. void KEY_PRESS_PROCESS(void);  //按住狀態(tài)事件
  5. void KEY_NOT_PRESS_PROCESS(void); //松開(kāi)狀態(tài)事件
  6. void KEY_DOWN_PROCESS(void); //按下事件
  7. void KEY_UP_PROCESS(void);  //彈起事件
復(fù)制代碼
3、按鍵掃描及邏輯處理思路
  1. //bool SINGLE_KEY 防抖開(kāi)關(guān),True時(shí)打開(kāi)
  2. void JUDGE_KEY(bool SINGLE_KEY)
  3. {
  4.     uint8_t TEMP_KEY;  //臨時(shí)的鍵值緩存
  5.     TEMP_KEY = PIND & 0x0C; //批量掃描IO,并生成鍵值,用戶需結(jié)合項(xiàng)目自已修改,PIND
  6.                             //此處表示PD0-7的端口,不同單片機(jī)不一樣,0x0C只取出
  7. //PD2 PD3的值  
  8.     TEMP_KEY ^= 0x0C;       //此處主要是把鍵值取反,如果你的按鍵是低電平觸發(fā)的話
  9.                             //如果你的按鍵是高電平觸發(fā),則刪除此行,不需要取反
  10.     if(TEMP_KEY > 0)  //鍵值大于0,表示有按鍵按著
  11.     {
  12.      delay(10); //防抖延時(shí)
  13.      //以下再一次批量掃描鍵值
  14.     KEY_PRESS = PIND & 0x0C;         
  15.     KEY_PRESS ^= 0x0C;

  16.     //如果防抖開(kāi)關(guān)有效且兩次鍵值不一致,返回不處理
  17.     if(TEMP_KEY!=KEY_PRESS && SINGLE_KEY)
  18.    {
  19.       return;
  20.     }
  21.     }
  22.     else //無(wú)按鍵動(dòng)作,當(dāng)前按下的鍵值=0
  23.     {
  24.     KEY_PRESS=0;
  25.     }
  26.    //核心按鍵邏輯判斷
  27.     KEY_DOWN=(KEY_LAST^KEY_PRESS) & KEY_PRESS;   //按下的鍵值
  28.     KEY_UP=(KEY_LAST^KEY_PRESS) & KEY_LAST;//彈起的鍵值(包含長(zhǎng)按鍵)
  29.     KEY_UP_NL=(~KEY_LONG) & KEY_UP; //彈起的鍵值(不包含長(zhǎng)按鍵)
  30.     KEY_NOT_PRESS=~KEY_PRESS;  //未按的鍵狀態(tài)值
  31.           if(KEY_LONG & KEY_UP)
  32.     {
  33.      KEY_LONG=0;
  34.     }
  35.     if(KEY_PRESS > 0)  //當(dāng)前有按鍵值按下
  36.     {
  37.        if(KEY_LAST & KEY_PRESS)     //如果當(dāng)前的值與上次按下的值有相同的地方
  38.                                               //表示有鍵一直按著,否則可能只是切換了其他按鍵
  39.        {
  40.             //millis()函數(shù)是Arduino的開(kāi)機(jī)時(shí)間毫秒計(jì)數(shù),其他單片機(jī)自己實(shí)現(xiàn)
  41.              if(millis() - KEY_TICKS > longkey_times)   //按鍵時(shí)間大于長(zhǎng)按時(shí)間
  42.             {
  43.                  KEY_LONG =KEY_LAST & KEY_PRESS;    //長(zhǎng)按鍵值等于一直按住的值
  44.                  KEY_LONG_PROCESS();   //長(zhǎng)按鍵處理
  45.                   #ifdef LONG_PROCESS_ONCE  //如果是長(zhǎng)按單次處理
  46.                   KEY_TICKS=millis(); //更新長(zhǎng)按時(shí)間標(biāo)記,避免進(jìn)入長(zhǎng)按判斷
  47.                  #endif
  48.              }
  49.         }
  50.        else
  51.        {
  52.             KEY_TICKS=millis();  //切換了其他鍵,更新長(zhǎng)按時(shí)間標(biāo)記,避免進(jìn)入長(zhǎng)按判斷
  53.         }
  54.     }
  55.     else   //當(dāng)前無(wú)按鍵按下
  56.     {
  57.       KEY_TICKS=millis();  //更新長(zhǎng)按時(shí)間標(biāo)記,避免進(jìn)入長(zhǎng)按判斷
  58.     }
  59.     if(KEY_UP > 0)  //如果有彈起的按鍵值
  60.     {
  61.     KEY_UP_PROCESS();    //按鍵彈起時(shí)處理
  62.     KEY_UP = 0;  //復(fù)位彈起的鍵值
  63.     }
  64.     if(KEY_DOWN > 0)
  65.     {
  66.     KEY_DOWN_PROCESS();  //按鍵按下時(shí)處理
  67.     }
  68.     if(KEY_PRESS > 0)
  69.     {
  70.     KEY_PRESS_PROCESS();  //按鍵按著狀態(tài)處理
  71.     }               
  72.                 if(KEY_NOT_PRESS)
  73.                 {
  74.      KEY_NOT_PRESS_PROCESS();  //按鍵彈起狀態(tài)處理
  75.     }
  76.      KEY_LAST=KEY_PRESS; //更新上一次的鍵值
  77. }
復(fù)制代碼


4、按鍵邏輯處理算法詳解
   4.1首次按下的鍵,先用異或^進(jìn)行上次掃描鍵值和本次掃描鍵值計(jì)算,取得不一樣的鍵位,不一樣的鍵位和本次掃描鍵位相同,則表示首次按下。
0000 0010表示上次掃描的鍵,第1位是按下的狀態(tài)
   0000 0110 表示本次掃描的鍵,第1位和第2位是按下的,
   我們要算出第2位是首次按下,則
  0000 0010 ^ 0000 0110=0000 0100
  0000 0100 & 0000 0110=0000 0100

又如 000 0010表示上次掃描的鍵,第1位是按下的
     0000 0100表示本次掃描的鍵,第2位是按下的,第1位已經(jīng)松開(kāi)
     我們要算出第2位是首次按下,則
     0000 0010 ^ 0000 0100=0000 0110
     0000 0110 & 0000 0100=0000 0100  
   (所以與本次掃描的鍵值與,可以得到首次按下的鍵值)
       KEY_DOWN=(KEY_LAST^KEY_PRESS) & KEY_PRESS;   //按下的鍵值

   4.2彈起的鍵值
      與按下的原理一樣,不同的是要和上次掃描的鍵值相與
          0000 0010表示上次掃描的鍵,第1位是按下的狀態(tài)
          0000 0100 表示本次掃描的鍵,第2位是按下的,
          我們要算出第1位是彈起,則
          0000 0010 ^ 0000 0100=0000 0110
          0000 0110 & 0000 0010=0000 0010

          KEY_UP=(KEY_LAST^KEY_PRESS) & KEY_LAST;//彈起的鍵值(包含長(zhǎng)按鍵)
   4.3長(zhǎng)按鍵一般單獨(dú)處理,彈起時(shí)如果要排除,避免多次觸發(fā)事件,需要計(jì)算出
      不包含長(zhǎng)按鍵的鍵值,用如下公式
           KEY_UP_NL=(~KEY_LONG) & KEY_UP; //彈起的鍵值(不包含長(zhǎng)按鍵)
   4.4 長(zhǎng)按鍵的計(jì)算邏輯,見(jiàn)程序注釋
5、如何使用

    5.1 設(shè)置好長(zhǎng)按的時(shí)間
       #define longkey_times 2000  //這里表示2秒
    5.2 修改掃描鍵值
        TEMP_KEY = PIND & 0x0C; //批量掃描IO,并生成鍵值,用戶需結(jié)合項(xiàng)目自已修改,PIND
                            //此處表示PD0-7的端口,不同單片機(jī)不一樣,0x0C只取出 PD2 PD3的值  
         TEMP_KEY ^= 0x0C;       //此處主要是把鍵值取反,如果你的按鍵是低電平觸發(fā)的話
        //還有一處地方也要一起改
        KEY_PRESS = PIND & 0x0C;         
        KEY_PRESS ^= 0x0C;

注意:51或其他單片機(jī)中,如果按鍵不在同一序列,比如P01 P03 P14 P16,則可以如下設(shè)置
    TEMP_KEY = P0 & 0x0A; //取出 P01 P03
    TEMP_KEY |=(P1 & 0x50); //取出 P14 P16

    TEMP_KEY ^= (0x0A|0x50);       //此處主要是把鍵值取反,如果你的按鍵是低電平觸發(fā)的話,
                                    //如果你的按鍵是高電平觸發(fā),則刪除此行,不需要取反
    //還有一處地方也要一起改
    KEY_PRESS = P0 & 0x0A; //取出 P01 P03   
    KEY_PRESS |=(P1 & 0x50); //取出 P14 P16
    KEY_PRESS ^= (0x0A|0x50);      //此處主要是把鍵值取反,如果你的按鍵是低電平觸發(fā)的話,
                                 //如果你的按鍵是高電平觸發(fā),則刪除此行,不需要取反
為了編程方便,盡量使用同一序列的口,如果不同序列的口,那端口號(hào)也要能錯(cuò)開(kāi),如用了P01,就不要用P11了。
這樣的話,才能方便計(jì)算,提高掃描效率,如果非要用,只能通過(guò)移位處理
如51或其他單片機(jī)中,想判斷 P01 P02 P12 P13的鍵
TEMP_KEY = P1 & 0x0C; //取出 P12 P13
TEMP_KEY =TEMP_KEY<<1; //左移1位,避開(kāi)P12和P02交叉重疊
TEMP_KEY |= (P0 & 0x06); //取出 P01 P02
TEMP_KEY ^= (0x18|0x06);       //此處主要是把鍵值取反,如果你的按鍵是低電平觸發(fā)的話
                               //如果你的按鍵是高電平觸發(fā),則刪除此行,不需要取反
這樣鍵值里,0x02表示P01,0x04表示P02,0x08表示P12,0x10表示P13

5.3在單片機(jī)循環(huán)程序或定時(shí)器里,周期性調(diào)用掃描程序
void loop()
{
   JUDGE_KEY(true);
}
5.4在對(duì)應(yīng)事件里進(jìn)行其他編程

如:
void KEY_NOT_PRESS_PROCESS()   //按鍵彈起狀態(tài)處理
{
  if(KEY_NOT_PRESS & 0x04)
  {
    //Serial.println("KEY PD2 is NOT PRESSING");
  }
}
void KEY_PRESS_PROCESS() //按鍵按著狀態(tài)處理
{
  if(KEY_PRESS & 0x04)
  {
    //Serial.println("KEY PD2 is PRESSING");
  }
}
void KEY_LONG_PROCESS() //長(zhǎng)按鍵處理
{
  if(KEY_LONG & 0x04)
  {
    Serial.println("KEY PD2 is LONG PRESS");
  }
  if(KEY_LONG & 0x08)
  {
    Serial.println("KEY PD3 is LONG PRESS");
  }  
}
void KEY_DOWN_PROCESS()  //按鍵按下時(shí)處理
{
  if(KEY_DOWN & 0x04)
  {
    Serial.println("KEY PD2 is DOWN NOW");
  }
  if(KEY_DOWN & 0x08)
  {
    Serial.println("KEY PD3 is DOWN NOW");
  }         
  Serial.println("---------------------");
}
void KEY_UP_PROCESS()    //按鍵彈起時(shí)處理
{
  if(KEY_UP_NL & 0x04)
  {
    Serial.println("KEY PD2 is UP_NL NOW");
  }        
  if(KEY_UP_NL & 0x08)
  {
    Serial.println("KEY PD3 is UP NOW");
  }  
  Serial.println("---------------------");
}
附上Arduino的測(cè)試程序,注意Arduino Uno中PD2表示數(shù)字腳2,PD3表示數(shù)字腳3
KEY_TEST-Arduino.rar (1.35 KB, 下載次數(shù): 135)

評(píng)分

參與人數(shù) 2黑幣 +62 收起 理由
sadv + 12 好帖
admin + 50 共享資料的黑幣獎(jiǎng)勵(lì)!

查看全部評(píng)分

回復(fù)

使用道具 舉報(bào)

ID:968484 發(fā)表于 2021-9-27 04:11 | 顯示全部樓層
謝了好資料,51黑有你更精彩!!!
回復(fù)

使用道具 舉報(bào)

ID:471574 發(fā)表于 2021-9-27 10:16 | 顯示全部樓層
ximao 發(fā)表于 2021-9-27 04:11
謝了好資料,51黑有你更精彩!!!

有用就好
回復(fù)

使用道具 舉報(bào)

ID:194006 發(fā)表于 2021-10-6 17:53 來(lái)自手機(jī) | 顯示全部樓層
先收藏一下,以后用得上
回復(fù)

使用道具 舉報(bào)

ID:971477 發(fā)表于 2021-10-12 16:19 | 顯示全部樓層
好資料,51黑有你更精彩!!!
回復(fù)

使用道具 舉報(bào)

ID:971822 發(fā)表于 2021-10-13 20:27 | 顯示全部樓層
不錯(cuò),值得學(xué)習(xí)
回復(fù)

使用道具 舉報(bào)

ID:471574 發(fā)表于 2021-10-25 11:16 | 顯示全部樓層

一起學(xué)習(xí),如果你有好的建議也不妨提出,給大家學(xué)習(xí)一下
回復(fù)

使用道具 舉報(bào)

ID:941265 發(fā)表于 2021-11-1 17:00 | 顯示全部樓層
好資料,51黑有你更精彩!!!
回復(fù)

使用道具 舉報(bào)

ID:370415 發(fā)表于 2021-11-9 12:47 | 顯示全部樓層
一起學(xué)習(xí),51黑一定輝煌起來(lái)
回復(fù)

使用道具 舉報(bào)

ID:850575 發(fā)表于 2021-11-19 08:18 | 顯示全部樓層
很深?yuàn)W的內(nèi)容。
回復(fù)

使用道具 舉報(bào)

ID:814879 發(fā)表于 2022-1-12 11:01 | 顯示全部樓層
剛好,我這個(gè)小白去學(xué)習(xí)學(xué)習(xí)!
回復(fù)

使用道具 舉報(bào)

ID:8222 發(fā)表于 2022-1-31 09:28 | 顯示全部樓層
很好的資料!
回復(fù)

使用道具 舉報(bào)

ID:258676 發(fā)表于 2022-3-11 09:54 | 顯示全部樓層
這個(gè)模塊很方便
回復(fù)

使用道具 舉報(bào)

ID:22218 發(fā)表于 2022-3-19 03:39 | 顯示全部樓層
本帖最后由 chinomango 于 2022-3-19 03:41 編輯

這個(gè)適合別的uP和C編譯嗎?最好前面有#if以自動(dòng)適合不同的C編譯。
看到晚了,上個(gè)月還自己寫(xiě)了一個(gè),不過(guò)支持連續(xù)按鍵2次,以即時(shí)關(guān)閉電源;單次則是延遲3分鐘關(guān)閉。不知用你這個(gè)要如何改動(dòng)? 先謝過(guò)。這個(gè)是堵塞式的嗎還是定時(shí)中斷掃描鍵盤(pán)?
回復(fù)

使用道具 舉報(bào)

ID:471574 發(fā)表于 2022-3-30 08:10 | 顯示全部樓層
chinomango 發(fā)表于 2022-3-19 03:39
這個(gè)適合別的uP和C編譯嗎?最好前面有#if以自動(dòng)適合不同的C編譯。
看到晚了,上個(gè)月還自己寫(xiě)了一個(gè),不過(guò) ...

這個(gè)只是把按鍵值用計(jì)算的方式來(lái)產(chǎn)生按下、彈長(zhǎng)、長(zhǎng)按等事件動(dòng)作,連續(xù)兩次按鍵,你可以在按下或彈起的事件里,用一個(gè)值來(lái)判斷按了幾次,再加上一個(gè)時(shí)長(zhǎng),如果超時(shí)或者中間按了其他按鍵,這個(gè)值就初始化到初值;
這個(gè)程序主要作用是產(chǎn)生事件,至少邏輯處理,要做什么事情,是你要在事件里進(jìn)行編程。我這個(gè)程序,如果 JUDGE_KEY(true)在定時(shí)器里中斷調(diào)用,就變成是中斷式,如果在Loop循環(huán)里調(diào)用,就成了堵塞式。看你的程序需求
回復(fù)

使用道具 舉報(bào)

ID:933601 發(fā)表于 2022-5-19 18:29 | 顯示全部樓層
收藏一下
回復(fù)

使用道具 舉報(bào)

ID:966468 發(fā)表于 2022-6-23 09:35 | 顯示全部樓層
非常好的設(shè)計(jì)思路,如果能提高實(shí)時(shí)性就更好了
回復(fù)

使用道具 舉報(bào)

ID:471574 發(fā)表于 2022-6-25 11:37 | 顯示全部樓層
mrzhou 發(fā)表于 2022-6-23 09:35
非常好的設(shè)計(jì)思路,如果能提高實(shí)時(shí)性就更好了

多個(gè)項(xiàng)目應(yīng)用證明,一般的項(xiàng)目都能應(yīng)對(duì),如果有擔(dān)心的話,也可以把調(diào)用放在定時(shí)中斷里
回復(fù)

使用道具 舉報(bào)

ID:364137 發(fā)表于 2022-8-19 22:45 | 顯示全部樓層
很好的經(jīng)驗(yàn),謝謝分享。
回復(fù)

使用道具 舉報(bào)

ID:509408 發(fā)表于 2022-8-20 09:03 | 顯示全部樓層
按鍵IO那里還是要做的更通用些就好了,我自己的項(xiàng)目應(yīng)用中因?yàn)镮O口緊張,是不太可能按鍵分布在連續(xù)IO口上的。
回復(fù)

使用道具 舉報(bào)

ID:137736 發(fā)表于 2022-9-3 17:52 | 顯示全部樓層
太好了,也上個(gè)51的源碼好了
回復(fù)

使用道具 舉報(bào)

ID:1032507 發(fā)表于 2022-11-2 15:48 | 顯示全部樓層
樓主寫(xiě)的很詳細(xì),謝謝分享。
回復(fù)

使用道具 舉報(bào)

ID:979603 發(fā)表于 2022-11-10 07:54 | 顯示全部樓層

好資料,51黑有你更精彩!!!
回復(fù)

使用道具 舉報(bào)

ID:1013961 發(fā)表于 2022-12-3 17:28 | 顯示全部樓層
很不錯(cuò)的按鍵邏輯處理。收藏了。
回復(fù)

使用道具 舉報(bào)

ID:27536 發(fā)表于 2023-5-12 16:47 | 顯示全部樓層
既然都用事件了,為什么還要延時(shí)防抖呢?
回復(fù)

使用道具 舉報(bào)

ID:224580 發(fā)表于 2023-8-21 23:26 | 顯示全部樓層
值得慢慢研究,加入到我的代碼上去
回復(fù)

使用道具 舉報(bào)

ID:471574 發(fā)表于 2024-6-8 08:28 | 顯示全部樓層
likejian 發(fā)表于 2023-8-21 23:26
值得慢慢研究,加入到我的代碼上去

核心思想是把按鍵的IO轉(zhuǎn)化為數(shù)值,然后通過(guò)新舊數(shù)據(jù)的運(yùn)算,篩選出IO對(duì)應(yīng)的不同狀態(tài)值,然后使用時(shí)通過(guò)判斷是否為需要的IO值。
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

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

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

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 久草高清视频 | 久久国 | 久久久www成人免费无遮挡大片 | 视频三区 | 成人精品鲁一区一区二区 | 成人伊人| 国产视频欧美 | 黄色成人在线网站 | 亚洲精品在线视频 | 成人国产精品入口免费视频 | 久久综合久 | 亚洲网站在线观看 | www.亚洲区 | 欧美三级在线 | 一区二区在线 | 中文字幕成人 | 精品成人 | 欧美日韩三级视频 | 久久精品一区二区三区四区 | 精品久久久久久久久久久院品网 | 久久久久久国产精品久久 | 日韩在线精品 | 色婷婷一区二区三区四区 | 久久国产精品一区二区三区 | 欧美黄在线观看 | 亚州成人 | 99爱在线视频 | 亚洲欧美日本国产 | 精品久久一区 | 精品国产一区二区三区性色av | 综合色播| 亚洲欧洲日本国产 | 超碰日本 | 欧美一区二区三区免费在线观看 | 91精品久久久久久久久久入口 | 欧美一级免费片 | 黄色毛片免费看 | 久久久91精品国产一区二区三区 | 国产一区二区电影 | 91精品综合久久久久久五月天 | 91免费在线 |