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

QQ登錄

只需一步,快速開始

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

基于狀態(tài)機(jī)的按鍵長(zhǎng)按,短按,雙擊 單片機(jī)源程序

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:573901 發(fā)表于 2019-8-23 14:41 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
先前根據(jù)以為前輩的程序理解了一段時(shí)間,之后自己寫了一份控制led燈的簡(jiǎn)單按鍵程序

單片機(jī)源程序如下:
  1. #include<stc15w202s.h>
  2. #include<stdio.h>
  3. #define key_state_0 0
  4. #define key_state_1 1
  5. #define key_state_2 2
  6. #define key_state_3 3
  7. #define key_no 0
  8. #define key_click 1
  9. #define key_double 2
  10. #define key_long 3
  11. sbit KEY = P3^3;
  12. sbit LED_E1P = P3^1;
  13. sbit LED_G1 = P3^2;
  14. sbit LED2 = P5^5;        //W2OUT
  15. sbit LED3 = P5^4;  
  16. unsigned char flag;
  17. unsigned char cnt = 0;


  18. static unsigned char key_driver(void)
  19. {
  20.         static unsigned char key_state_buffer1 = key_state_0;
  21.         static unsigned char key_timer_cnt1 = 0;
  22.         unsigned char key_return = key_no;
  23.         unsigned char key;
  24.        
  25.         key = KEY;  //read the I/O states
  26.        
  27.         switch(key_state_buffer1)
  28.         {
  29.                 case key_state_0:
  30.                         if(key == 0)
  31.                                 key_state_buffer1 = key_state_1;
  32.                                 //按鍵被按下,狀態(tài)轉(zhuǎn)換到按鍵消抖和確認(rèn)狀態(tài)//
  33.                         break;
  34.                 case key_state_1:
  35.                         if(key == 0)
  36.                         {
  37.                                 key_timer_cnt1 = 0;
  38.                                 key_state_buffer1 = key_state_2;
  39.                                 //按鍵仍然處于按下狀態(tài)
  40.                                 //消抖完成,key_timer開始準(zhǔn)備計(jì)時(shí)
  41.                                 //狀態(tài)切換到按下時(shí)間計(jì)時(shí)狀態(tài)
  42.                         }
  43.                         else
  44.                                 key_state_buffer1 = key_state_0;
  45.                                 //按鍵已經(jīng)抬起,回到按鍵初始狀態(tài)
  46.                         break;  //完成軟件消抖
  47.                 case key_state_2:
  48.                         if(key == 1)
  49.                         {
  50.                                 key_return = key_click;  //按鍵抬起,產(chǎn)生一次click操作
  51.                                 key_state_buffer1 = key_state_0;  //轉(zhuǎn)換到按鍵初始狀態(tài)
  52.                         }
  53.                         else if(++key_timer_cnt1 >= 100)  //按鍵繼續(xù)按下,計(jì)時(shí)超過1000ms
  54.                         {
  55.                                 key_return = key_long;  //送回長(zhǎng)按事件
  56.                                 key_state_buffer1 = key_state_3;  //轉(zhuǎn)換到等待按鍵釋放狀態(tài)
  57.                         }
  58.                         break;
  59.                 case key_state_3:  //等待按鍵釋放
  60.                         if(key == 1)  //按鍵釋放
  61.                                 key_state_buffer1 = key_state_0;  //切回按鍵初始狀態(tài)
  62.                         break;
  63.         }
  64.         return key_return;
  65. }

  66. unsigned char key_read(void)
  67. {
  68.         static unsigned char key_state_buffer2 = key_state_0;
  69.         static unsigned char key_timer_cnt2 = 0;
  70.         unsigned char key_return = key_no;
  71.         unsigned char key;
  72.        
  73.         key = key_driver();
  74.        
  75.         switch(key_state_buffer2)
  76.         {
  77.                 case key_state_0:
  78.                         if(key == key_click)
  79.                         {
  80.                                 key_timer_cnt2 = 0;  //第一次單擊,不返回,到下個(gè)狀態(tài)判斷是否會(huì)出現(xiàn)雙擊
  81.                                 key_state_buffer2 = key_state_1;
  82.                         }
  83.                         else
  84.                                 key_return = key;  //對(duì)于無鍵、長(zhǎng)按,返回原事件
  85.                         break;
  86.                 case key_state_1:
  87.                         if(key == key_click)  //又一次單擊,時(shí)間間隔小于500ms
  88.                         {
  89.                                 key_return = key_double;  //返回雙擊事件,回到初始狀態(tài)
  90.                                 key_state_buffer2 = key_state_0;
  91.                         }
  92.                         else if(++key_timer_cnt2 >= 50)
  93.                         {   
  94.                         //這里在下一次的按鍵來臨之前,并且時(shí)間是小于500ms的時(shí)候,就會(huì)一直執(zhí)行的是這個(gè)key_timer_cnt2++.直到下一次的按鍵到來,再判斷看是雙擊還是單擊。
  95.                                 //這里500ms內(nèi)肯定讀到的都是無鍵事件,因?yàn)殚L(zhǎng)按大于1000ms
  96.                                 //在1s前底層返回的都是無鍵
  97.                                                                        
  98.                                 key_return = key_click;  //500ms內(nèi)沒有再次出現(xiàn)單擊事件,返回單擊事件
  99.                                 key_state_buffer2 = key_state_0;  //返回初始狀態(tài)
  100.                                        
  101.                         }
  102.                         break;
  103.         }
  104.        
  105.         return key_return;
  106. }

  107. void Timer0Init(void)                //1毫秒@11.0592MHz
  108. {
  109.         AUXR |= 0x80;                //定時(shí)器時(shí)鐘1T模式
  110.         TMOD &= 0xF0;                //設(shè)置定時(shí)器模式
  111.         TL0 = 0xCD;                //設(shè)置定時(shí)初值
  112.         TH0 = 0xD4;                //設(shè)置定時(shí)初值
  113.         TF0 = 0;                //清除TF0標(biāo)志
  114.         TR0 = 1;                //定時(shí)器0開始計(jì)時(shí)
  115.         ET0 = 1;

  116. }
  117. void IO_init()
  118. {
  119.    
  120.     P3M0 = 0x01;
  121.     P3M1 = 0x01;
  122.     P5M0 = 0x00;
  123.     P5M1 = 0x00;
  124.    
  125. }


  126. void main(void)
  127. {          
  128.        unsigned char key = 1;
  129.              Timer0Init();
  130.            IO_init();
  131.            LED_E1P = 1;
  132.            LED_G1 = 1;
  133.            LED2 = 1;
  134.            LED3 = 0;
  135.                    EA = 1;
  136.            while(1)
  137.            {
  138.              if(flag)
  139.                  {
  140.                    flag=0;
  141.            key = key_read();
  142.                    switch(key)
  143.                    {
  144.                             case key_click:LED2 = !LED2; break;
  145.                          case key_double:LED2 = !LED2;LED3 = !LED3; break;
  146.                          case key_long: LED2 = 1; LED3 = 1; LED_G1 = 0; LED_E1P = 0; break;
  147.                          default : break;
  148.                    }
  149.                  }
  150.            }
  151. }

  152. void Timr0_ISR() interrupt 1
  153. {
  154.    cnt++;
  155.    if(cnt>=10)
  156.    {
  157.        cnt = 0;
  158.               flag = 1;
  159.    }
  160. }
