亚洲春色中文字幕久久久-三上亚,一吻二脱三床四吻胸,国产真实伦对白视频全集,在线毛片观看,精品成品入口黄网,国产毛aⅴ片久久久,亚洲AV色香蕉一区二区三区老师,萧皇后A级艳片,色情日本视频更新,99久久亚洲精品日本无码

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

QQ登錄

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

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

僅需40行!可實(shí)現(xiàn)按鍵長(zhǎng)按單擊雙擊三擊四擊,挑戰(zhàn)行數(shù)最少代碼

  [復(fù)制鏈接]
回帖獎(jiǎng)勵(lì) 2 黑幣 回復(fù)本帖可獲得 2 黑幣獎(jiǎng)勵(lì)! 每人限 1 次
跳轉(zhuǎn)到指定樓層
樓主
ID:1155837 發(fā)表于 2026-2-5 01:11 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
差不多一年前我發(fā)了個(gè)帖子,內(nèi)容是“使用掃描后輸出結(jié)果進(jìn)行邏輯運(yùn)算的按鍵處理方法”,就在這里http://www.denmoz.com/bbs/dpj-240563-1.html
當(dāng)時(shí)因?yàn)閯傞_(kāi)始學(xué)單片機(jī),沒(méi)那個(gè)實(shí)力也沒(méi)時(shí)間,所以今天再翻老底的時(shí)候才發(fā)現(xiàn)了之前的創(chuàng)意
現(xiàn)在的我也是有能力把這個(gè)代碼寫(xiě)出來(lái)了,說(shuō)白了也沒(méi)什么難的,當(dāng)時(shí)其實(shí)也寫(xiě)完大半了,只是當(dāng)時(shí)課設(shè)趕著交,所以也就沒(méi)搞出來(lái)。
這個(gè)代碼實(shí)現(xiàn)的功能從原理上非常容易理解,既:以定時(shí)器中斷為時(shí)基,每次定時(shí)器中斷都采樣一次,總共采樣32次,存為一個(gè)32位的ulong量。然后通過(guò)邏輯與位運(yùn)算,計(jì)算采樣結(jié)果中的上升沿?cái)?shù)量,進(jìn)而得到按鍵按下的次數(shù)與時(shí)間。
那么應(yīng)該如何采樣呢?在回答這個(gè)問(wèn)題之前,我們必須明白,采樣應(yīng)該如何觸發(fā)畢竟中斷無(wú)時(shí)無(wú)刻都在調(diào)用,如果一直采樣,不僅邏輯上不好處理,對(duì)CPU時(shí)間消耗也會(huì)更多。
答案很簡(jiǎn)單,第一個(gè)下降沿就代表按鍵已經(jīng)按下,要判斷下降沿,我們需要兩個(gè)變量和一個(gè)if判斷
  Previous_Key = Now_Key;//存儲(chǔ)上一次的結(jié)果
  Now_Key = Read_Temp;//讀取本次結(jié)果
  //Key_Event = NOKEY;//未在采樣狀態(tài),全局按鍵事件為無(wú)鍵
  if(!Now_Key && Previous_Key){//上一次為1,這一次為0,是下降沿
  Start_Sampling = true;//開(kāi)始采樣
  }


上面的代碼很簡(jiǎn)單是不是?先保存之前的按鍵狀態(tài),再讀取現(xiàn)在的,只要之前是1,現(xiàn)在是0,那么就是一個(gè)下降沿
明白了采樣如何觸發(fā)之后,就該考慮如何采樣了,這個(gè)也很簡(jiǎn)單,只需要一個(gè)uchar記錄采樣次數(shù),一個(gè)ulong變量緩存采樣結(jié)果即可
if(Sampling_Counter < 32){
      Is_Sampling = true;
      Sampling_Temp <<= 1;//整體左移一位,低位補(bǔ)0
      if (Read_Temp){//采樣引腳,如果為高電平,最低位置1
        Sampling_Temp |= 0x01;//如果引腳為高才計(jì)1,如果為低就繼續(xù)左移一位,也就是補(bǔ)0
        }
        Sampling_Counter++;
        }


是不是也很簡(jiǎn)單?每次采樣,緩存數(shù)據(jù)左移一位,填入低位,執(zhí)行32次就得到了結(jié)果
有了結(jié)果之后,怎么計(jì)算上升沿?cái)?shù)量呢?這是本方案里最巧妙的一點(diǎn)。我們用一個(gè)掩碼Mask表示運(yùn)算結(jié)果。
Mask = ~Input & (Input << 1);

即:先對(duì)data取反,將取反后的值,與data左移一位后的值,做邏輯與就可得到上升沿掩碼,mask中的1的數(shù)量,就是采樣結(jié)果中上升沿的數(shù)量
這樣做到底對(duì)不對(duì)?我們來(lái)簡(jiǎn)單驗(yàn)證。
假設(shè)一個(gè)八位采樣結(jié)果為: 01101000
取反:1 0 0 1 0 1 1 1
左移一位:1 1 0 1 0 0 0 0
邏輯與:1 0 0 1 0 0 0 0
可以看到,結(jié)果中有兩個(gè)1,也就是兩個(gè)上升沿。而我們手動(dòng)找出原數(shù)據(jù)中的上升沿(0→1)同樣是兩個(gè)。
說(shuō)明該算法正確。實(shí)際測(cè)試結(jié)果也是可以正確判斷上升沿?cái)?shù)量的,并且上升沿的數(shù)量完全按鍵按下次數(shù)相同。
而如何統(tǒng)計(jì)出掩碼的中的1的數(shù)量呢?笨辦法是直接while循環(huán)遍歷。
    while(Mask){//當(dāng)mask = 0時(shí),循環(huán)結(jié)束
        Count_Temp += Mask & 0X01;//取最低位,與count相加
        Mask >>= 1;//右移一位,拋棄最低位,最高位補(bǔ)0
    }

這是最容易理解的,代碼行數(shù)也最少。但是過(guò)于消耗CPU時(shí)間。每次循環(huán)都需要幾個(gè)CPU周期,總共需要消耗160個(gè)周期左右。那有沒(méi)有更省性能的辦法呢?有的兄弟,有的,那就是著名的SIMD Within A Register算法,這個(gè)算法我也不太明白原理,但是能用。
unsigned char Rise_Edge_Counter(unsigned long Input){
  unsigned long Mask;//掩碼
    Mask = ~Input & (Input << 1);
    Mask = (Mask & 0x55555555) + ((Mask >> 1) & 0x55555555);
    Mask = (Mask & 0x33333333) + ((Mask >> 2) & 0x33333333);
    Mask = (Mask + (Mask >> 4)) & 0x0F0F0F0F;
    Mask = Mask + (Mask >> 8);
    Mask = Mask + (Mask >> 16);
    Mask = Mask & 0x0000003F;
    return Mask;
}

將消耗時(shí)間的while循環(huán)變成了CPU可以輕松執(zhí)行的邏輯與移位運(yùn)算,整體不會(huì)超過(guò)15個(gè)時(shí)鐘周期!
上面已經(jīng)將基本原理說(shuō)清楚了,也沒(méi)什么好說(shuō)的了,直接上源碼!需要和上面那個(gè)riseedgecounter函數(shù)一塊使用。
  1. #define NOKEY 0
  2. #define SINGLEKEY 1
  3. #define DOUBLEKEY 2
  4. #define TRIPLEKEY 3
  5. #define QUADRAKEY 4
  6. #define LONGKEY 5
  7. unsigned char Key_Event = NOKEY;//全局按鍵狀態(tài)
  8. //函數(shù)不會(huì)主動(dòng)清零,需要在使用之后手動(dòng)清零,可以保持輸出狀態(tài)
  9. void Key_Sampling(unsigned char IO_Set){//傳入需要進(jìn)行采樣的引腳
  10.   //實(shí)際上本函數(shù)自帶消抖,因?yàn)椴蛔阋粋(gè)定時(shí)器間隔(20ms)的下降沿?zé)o法觸發(fā)采樣
  11.   static unsigned char Sampling_Counter = 0;//采樣次數(shù)計(jì)數(shù)器
  12.   static unsigned long Sampling_Temp = 0;//采樣結(jié)果緩存
  13.   static bool Previous_Key = false;//之前的按鍵采樣記錄
  14.   static bool Now_Key = false;//當(dāng)前的按鍵采樣記錄
  15.   static bool Is_Sampling = false;//正在采樣標(biāo)志
  16.   static bool Start_Sampling = false;//開(kāi)始采樣標(biāo)志,由下降沿觸發(fā)
  17.   static bool Read_Temp = false;//按鍵狀態(tài)緩存,每次進(jìn)入函數(shù)時(shí)讀取*/
  18.   unsigned char Rise_Edge_Result = 0;//上升沿計(jì)算的結(jié)果,非static變量,重入自動(dòng)清零
  19.   Read_Temp = digitalRead(IO_Set);//采樣一次
  20.   //采樣觸發(fā)(檢測(cè)下降沿)
  21.   if(!Is_Sampling){//如果沒(méi)有在采樣,才做判斷
  22.   Previous_Key = Now_Key;//存儲(chǔ)上一次的結(jié)果
  23.   Now_Key = Read_Temp;//讀取本次結(jié)果
  24.   //Key_Event = NOKEY;//未在采樣狀態(tài),全局按鍵事件為無(wú)鍵
  25.   if(!Now_Key && Previous_Key){//上一次為1,這一次為0,是下降沿
  26.   Start_Sampling = true;//開(kāi)始采樣
  27.   }
  28.   else{
  29.     return;}//未觸發(fā)采樣,直接返回
  30.   }
  31.   //開(kāi)始采樣與結(jié)果處理
  32.   if(Start_Sampling){
  33.     if(Sampling_Counter < 32){
  34.       Is_Sampling = true;
  35.       Sampling_Temp <<= 1;//整體左移一位,低位補(bǔ)0
  36.       if (Read_Temp){//采樣引腳,如果為高電平,最低位置1
  37.         Sampling_Temp |= 0x01;//如果引腳為高才計(jì)1,如果為低就繼續(xù)左移一位,也就是補(bǔ)0
  38.         }
  39.         Sampling_Counter++;
  40.         }
  41.       else{//采滿32次,采樣結(jié)束,開(kāi)始計(jì)算
  42.         Is_Sampling = false;
  43.         Start_Sampling = false;
  44.         Sampling_Counter = 0;//清空計(jì)數(shù)器
  45.         Rise_Edge_Result = Rise_Edge_Counter(Sampling_Temp);//計(jì)算上升沿?cái)?shù)量
  46.         switch(Rise_Edge_Result){
  47.           case 0: Key_Event = LONGKEY; break;
  48.           case 1: Key_Event = SINGLEKEY;break;
  49.           case 2: Key_Event = DOUBLEKEY;break;
  50.           case 3: Key_Event = TRIPLEKEY;break;
  51.           case 4: Key_Event = QUADRAKEY;break;
  52.         }
  53.         }
  54.   }
  55. }
復(fù)制代碼

其中,Key_Event為全局按鍵變量,在函數(shù)內(nèi)部不會(huì)清零,需要手動(dòng)在使用完畢之后清零。變相實(shí)現(xiàn)了保持輸出狀態(tài)的功能。
除過(guò)變量聲明還有注釋,確實(shí)只有幾十行,不是嗎?
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏17 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:1155837 發(fā)表于 2026-2-5 01:30 | 只看該作者
下面為使用教程,本函數(shù)使用了一個(gè)Key_Event為全局按鍵事件變量,需要在其他函數(shù)中讀取這個(gè)Key_Event。
你需要在定時(shí)器中斷中調(diào)用采樣函數(shù),結(jié)果保存在Key_Event中。
void TIMER_ISR onTimer() {
  Key_Sampling(Button);
}

如果你想通過(guò)串口測(cè)試按鍵結(jié)果?
void loop() {
    delay(500);
    Serial.print("Key Event is");
    Serial.println(Key_Event);
    Key_Event = NOKEY;
}

另外,這是Arduino平臺(tái)的測(cè)試方法,如果你想移植,也非常簡(jiǎn)單,只需要修改下這一行。
  Read_Temp = digitalRead(IO_Set);//采樣一次
如果你是51單片機(jī),那你可以寫(xiě)
  Read_Temp = P32;//采樣一次
如果你是32單片機(jī),你可以寫(xiě)
  Read_Temp = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);//采樣一次
實(shí)際上這種不涉及外設(shè)的功能代碼都是通用的,可以隨意移植的,不同平臺(tái),也只不過(guò)是初始化的時(shí)候不同,51用寄存器初始化,32單片機(jī)用hal庫(kù)或者標(biāo)準(zhǔn)庫(kù)初始化,arduino同理。
另外,如果你想用在多個(gè)按鍵上,怎么處理?
這個(gè)比較復(fù)雜,需要用到結(jié)構(gòu)體:
  1. typedef struct{//多個(gè)按鍵存儲(chǔ)不同結(jié)構(gòu)體
  2.   unsigned char Key_IO;
  3.   unsigned char Sampling_Counter;//采樣次數(shù)計(jì)數(shù)器
  4.   unsigned long Result_Temp;//采樣結(jié)果緩存
  5.   bool Previous_Key;//之前的按鍵采樣記錄
  6.   bool Now_Key;//當(dāng)前的按鍵采樣記錄
  7.   bool Is_Sampling;//正在采樣標(biāo)志
  8.   bool Start_Sampling;//開(kāi)始采樣標(biāo)志,由下降沿觸發(fā)
  9.   bool Key_Temp;//按鍵狀態(tài)緩存,每次進(jìn)入函數(shù)時(shí)讀取
  10.   unsigned char Rise_Edge_Result;
  11. } Key_Sampling_Group;
  12. Key_Sampling_Group IO_15{15,0,0,0,0,0,0,0};//IO15所接按鍵
  13. Key_Sampling_Group IO_7{7,0,0,0,0,0,0,0};//IO7所接按鍵