復(fù)制代碼

以上資料51hei提供下載:
基于狀態(tài)機(jī)的按鍵長(zhǎng)按短按雙擊控制程序.zip (1.87 KB, 下載次數(shù): 367)


評(píng)分

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

查看全部評(píng)分

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

使用道具 舉報(bào)

沙發(fā)
ID:405604 發(fā)表于 2020-1-13 16:51 | 只看該作者
程序?qū)懙姆浅M昝,沒毛病。提一個(gè)存在的小問題,在定時(shí)器中斷函數(shù)Timr0_ISR()里面要重裝定時(shí)器初值,不然主函數(shù)第一次初始化定時(shí)器以后定時(shí)器溢出,會(huì)從0~65535計(jì)數(shù),并且一直這樣,這樣的cnt++就不能計(jì)時(shí)10ms了,導(dǎo)致前面的按鍵都不能用,以上本人STC89C52親測(cè),這個(gè)問題頭疼了2天后來發(fā)現(xiàn)是沒有重裝初值的問題。其他不懂也沒學(xué)過!希望后來的朋友不要走彎路,樓主加油!
回復(fù)

使用道具 舉報(bào)

板凳
ID:377382 發(fā)表于 2020-2-9 17:22 | 只看該作者
51需要改不少的地方
回復(fù)

使用道具 舉報(bào)

地板
ID:334781 發(fā)表于 2020-2-15 21:20 | 只看該作者
謝謝樓主,下載試一下。對(duì)于按鍵的長(zhǎng)按與短按,一直是困或我的問題,先謝謝了!
回復(fù)

使用道具 舉報(bào)

5#
ID:465279 發(fā)表于 2020-3-7 12:39 | 只看該作者
wxqhigh 發(fā)表于 2020-1-13 16:51
程序?qū)懙姆浅M昝溃瑳]毛病。提一個(gè)存在的小問題,在定時(shí)器中斷函數(shù)Timr0_ISR()里面要重裝定時(shí)器初值,不然 ...

仔細(xì)看了代碼并測(cè)試,回復(fù)沒毛病。正在學(xué)習(xí)按鍵處理這塊,向你學(xué)習(xí)。
回復(fù)

使用道具 舉報(bào)