復(fù)制代碼
通過(guò)這個(gè)結(jié)構(gòu)體,我們可以用同一個(gè)函數(shù),實(shí)現(xiàn)多個(gè)IO并行檢測(cè),函數(shù)的變量分別保存,不會(huì)互相干擾,節(jié)省flash,雖然其實(shí)這么短的代碼根本不會(huì)超過(guò)500字節(jié)代碼吧,但是如果按鍵比較多,占用還是很可觀的。
使用結(jié)構(gòu)體,傳入時(shí)需要傳入指針(地址),否則會(huì)導(dǎo)致直接將結(jié)構(gòu)體中的數(shù)值復(fù)制一份到函數(shù)中,你在定時(shí)器中斷中這么寫(xiě):
Key_Sampling(Key_Sampling_Group *IO_Set)
這樣就可以傳入結(jié)構(gòu)體的地址,函數(shù)會(huì)使用結(jié)構(gòu)體中的變量,也會(huì)自動(dòng)保存在結(jié)構(gòu)體中。
然后你需要在函數(shù)中每個(gè)用到對(duì)應(yīng)變量的地方修改,比如:
原本是digitalRead(Key_IO);
現(xiàn)在你需要改為digitalRead(IO_Set -> Key_IO);
這樣才能讓函數(shù)讀取到你結(jié)構(gòu)體中的數(shù)據(jù)。
回復(fù)

使用道具 舉報(bào)

板凳
ID:1142711 發(fā)表于 2026-2-5 08:14 | 只看該作者
獲益匪淺!
回復(fù)

使用道具 舉報(bào)

地板
ID:143767 發(fā)表于 2026-2-6 11:16 | 只看該作者
從程序里看不出按鍵接在哪個(gè)IO口了
回復(fù)

使用道具 舉報(bào)

5#
ID:1073939 發(fā)表于 2026-2-6 11:19 | 只看該作者
想法很新穎,不過(guò)很難用于工程實(shí)踐:
采用固定32次(640ms)采樣窗口,無(wú)法滾動(dòng)更新數(shù)據(jù)。感覺(jué)雙擊,長(zhǎng)按會(huì)有問(wèn)題。
常規(guī)工程中長(zhǎng)按功能通常設(shè)置為3秒左右(且可配置1~20秒)。
三連擊、四連擊在實(shí)際項(xiàng)目中很少用到;
回復(fù)

使用道具 舉報(bào)

6#
ID:584814 發(fā)表于 2026-2-6 12:06 | 只看該作者
感謝分享一種好思路,實(shí)用情況待測(cè)。
回復(fù)

使用道具 舉報(bào)

7#
ID:1073939 發(fā)表于 2026-2-6 14:12 | 只看該作者
下面是ai分析的結(jié)果,雖不完全贊同,但部分是正確的。
你希望我分析這段按鍵采樣代碼的邏輯,并結(jié)合 20ms 的執(zhí)行間隔,指出它與常規(guī)按鍵處理相比的優(yōu)缺點(diǎn),特別是針對(duì)單擊、雙擊等場(chǎng)景的問(wèn)題。
一、代碼核心邏輯梳理
這段代碼的核心是基于定時(shí)器(20ms 間隔)的連續(xù)采樣機(jī)制,通過(guò)檢測(cè)按鍵引腳的電平變化,統(tǒng)計(jì) 32 次采樣內(nèi)的上升沿?cái)?shù)量,來(lái)判斷按鍵事件類型,核心流程如下:
觸發(fā)采樣:檢測(cè)到按鍵下降沿(按下瞬間),啟動(dòng) 32 次連續(xù)采樣;
采樣過(guò)程:每 20ms 采樣一次引腳電平,存入 32 位的Sampling_Temp緩存;
結(jié)果判定:32 次采樣完成后,調(diào)用Rise_Edge_Counter統(tǒng)計(jì)上升沿?cái)?shù)量,對(duì)應(yīng)不同按鍵事件(0 = 長(zhǎng)按、1 = 單擊、2 = 雙擊等);
狀態(tài)保持:全局變量Key_Event會(huì)保持判定結(jié)果,直到手動(dòng)清零。
簡(jiǎn)單來(lái)說(shuō),它的判定邏輯是:32 次采樣(共 640ms)內(nèi)的上升沿?cái)?shù)量 = 按鍵點(diǎn)擊次數(shù)。
二、結(jié)合 20ms 間隔的核心缺點(diǎn)分析
1. 判定延遲嚴(yán)重,實(shí)時(shí)性差
代碼必須完成32 次采樣(32×20ms=640ms) 后,才能輸出按鍵事件結(jié)果。
單擊場(chǎng)景:用戶按下并松開(kāi)按鍵后,需要等待 640ms 才能觸發(fā)SINGLEKEY事件,遠(yuǎn)慢于常規(guī)按鍵 “松開(kāi)即觸發(fā)” 的邏輯,操作反饋滯后明顯;
長(zhǎng)按場(chǎng)景:長(zhǎng)按超過(guò) 640ms 才會(huì)判定為L(zhǎng)ONGKEY,常規(guī)長(zhǎng)按通常 200~300ms 即可觸發(fā),這段代碼的長(zhǎng)按判定過(guò)于遲鈍。
2. 雙擊 / 多擊邏輯不符合用戶習(xí)慣,易誤判
常規(guī)雙擊的核心邏輯是:第一次單擊松開(kāi)后,500ms 內(nèi)再次單擊,判定為雙擊;超過(guò) 500ms 則判定為兩次獨(dú)立單擊。但這段代碼完全違背這一習(xí)慣:
代碼的判定窗口是固定的 640ms,只要兩次單擊的時(shí)間間隔≤640ms,就會(huì)被判定為雙擊;
你提到的 “單擊后間隔 500ms 再單擊”,在代碼中會(huì)被判定為雙擊(32 次采樣內(nèi)包含 2 個(gè)上升沿),而非用戶預(yù)期的兩次單擊,完全不符合日常操作直覺(jué);
多擊(三擊、四擊)同理,只要 640ms 內(nèi)完成多次點(diǎn)擊,就會(huì)被合并判定,無(wú)法識(shí)別 “間隔稍長(zhǎng)的多次獨(dú)立單擊”。
3. 采樣窗口固定,缺乏靈活性,易漏判 / 誤判
32 次采樣的 640ms 窗口是固定的,沒(méi)有動(dòng)態(tài)調(diào)整機(jī)制,存在明顯缺陷:
漏判短按:如果用戶點(diǎn)擊速度極快(如 < 100ms),可能在 32 次采樣開(kāi)始前就完成按下松開(kāi),導(dǎo)致采樣未捕捉到有效電平,判定為NOKEY;
誤判長(zhǎng)按:如果用戶按下按鍵后,在 640ms 內(nèi)未松開(kāi)(如按住 300ms),會(huì)被判定為L(zhǎng)ONGKEY,但常規(guī)邏輯中這屬于 “短按未松開(kāi)”,而非長(zhǎng)按;
無(wú)法處理 “長(zhǎng)按后松開(kāi)”:代碼中LONGKEY僅由 “32 次采樣內(nèi)無(wú)上升沿” 判定,即 “按下后 640ms 未松開(kāi)”,但用戶長(zhǎng)按后松開(kāi)的動(dòng)作,代碼無(wú)法區(qū)分 “長(zhǎng)按過(guò)程中” 和 “長(zhǎng)按結(jié)束”,事件粒度太粗。
4. 狀態(tài)管理混亂,易導(dǎo)致邏輯異常
全局變量依賴:Key_Event是全局變量,且不會(huì)自動(dòng)清零,必須手動(dòng)處理。如果主程序忘記清零,會(huì)導(dǎo)致后續(xù)采樣結(jié)果被覆蓋,或重復(fù)觸發(fā)同一事件;
采樣狀態(tài)鎖死:一旦啟動(dòng) 32 次采樣,期間無(wú)法響應(yīng)新的按鍵動(dòng)作(Is_Sampling為 true 時(shí),下降沿檢測(cè)被屏蔽)。如果用戶在采樣過(guò)程中再次點(diǎn)擊,會(huì)被完全忽略,導(dǎo)致按鍵響應(yīng)丟失;
無(wú)防抖優(yōu)化:代碼聲稱 “自帶消抖”,但僅依賴 “不足 20ms 的下降沿?zé)o法觸發(fā)采樣”,實(shí)際未對(duì)按鍵物理抖動(dòng)(通常 10~20ms)做專門(mén)處理,抖動(dòng)電平可能被計(jì)入采樣緩存,導(dǎo)致上升沿統(tǒng)計(jì)錯(cuò)誤,引發(fā)誤判。
5. 事件類型覆蓋不全,實(shí)用性低
代碼僅支持 “無(wú)鍵、單擊、雙擊、三擊、四擊、長(zhǎng)按”6 種事件,缺乏常規(guī)按鍵的核心事件類型:
無(wú) “按鍵按下”“按鍵松開(kāi)” 基礎(chǔ)事件,無(wú)法實(shí)現(xiàn) “按下執(zhí)行、松開(kāi)停止” 的交互邏輯;
無(wú) “長(zhǎng)按保持” 事件(如長(zhǎng)按持續(xù)調(diào)節(jié)音量),僅能判定 “長(zhǎng)按觸發(fā)一次”,無(wú)法滿足連續(xù)操作需求;
多擊上限僅到四擊,且判定邏輯不符合習(xí)慣,實(shí)際應(yīng)用中幾乎無(wú)法適配雙擊、三擊的正常交互。
三、與常規(guī)按鍵處理方案的核心差異
特性        這段代碼        常規(guī)按鍵處理(狀態(tài)機(jī) / 定時(shí)器方案)
判定時(shí)機(jī)        固定 640ms 采樣結(jié)束后        松開(kāi)即觸發(fā)單擊,500ms 內(nèi)二次按下觸發(fā)雙擊
雙擊邏輯        640ms 內(nèi)兩次點(diǎn)擊 = 雙擊        第一次松開(kāi)后 500ms 內(nèi)二次按下 = 雙擊
實(shí)時(shí)性        延遲 640ms,反饋滯后        無(wú)延遲,操作即響應(yīng)
采樣過(guò)程        鎖死狀態(tài),忽略新按鍵        實(shí)時(shí)掃描,響應(yīng)新動(dòng)作
防抖處理        依賴采樣間隔,無(wú)專門(mén)優(yōu)化        連續(xù)采樣(如 10ms)確認(rèn)電平,過(guò)濾抖動(dòng)
事件粒度        僅 6 種復(fù)合事件        支持按下、松開(kāi)、單擊、雙擊、長(zhǎng)按、長(zhǎng)按保持
四、總結(jié)
這段代碼的核心問(wèn)題是用 “固定時(shí)長(zhǎng)的連續(xù)采樣 + 上升沿統(tǒng)計(jì)” 替代了常規(guī)按鍵的 “狀態(tài)時(shí)序判定”,導(dǎo)致實(shí)時(shí)性差、交互邏輯不符合用戶習(xí)慣、狀態(tài)管理混亂,且缺乏實(shí)用的基礎(chǔ)事件類型。
如果要適配常規(guī)按鍵交互,建議改用按鍵狀態(tài)機(jī) + 定時(shí)器超時(shí)判定方案:通過(guò)掃描引腳電平檢測(cè)按下 / 松開(kāi),用定時(shí)器記錄點(diǎn)擊間隔(如 500ms 雙擊窗口、300ms 長(zhǎng)按閾值),實(shí)時(shí)輸出按下、松開(kāi)、單擊、雙擊、長(zhǎng)按等事件,既保證實(shí)時(shí)性,又符合用戶操作習(xí)慣。
總結(jié)
核心缺陷:640ms 固定采樣窗口導(dǎo)致判定延遲、雙擊邏輯違背用戶習(xí)慣,且采樣過(guò)程鎖死新按鍵;
關(guān)鍵問(wèn)題:缺乏基礎(chǔ)按鍵事件、無(wú)有效防抖、全局狀態(tài)管理易出錯(cuò),實(shí)用性極低;
改進(jìn)方向:替換為狀態(tài)機(jī) + 超時(shí)判定的常規(guī)按鍵方案,適配單擊、雙擊、長(zhǎng)按的標(biāo)準(zhǔn)交互邏輯。
回復(fù)

使用道具 舉報(bào)

8#
ID:1155837 發(fā)表于 2026-2-6 19:02 | 只看該作者
ydatou 發(fā)表于 2026-2-6 11:19
想法很新穎,不過(guò)很難用于工程實(shí)踐:
采用固定32次(640ms)采樣窗口,無(wú)法滾動(dòng)更新數(shù)據(jù)。感覺(jué)雙擊,長(zhǎng)按會(huì) ...