6#
ID:448556 發(fā)表于 2020-3-10 15:48 | 只看該作者
是啊不重裝初值 定時(shí)中斷只運(yùn)行一次就停止了
回復(fù)

使用道具 舉報(bào)

7#
ID:573901 發(fā)表于 2020-6-1 10:37 | 只看該作者
wxqhigh 發(fā)表于 2020-1-13 16:51
程序?qū)懙姆浅M昝,沒毛病。提一個(gè)存在的小問題,在定時(shí)器中斷函數(shù)Timr0_ISR()里面要重裝定時(shí)器初值,不然 ...

這個(gè)是stc15w系列的單片機(jī),定時(shí)器用的是16位自動(dòng)重裝載,所以不需要從新賦初值了,不過還是謝謝提醒,一起加油!
回復(fù)

使用道具 舉報(bào)

8#
ID:573901 發(fā)表于 2020-6-1 10:39 | 只看該作者
liyezhao2019 發(fā)表于 2020-3-10 15:48
是啊不重裝初值 定時(shí)中斷只運(yùn)行一次就停止了

嗯嗯 ,怪我沒注意,51系列的要裝初值,我這個(gè)是stc系列的芯片,定時(shí)器0是16位自動(dòng)重裝載的,一起加油
回復(fù)

使用道具 舉報(bào)

9#
ID:573901 發(fā)表于 2020-6-1 10:42 | 只看該作者

嗯,是的,手上沒有51的板子,所以用了stc的程序?qū)懙模贿^這個(gè)按鍵的狀態(tài)機(jī)是可以用的,其他注意51的重裝初值問題。
回復(fù)

使用道具 舉報(bào)

10#
ID:389488 發(fā)表于 2020-9-22 15:27 | 只看該作者
wxqhigh 發(fā)表于 2020-1-13 16:51
程序?qū)懙姆浅M昝,沒毛病。提一個(gè)存在的小問題,在定時(shí)器中斷函數(shù)Timr0_ISR()里面要重裝定時(shí)器初值,不然 ...

stc15不用重裝初值
回復(fù)

使用道具 舉報(bào)

11#
ID:610086 發(fā)表于 2020-11-27 10:59 | 只看該作者
單擊的時(shí)候500ms檢測(cè),會(huì)不會(huì)有些卡頓感?
回復(fù)

使用道具 舉報(bào)

12#
ID:367948 發(fā)表于 2021-2-18 12:04 | 只看該作者
謝謝樓主,正需要單片機(jī)檢測(cè)連續(xù)按下按鍵程序
回復(fù)

使用道具 舉報(bào)

13#
ID:275826 發(fā)表于 2021-2-26 18:05 | 只看該作者
程序可以,就是太復(fù)雜了,可以簡(jiǎn)化很多
回復(fù)

使用道具 舉報(bào)

14#
ID:508724 發(fā)表于 2021-3-10 09:51 | 只看該作者
這個(gè)狀態(tài)機(jī)寫的很不錯(cuò),nice
回復(fù)

使用道具 舉報(bào)

15#
ID:430492 發(fā)表于 2021-3-23 11:40 | 只看該作者
寫得不錯(cuò),是個(gè)很好的參考例程。
回復(fù)

使用道具 舉報(bào)

16#
ID:839438 發(fā)表于 2021-6-3 21:38 | 只看該作者
謝謝,正在學(xué)習(xí)狀態(tài)機(jī)
回復(fù)

使用道具 舉報(bào)

17#
ID:935260 發(fā)表于 2021-6-10 10:03 | 只看該作者
針對(duì)特定的單片機(jī)寫程序和通用的單片機(jī)程序,還是有差別
回復(fù)

使用道具 舉報(bào)

18#
ID:601234 發(fā)表于 2021-10-20 16:53 | 只看該作者
key_read  和  key_drive可以合并嗎
回復(fù)

使用道具 舉報(bào)

19#
ID:601234 發(fā)表于 2021-10-20 16:54 | 只看該作者
key_read和key_drive可以合并嗎
回復(fù)

使用道具 舉報(bào)

20#
ID:821788 發(fā)表于 2022-8-28 15:19 | 只看該作者
感謝!感謝!
key_read和key_drive可以嘗試合并,少用一個(gè)狀態(tài)機(jī)、少占用一個(gè)byte RAM
回復(fù)

使用道具 舉報(bào)

21#
ID:59300 發(fā)表于 2023-9-11 09:28 | 只看該作者
很好的文章,對(duì)我正在學(xué)習(xí)狀態(tài)機(jī)非常有幫助,謝謝
回復(fù)

使用道具 舉報(bào)

22#
ID:1127891 發(fā)表于 2024-7-2 14:35 | 只看該作者
樓主這么nb
回復(fù)

使用道具 舉報(bào)

23#
ID:786399 發(fā)表于 2024-7-2 21:11 | 只看該作者
受益匪淺
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

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

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

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