你可以調(diào)節(jié)定時(shí)器中斷間隔來(lái)延長(zhǎng)時(shí)間,我測(cè)過(guò)其實(shí)30ms一次中斷下比較可靠,單擊雙擊長(zhǎng)按所有功能都穩(wěn)定觸發(fā),另外我做的開(kāi)關(guān)機(jī)沒(méi)有用過(guò)這么長(zhǎng)時(shí)間,最多的也就是1.2秒,所以確實(shí)沒(méi)考慮過(guò)3秒開(kāi)機(jī)的情況。雖然也可以用三個(gè)uint16拼出來(lái)更長(zhǎng)的采樣時(shí)間,也可以實(shí)現(xiàn)超時(shí)退出采樣,但是因?yàn)槟菢哟a就復(fù)雜了,還不如老實(shí)用多層switch呢。
回復(fù)

使用道具 舉報(bào)

9#
ID:1155837 發(fā)表于 2026-2-6 19:05 | 只看該作者
dj3365191 發(fā)表于 2026-2-6 11:16
從程序里看不出按鍵接在哪個(gè)IO口了

Read_Temp = digitalRead(IO_Set);//采樣一次
這一行是讀取IO電平。這是arduino的寫(xiě)法,你如果是51的話直接寫(xiě)Read_Temp = PXX就可以了
回復(fù)

使用道具 舉報(bào)

10#
ID:1155837 發(fā)表于 2026-2-6 20:01 | 只看該作者
ydatou 發(fā)表于 2026-2-6 11:19
想法很新穎,不過(guò)很難用于工程實(shí)踐:
采用固定32次(640ms)采樣窗口,無(wú)法滾動(dòng)更新數(shù)據(jù)。感覺(jué)雙擊,長(zhǎng)按會(huì) ...

說(shuō)很難用于工程實(shí)踐有些過(guò)了,實(shí)際上即使是640ms的采樣串口下,我進(jìn)行幾十次輸入測(cè)試也都可以穩(wěn)定觸發(fā)長(zhǎng)按單擊雙擊三擊,只是時(shí)間太短,沒(méi)那么快四連擊,你可以試試,不要光感覺(jué),復(fù)制代碼,只需要改一下按鍵的引腳輸入就可以測(cè)試了,用串口回傳keyevent變量即可。
回復(fù)

使用道具 舉報(bào)

11#
ID:1155837 發(fā)表于 2026-2-6 20:29 | 只看該作者
更新一下,相比傳統(tǒng)switch狀態(tài)機(jī),目前來(lái)說(shuō)固定32次采樣確實(shí)不夠靈活,因此我簡(jiǎn)單修改了一下,實(shí)現(xiàn)了按鍵輸入超時(shí)檢測(cè)。在實(shí)時(shí)性上能達(dá)到switch狀態(tài)機(jī)的水平了,那種寫(xiě)法可以規(guī)定每個(gè)狀態(tài)的等待時(shí)間,還可以在到達(dá)狀態(tài)后立即返回,比如三擊后立即返回三擊狀態(tài),確實(shí)實(shí)時(shí)性更高。
本采訪法按鍵處理函數(shù),實(shí)現(xiàn)超時(shí)檢查的方法就是如果按鍵沒(méi)按下就計(jì)數(shù)一次,按下了就清空計(jì)數(shù),連續(xù)12次沒(méi)按下就停止采樣,沒(méi)采完的直接全部補(bǔ)1。總共增加了10行,依然比多層switch方案簡(jiǎn)短。
void Key_Sampling(unsigned char IO_Set){//傳入需要進(jìn)行采樣的引腳
  //實(shí)際上本函數(shù)自帶消抖,因?yàn)椴蛔阋粋(gè)定時(shí)器間隔的下降沿?zé)o法觸發(fā)采樣
  //在20ms以上的定時(shí)器中斷中調(diào)用本函數(shù),推薦30ms一次中斷
  static unsigned char Sampling_Counter = 0;//采樣次數(shù)計(jì)數(shù)器
  static unsigned long Sampling_Temp = 0;//采樣結(jié)果緩存
  static bool Previous_Key = false;//之前的按鍵采樣記錄
  static bool Now_Key = false;//當(dāng)前的按鍵采樣記錄
  static bool Is_Sampling = false;//正在采樣標(biāo)志
  static bool Start_Sampling = false;//開(kāi)始采樣標(biāo)志,由下降沿觸發(fā)
  static bool Read_Temp = false;//按鍵狀態(tài)緩存,每次進(jìn)入函數(shù)時(shí)讀取
  static unsigned char Key_Release_Counter = 0;//超時(shí)計(jì)數(shù)器,可以更快的響應(yīng)單擊和雙擊操作
  unsigned char Rise_Edge_Result = 0;//上升沿計(jì)算的結(jié)果,非static變量,重入自動(dòng)清零
  Read_Temp = digitalRead(IO_Set);//采樣一次
  //采樣觸發(fā)(檢測(cè)下降沿)
  if(!Is_Sampling){//如果沒(méi)有在采樣,才做判斷
  Previous_Key = Now_Key;//存儲(chǔ)上一次的結(jié)果
  Now_Key = Read_Temp;//讀取本次結(jié)果
  //Key_Event = NOKEY;//未在采樣狀態(tài),全局按鍵事件為無(wú)鍵
  if(!Now_Key && Previous_Key){//上一次為1,這一次為0,是下降沿
  Start_Sampling = true;//開(kāi)始采樣
  }
  else{
    return;}//未觸發(fā)采樣,直接返回
  }
  //開(kāi)始采樣與結(jié)果處理
  if(Start_Sampling){
    if(Sampling_Counter < 32){
      Is_Sampling = true;
      Sampling_Temp <<= 1;//整體左移一位,低位補(bǔ)0
      if (Read_Temp){//采樣引腳,如果為高電平,最低位置1
        Sampling_Temp |= 0x01;//如果引腳為高才計(jì)1,如果為低就繼續(xù)左移一位,也就是補(bǔ)0
        Key_Release_Counter++;
        if(Key_Release_Counter >= 12){//超過(guò)240ms沒(méi)有按鍵輸入
        Key_Release_Counter = 0;
        //采樣超時(shí),提前結(jié)束,未采樣的部分全部補(bǔ)1
        Sampling_Temp = (Sampling_Temp << (32-Sampling_Counter)) | ((1UL << (32-Sampling_Counter)) - 1);
        Sampling_Counter = 32;//標(biāo)記為采樣結(jié)束
        }
        }
        else{//如果按鍵為低電平
        Key_Release_Counter = 0;
        }
        Sampling_Counter++;
        }
      else{//采滿32次,采樣結(jié)束,開(kāi)始計(jì)算
        Is_Sampling = false;
        Start_Sampling = false;
        Sampling_Counter = 0;//清空計(jì)數(shù)器
        Rise_Edge_Result = Rise_Edge_Counter(Sampling_Temp);//計(jì)算上升沿?cái)?shù)量
        switch(Rise_Edge_Result){
          case 0: Key_Event = LONGKEY;  break;
          case 1: Key_Event = SINGLEKEY;break;
          case 2: Key_Event = DOUBLEKEY;break;
          case 3: Key_Event = TRIPLEKEY;break;
          case 4: Key_Event = QUADRAKEY;break;
        }
        }
  }
}
回復(fù)

使用道具 舉報(bào)

12#
ID:1155837 發(fā)表于 2026-2-7 13:45 | 只看該作者
ydatou 發(fā)表于 2026-2-6 14:12
下面是ai分析的結(jié)果,雖不完全贊同,但部分是正確的。
你希望我分析這段按鍵采樣代碼的邏輯,并結(jié)合 20ms  ...

這個(gè)ai的說(shuō)法不太對(duì),因?yàn)橐粋(gè)要實(shí)現(xiàn)單擊雙擊三擊的按鍵處理函數(shù),等待下次按鍵輸入的時(shí)間是必須的,不可能輸入單擊不等待就直接輸出。而傳統(tǒng)按鍵狀態(tài)機(jī)確實(shí)可以減少在單擊輸入下的等待時(shí)間,比如單擊后等待300ms,雙擊后再等待300ms。但是你可以看看我更新后的另一條評(píng)論,已經(jīng)實(shí)現(xiàn)了超時(shí)退出,在實(shí)時(shí)性上已經(jīng)不輸了。
至于這個(gè)ai說(shuō)的什么用戶習(xí)慣,完全狗屁不通,兩次單擊和一次雙擊完全是同一個(gè)事件,哪里來(lái)的習(xí)慣不同一說(shuō)?
至于采樣窗口固定?你可以調(diào)整定時(shí)器中斷間隔,實(shí)際上30ms是最合適的,結(jié)合我新增加的超時(shí)退出,實(shí)時(shí)性也是很好的。
總之,各個(gè)方案有各個(gè)方案的優(yōu)勢(shì),我的采樣32次的方法,其實(shí)就是從另一個(gè)角度去看待按鍵輸入。
回復(fù)

使用道具 舉報(bào)

13#
ID:1155837 發(fā)表于 2026-2-8 13:49 | 只看該作者
千早愛(ài)音愛(ài)玩51 發(fā)表于 2026-2-6 20:29
更新一下,相比傳統(tǒng)switch狀態(tài)機(jī),目前來(lái)說(shuō)固定32次采樣確實(shí)不夠靈活,因此我簡(jiǎn)單修改了一下,實(shí)現(xiàn)了按鍵輸 ...

其實(shí)超時(shí)后沒(méi)必要對(duì)數(shù)據(jù)全部補(bǔ)1,直接輸出就行了,默認(rèn)全是0
Sampling_Temp = (Sampling_Temp << (32-Sampling_Counter)) | ((1UL << (32-Sampling_Counter)) - 1);
這行可以刪掉,不需要這個(gè)計(jì)算
回復(fù)

使用道具 舉報(bào)

14#
ID:344848 發(fā)表于 2026-2-9 17:22 | 只看該作者
對(duì)于愛(ài)好者提高個(gè)人掌握單片機(jī)技能來(lái)說(shuō),有一定的參考價(jià)值,但對(duì)于實(shí)際工程來(lái)說(shuō),意義不大,對(duì)于每個(gè)實(shí)際用戶來(lái)說(shuō),每個(gè)用戶認(rèn)為長(zhǎng)、短時(shí)間實(shí)際有相當(dāng)大的差異。有雷同的功能產(chǎn)品在市場(chǎng)有一款產(chǎn)品:某廠家的溫度控制器,為了壓低產(chǎn)品價(jià)格和追求小體積。減少按鍵個(gè)數(shù),廠家將兩類不同功能合并為一鍵來(lái)實(shí)現(xiàn),即長(zhǎng)按鍵和短按鍵來(lái)實(shí)現(xiàn),通過(guò)顯示功能的來(lái)確定用戶選擇。短按鍵是普通用戶需要調(diào)整的功能,長(zhǎng)按鍵是提高產(chǎn)品性能的高級(jí)用戶使用——這些功能普通用戶不常使用。
回復(fù)

使用道具 舉報(bào)

15#
ID:1155837 發(fā)表于 2026-2-12 02:07 | 只看該作者
本帖最后由 千早愛(ài)音愛(ài)玩51 于 2026-2-13 00:37 編輯

突然發(fā)現(xiàn)如果用我的采樣法按鍵處理來(lái)做開(kāi)關(guān)機(jī)的話,有一個(gè)bug(劃掉),或者說(shuō)特性。
這個(gè)狀態(tài)機(jī),進(jìn)行按鍵采樣的條件必須是有下降沿,而通過(guò)外部中斷喚醒后才開(kāi)始檢測(cè),單片機(jī)檢測(cè)到的Nowkey和previouskey都是低電平,也就是沒(méi)有下降沿,這導(dǎo)致無(wú)法真正的檢測(cè)按鍵,導(dǎo)致超時(shí)關(guān)機(jī)。
而只要單擊后長(zhǎng)按,就可以給一個(gè)下降沿,開(kāi)始采樣,最后得到長(zhǎng)按信號(hào)。
下面是休眠的測(cè)試代碼,用到了TM1650來(lái)顯示參數(shù),沒(méi)有的話一個(gè)LED就夠了、#include "STC8G.H"
#include "intrins.H"
#include "TM1650.H"
unsigned long Main_FOSC = 6000000L;
bit poweron = 1;
bit was_enter_sleep = 0;
bit need_wakeup_sampling = 0;
void IO_CONFIG(void){
//IO模式設(shè)置
//P31電源開(kāi)關(guān)開(kāi)漏,低電平開(kāi)
//P32外部按鍵高阻,啟用內(nèi)部上拉
//P33LEDPWM信號(hào)輸出,推挽
//P54風(fēng)扇開(kāi)關(guān)信號(hào)輸出推挽,高電平開(kāi)
//P55NTC電源開(kāi)關(guān)推挽,低電平開(kāi),同時(shí)控制ETA9741的按鍵,開(kāi)漏
    P_SW2 = 0X80;//允許訪問(wèn)擴(kuò)展寄存器(設(shè)置上拉,轉(zhuǎn)換速度等擴(kuò)展寄存器)
    P3M1 = 0XF7;//P30高阻,P31開(kāi)漏,P32高阻,P33推挽
    P3M0 = 0X0A;
    P5M1 = 0XEF;//P54推挽,P55開(kāi)漏
    P5M0 = 0X30;
    P3SR = 0XF7;//P33引腳電平轉(zhuǎn)換速度為快
    P3DR = 0XF7;//P33引腳驅(qū)動(dòng)電流增強(qiáng)
//省電設(shè)置//
//高阻輸入的引腳如果無(wú)上拉下拉,電平不確定就會(huì)導(dǎo)致漏電
    P3IE = 0X05;//P3只啟用P30和P32的數(shù)字輸入功能,允許讀取外部電平,防止休眠漏電
    P5IE = 0XFF;//P5全部關(guān)閉數(shù)字輸入,不需要讀取外部電平
//規(guī)定引腳初始電平//
    P31 = 1;//打開(kāi)電源
    P33 = 0;//初始化為關(guān)閉LED
    P54 = 0;//N管高電平開(kāi)
    P55 = 1;//P管低電平開(kāi)
}
void TIMER_INIT(void){
    AUXR &= 0x7F;
    TCON = 0X01;//中斷標(biāo)志清零,外部中斷0設(shè)置為下降沿觸發(fā)。
    TMOD = 0X00;//00000000,GATE置零,TR=1就開(kāi)始計(jì)數(shù),T_CT=0,作為定時(shí)器,T0T1均十六位自動(dòng)重載
    //AUXR輔助寄存器不用配置,復(fù)位值就是12T模式,即系統(tǒng)時(shí)鐘12分頻
    //定時(shí)器周期配置
    //定時(shí)器0,10ms一次溢出中斷
    TL0 = 0x68;                //30ms@6MHZ
    TH0 = 0xC5;               
    TF0 = 1;
    //定時(shí)器1,125ms一次溢出中斷
    TH1 = 0X0B;
    TL1 = 0XDC;
    TF1 = 0;
    //STC8G1K08A SOP8只有定時(shí)器0和1兩個(gè)定時(shí)器
}
unsigned char Rise_Edge_Counter(unsigned long Input){
  unsigned long Mask;//掩碼
    Mask = ~Input & (Input << 1);
    Mask = (Mask & 0x55555555) + ((Mask >> 1) & 0x55555555);
    Mask = (Mask & 0x33333333) + ((Mask >> 2) & 0x33333333);
    Mask = (Mask + (Mask >> 4)) & 0x0F0F0F0F;
    Mask = Mask + (Mask >> 8);
    Mask = Mask + (Mask >> 16);
    Mask = Mask & 0x0000003F;
    return Mask;
}
#define NOKEY 0
#define SINGLEKEY 1
#define DOUBLEKEY 2
#define TRIPLEKEY 3
#define QUADRAKEY 4
#define LONGKEY 5
unsigned char Key_Event = NOKEY;
void Key_Sampling(void){//傳入需要進(jìn)行采樣的引腳
  //實(shí)際上本函數(shù)自帶消抖,因?yàn)椴蛔阋粋(gè)定時(shí)器間隔的下降沿?zé)o法觸發(fā)采樣
  //在20ms以上的定時(shí)器中斷中調(diào)用本函數(shù),推薦30ms一次中斷
  static unsigned char Sampling_Counter = 0;//采樣次數(shù)計(jì)數(shù)器
  static unsigned long Sampling_Temp = 0;//采樣結(jié)果緩存
  static bit Previous_Key = false;//之前的按鍵采樣記錄
  static bit Now_Key = false;//當(dāng)前的按鍵采樣記錄
  static bit Is_Sampling = false;//正在采樣標(biāo)志
  static bit Start_Sampling = false;//開(kāi)始采樣標(biāo)志,由下降沿觸發(fā)
  static unsigned char Key_Release_Counter = 0;//超時(shí)計(jì)數(shù)器,可以更快的響應(yīng)單擊和雙擊操作
  unsigned char Rise_Edge_Result = 0;//上升沿計(jì)算的結(jié)果,非static變量,重入自動(dòng)清零
  bit Read_Temp = P32;//按鍵狀態(tài)緩存,每次進(jìn)入函數(shù)時(shí)讀取
  //采樣觸發(fā)(檢測(cè)下降沿)
  if(!Is_Sampling){//如果沒(méi)有在采樣,才做判斷
  Previous_Key = Now_Key;//存儲(chǔ)上一次的結(jié)果
  Now_Key = Read_Temp;//讀取本次結(jié)果
  //Key_Event = NOKEY;//未在采樣狀態(tài),全局按鍵事件為無(wú)鍵
  if((!Now_Key && Previous_Key) || need_wakeup_sampling){//上一次為1,這一次為0,是下降沿
  Start_Sampling = true;//開(kāi)始采樣
  }
  else{
    return;}//未觸發(fā)采樣,直接返回
  }
  //開(kāi)始采樣與結(jié)果處理
  if(Start_Sampling){
    if(Sampling_Counter < 32){
      Is_Sampling = true;
      Sampling_Temp <<= 1;//整體左移一位,低位補(bǔ)0
      if (Read_Temp){//采樣引腳,如果為高電平,最低位置1
        Sampling_Temp |= 0x01;//如果引腳為高才計(jì)1,如果為低就繼續(xù)左移一位,也就是補(bǔ)0
        Key_Release_Counter++;
        if(Key_Release_Counter >= 12){//超過(guò)240ms沒(méi)有按鍵輸入
        Key_Release_Counter = 0;
        //采樣超時(shí),提前結(jié)束,未采樣的部分全部補(bǔ)1
        Sampling_Temp = (Sampling_Temp << (32-Sampling_Counter)) | ((1UL << (32-Sampling_Counter)) - 1);
        Sampling_Counter = 32;//標(biāo)記為采樣結(jié)束
        }
        }
        else{//如果按鍵為低電平
        Key_Release_Counter = 0;
        }
        Sampling_Counter++;
        }
      else{//采滿32次,采樣結(jié)束,開(kāi)始計(jì)算
        Is_Sampling = false;
        Start_Sampling = false;
        need_wakeup_sampling = false;
        Sampling_Counter = 0;//清空計(jì)數(shù)器
        Rise_Edge_Result = Rise_Edge_Counter(Sampling_Temp);//計(jì)算上升沿?cái)?shù)量
        switch(Rise_Edge_Result){
          case 0: Key_Event = LONGKEY;  break;
          case 1: Key_Event = SINGLEKEY;break;
          case 2: Key_Event = DOUBLEKEY;break;
          case 3: Key_Event = TRIPLEKEY;break;
          case 4: Key_Event = QUADRAKEY;break;
        }
        }
  }
}
volatile unsigned char sleep_timer = 0;//睡眠計(jì)時(shí)器,用于避免誤觸喚醒
bit wakeup_flag = 1;
bit backsleep_flag = 0;
void ENTER_SLEEP(void){
    while(1){//外層while
    TR0 = 0;
    P33 = 0;
    TM1650_Init(0,0);
    sleep_timer = 0;
    poweron = false;
    Key_Event = NOKEY;
    was_enter_sleep = true;
    PCON = 0X02;//進(jìn)入掉電模式
/////喚醒后從這里繼續(xù)執(zhí)行/////
    _nop_();_nop_();_nop_();_nop_();//空指令,避免CPU上電后的不穩(wěn)定
    TR0 = 1;//打開(kāi)定時(shí)器0,開(kāi)始計(jì)時(shí),在中斷中執(zhí)行按鍵檢測(cè)
    TM1650_Init(0,4);
    TM1650_Display_Word("FUnC");
    while(!poweron){//內(nèi)層while
        WDT_CONTR |= 0X35;//喂狗
        if(Key_Event ==LONGKEY){
            Key_Event = NOKEY;
            poweron ^= 1;}
        if(backsleep_flag){
            backsleep_flag = 0;
            break;//跳出內(nèi)層while,重新執(zhí)行休眠。
        }
    }
    if(poweron){
        break;//跳出外層while
    }
    }
/////從這里開(kāi)始執(zhí)行恢復(fù)程序/////
    TR0 = 1;
    P33 = 1;
    TM1650_Display_Word("On  ");
}
void TIMER0_ROUTINE(void) interrupt 1 {//定時(shí)器0 30ms中斷服務(wù)函數(shù)
    Key_Sampling();
    if(!poweron && sleep_timer <= 63){//誤喚醒超時(shí)檢測(cè)
        sleep_timer++;}
    else if(!poweron && sleep_timer > 63){
        sleep_timer = 0;//超過(guò)1600ms,清空狀態(tài),重新睡眠
        backsleep_flag = 1;
    }
    }
void INT0_ROUTINE(void) interrupt 0 {//定時(shí)器1 125ms中斷服務(wù)函數(shù)
    if(was_enter_sleep){
        need_wakeup_sampling = true;
        was_enter_sleep = false;
    }
    }
void main(void){
    unsigned int a = 0;
    IO_CONFIG();
    TIMER_INIT();
    TM1650_PinSet(54,55);
    TM1650_Init(100,4);
    TR0 = 1;
    WDT_CONTR = 0X25;
    IE = 0X8B;
    while(1){
        a = 65535;
        while(a--);
        if(!poweron){
            ENTER_SLEEP();
        }
        if(Key_Event ==LONGKEY){
            TM1650_Display_Word("ToSL");
            Key_Event = NOKEY;
            poweron ^= 1;
        }
        else{
        TM1650_Display_Num(Key_Event,0);
        Key_Event = NOKEY;
        }
    }
}
回復(fù)

使用道具 舉報(bào)

16#
ID:1155837 發(fā)表于 2026-2-12 02:38 | 只看該作者
tips:上面的tm1650驅(qū)動(dòng)庫(kù),點(diǎn)進(jìn)我的主頁(yè)找相關(guān)帖子里有
如果你真的不需要單擊后休眠喚醒的話,也是很容易解決的。只需要增加兩個(gè)變量即可。
bit was_enter_sleep = false;
bit need_wakeup_sampling = false;
在休眠時(shí)設(shè)置 was_enter_sleep =true;
在喚醒時(shí)int0中斷服務(wù)函數(shù)中檢測(cè)flag:
void INT0_ROUTINE(void) interrupt 0 {//定時(shí)器1 125ms中斷服務(wù)函數(shù)
    if(was_enter_sleep){
        need_wakeup_sampling = true;
        was_enter_sleep = false;
    }
    }
在采樣函數(shù)中增加額外條件:
  if((!Now_Key && Previous_Key) || need_wakeup_sampling){//上一次為1,這一次為0,是下降沿
  Start_Sampling = true;//開(kāi)始采樣
  }
然后在采樣結(jié)束后清零need flag即可。
如此以來(lái)就可以規(guī)避單擊后長(zhǎng)按喚醒。
但我真的不推薦你這么搞,單擊后長(zhǎng)按喚醒更穩(wěn)定而且不容易誤觸。
說(shuō)句題外話,這個(gè)代碼在51上編譯大概需要800字節(jié) ,因?yàn)榇罅渴褂昧藆long 32位量,51是不支持直接計(jì)算32位變量的,都是軟件模擬的,所以尺寸大,速度也慢(即使使用了swar算法,24mhz下依然需要50us才能算完上升沿)
但是一旦你將眼光投到32單片機(jī)上就會(huì)發(fā)現(xiàn),32單片機(jī)天生支持32位 移位運(yùn)算,和32位邏輯運(yùn)算,不需要多行匯編模擬,因此速度會(huì)快的驚人,十幾個(gè)時(shí)鐘周期就能算完上升沿。
但你按鍵數(shù)量多了之后就會(huì)發(fā)現(xiàn)本庫(kù)函數(shù)的優(yōu)點(diǎn),變量都集中在一個(gè)函數(shù)中,僅僅一個(gè)簡(jiǎn)單的結(jié)構(gòu)體,就可以無(wú)限擴(kuò)展按鍵,每個(gè)按鍵只需要多9字節(jié)內(nèi)存(全部變量占用,當(dāng)然,在51上使用bit變量更節(jié)省),無(wú)疑是相當(dāng)有優(yōu)勢(shì)的。
回復(fù)

使用道具 舉報(bào)

17#
ID:1155837 發(fā)表于 2026-2-21 01:55 | 只看該作者
我發(fā)現(xiàn)這個(gè)代碼在30ms定時(shí)器中斷的情況下無(wú)法穩(wěn)定運(yùn)行,容易出現(xiàn)將單擊誤判為長(zhǎng)按的問(wèn)題,但在20ms下完全不會(huì)有問(wèn)題,但是不知道為什么,我在arduino esp32上始終無(wú)法復(fù)現(xiàn)這個(gè)問(wèn)題,可能是和51單片機(jī)有關(guān)系。但是至少20ms下是完全穩(wěn)定可用的
回復(fù)

使用道具 舉報(bào)

18#
ID:1155837 發(fā)表于 2026-2-21 18:03 | 只看該作者
好吧,我已經(jīng)找到了問(wèn)題原因,是之前加入的提前退出功能導(dǎo)致的
我之前寫(xiě)的是這樣
Sampling_Temp = (Sampling_Temp << (32-Sampling_Counter)) | ((1UL << (32-Sampling_Counter)) - 1);
這導(dǎo)致其實(shí)多左移了一位,如果按鍵低電平時(shí)間持續(xù)很短的話,采樣結(jié)果為01111111...,再執(zhí)行這個(gè)就會(huì)導(dǎo)致丟失最高位,輸出0xffffffff,進(jìn)而導(dǎo)致上升沿?cái)?shù)量為0。
將這里改成31,問(wèn)題就可以完全解決了。
回復(fù)

使用道具 舉報(bào)

19#
ID:1084416 發(fā)表于 2026-3-5 10:54 | 只看該作者
感謝分享思路
回復(fù)

使用道具 舉報(bào)

20#
ID:1084416 發(fā)表于 2026-3-5 11:22 | 只看該作者
感謝分享思路
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

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

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

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