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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 22345|回復: 15
收起左側

最為精辟和實用的按鍵處理程序

  [復制鏈接]
ID:98238 發表于 2015-12-11 00:39 來自手機 | 顯示全部樓層 |閱讀模式
5黑幣
    最為精辟和實用的按鍵處理程序;1.新型的按鍵掃描程序;不過我在網上游逛了很久,也看過不少源程序了,沒有;同時,這里面用到了一些分層的思想,在單片機當中也;以下假設你懂C語言,因為純粹的C語言描述,所以和;核心算法:;unsignedcharTrg;;unsignedcharCont;;voidKeyRead(void);unsignedcharReadDat

      最為精辟和實用的按鍵處理程序

      1.新型的按鍵掃描程序

      不過我在網上游逛了很久,也看過不少源程序了,沒有發現這種按鍵處理辦法的蹤跡,所以,我將他共享出來,和廣大同僚們共勉。我非常堅信這種按鍵處理辦法的便捷和高效,你可以移植到任何一種嵌入式處理器上面,因為C語言強大的可移植性。

      同時,這里面用到了一些分層的思想,在單片機當中也是相當有用的,也是本文的另外一個重點。 對于老鳥,我建議直接看那兩個表達式,然后自己想想就會懂的了,也不需要聽我后面的自吹自擂了,我可沒有班門弄斧的意思,hoho~~但是對于新手,我建議將全文看完。因為這是實際項目中總結出來的經驗,學校里面學不到的東西。

      以下假設你懂C語言,因為純粹的C語言描述,所以和處理器平臺無關,你可以在MCS-51,AVR,PIC,甚至是ARM平臺上面測試這個程序性能。當然,我自己也是在多個項目用過,效果非常好的。 好了,工程人員的習慣,廢話就應該少說,開始吧。以下我以AVR的MEGA8作為平臺講解,沒有其它原因,因為我手頭上只有AVR的板子而已沒有51的。用51也可以,只是芯片初始化部分不同,還有寄存器名字不同而已。

      核心算法:

      unsigned char Trg;

      unsigned char Cont;

      void KeyRead( void )

      {

      unsigned char ReadData = PINB^0xff; // 1

      Trg = ReadData & (ReadData ^ Cont); // 2

      Cont = ReadData; // 3

      }

      完了。有沒有一種不可思議的感覺?當然,沒有想懂之前會那樣,想懂之后就會驚嘆于這算法的精妙!! 下面是程序解釋:

      Trg(triger) 代表的是觸發,Cont(continue)代表的是連續按下。

      1:讀PORTB的端口數據,取反,然后送到ReadData 臨時變量里面保存起來。

      2:算法1,用來計算觸發變量的。一個位與操作,一個異或操作,我想學過C語言都應該懂吧?Trg為全局變量,其它程序可以直接引用。

      3:算法2,用來計算連續變量。

      看到這里,有種“知其然,不知其所以然”的感覺吧?代碼很簡單,但是它到底是怎么樣實現我們的目的的呢?好,下面就讓我們繞開云霧看青天吧。

      我們最常用的按鍵接法如下:AVR是有內部上拉功能的,但是為了說明問題,我是特意用外部上拉電阻。那么,按鍵沒有按下的時候,讀端口數據為1,如果按鍵按下,那么端口讀到0。下面就看看具體幾種情況之下,這算法是怎么一回事。

      (1) 沒有按鍵的時候

      端口為0xff,ReadData讀端口并且取反,很顯然,就是 0x00 了。

      Trg = ReadData & (ReadData ^ Cont); (初始狀態下,Cont也是為0的)很簡單的數學計算,因為ReadData為0,則它和任何數“相與”,結果也是為0的。

      Cont = ReadData; 保存Cont 其實就是等于ReadData,為0;

      結果就是:

      ReadData = 0;

      Trg = 0;

      Cont = 0;

      (2) 第一次PB0按下的情況

      端口數據為0xfe,ReadData讀端口并且取反,很顯然,就是 0x01 了。

      Trg = ReadData & (ReadData ^ Cont); 因為這是第一次按下,所以Cont是上次的值,應為為0。那么這個式子的值也不難算,也就是 Trg = 0x01 & (0x01^0x00) = 0x01

      Cont = ReadData = 0x01;

      結果就是:

      ReadData = 0x01;

      Trg = 0x01;Trg只會在這個時候對應位的值為1,其它時候都為0

      Cont = 0x01;

      (3) PB0按著不松(長按鍵)的情況

      端口數據為0xfe,ReadData讀端口并且取反是 0x01 了。

      Trg = ReadData & (ReadData ^ Cont); 因為這是連續按下,所以Cont是上次的值,應為為0x01。那么這個式子就變成了 Trg = 0x01 & (0x01^0x01) = 0x00

      Cont = ReadData = 0x01;

      結果就是:

      ReadData = 0x01;

      Trg = 0x00;

      Cont = 0x01;

      因為現在按鍵是長按著,所以MCU會每個一定時間(20ms左右)不斷的執行這個函數,那么下次執行的時候情況會是怎么樣的呢?

      ReadData = 0x01;這個不會變,因為按鍵沒有松開

      Trg = ReadData & (ReadData ^ Cont) = 0x01 & (0x01 ^ 0x01) = 0 ,只要按鍵沒有松開,這個Trg值永遠為 0 !!!

      Cont = 0x01;只要按鍵沒有松開,這個值永遠是0x01!!

      (4) 按鍵松開的情況

      端口數據為0xff,ReadData讀端口并且取反是 0x00 了。

      Trg = ReadData & (ReadData ^ Cont) = 0x00 & (0x00^0x01) = 0x00

      Cont = ReadData = 0x00;

      結果就是:

      ReadData = 0x00;

      Trg = 0x00;

      Cont = 0x00;

      很顯然,這個回到了初始狀態,也就是沒有按鍵按下的狀態。

      總結一下,不知道想懂了沒有?其實很簡單,答案如下:

      Trg 表示的就是觸發的意思,也就是跳變,只要有按鍵按下(電平從1到0的跳變),那么Trg在對應按鍵的位上面會置一,我們用了PB0則Trg的值為0x01,類似,如果我們PB7按下的話,Trg 的值就應該為 0x80 ,這個很好理解,還有,最關鍵的地方,Trg 的值每次按下只會出現一次,然后立刻被清除,完全不需要人工去干預。所以按鍵功能處理程序不會重復執行,省下了一大堆的條件判斷,這個可是精粹哦!!Cont代表的是長按鍵,如果PB0按著不放,那么Cont的值就為 0x01,相對應,PB7按著不放,那么Cont的值應該為0x80,同樣很好理解。

      如果還是想不懂的話,可以自己演算一下那兩個表達式,應該不難理解的。

      因為有了這個支持,那么按鍵處理就變得很爽了,下面看應用:

      應用一:一次觸發的按鍵處理

      假設PB0為蜂鳴器按鍵,按一下,蜂鳴器beep的響一聲。這個很簡單,但是大家以前是怎么做的呢?對比一下看誰的方便?

      #define KEY_BEEP 0x01

      void KeyProc(void)

      {

      if (Trg & KEY_BEEP) // 如果按下的是KEY_BEEP

      {

      Beep(); // 執行蜂鳴器處理函數

      }

      }

      怎么樣?夠和諧不?記得前面解釋說Trg的精粹是什么?精粹就是只會出現一次。所以你按下按鍵的話,Trg & KEY_BEEP 為“真”的情況只會出現一次,所以處理起來非常的方便,蜂鳴器也不會沒事亂叫,hoho~~~

      或者你會認為這個處理簡單,沒有問題,我們繼續。

      應用2:長按鍵的處理

      項目中經常會遇到一些要求,例如:一個按鍵如果短按一下執行功能A,如果長按2秒不放的話會執行功能B,又或者是要求3秒按著不放,計數連加什么什么的功能,很實際。不知道大家以前是怎么做的呢?我承認以前做的很郁悶。

      但是看我們這里怎么處理吧,或許你會大吃一驚,原來程序可以這么簡單

      這里具個簡單例子,為了只是說明原理,PB0是模式按鍵,短按則切換模式,PB1就是加,如果長按的話則連加(玩過電子表吧?沒錯,就是那個!)

      #define KEY_MODE 0x01 // 模式按鍵

      #define KEY_PLUS 0x02 // 加

      void KeyProc(void)

      {

      if (Trg & KEY_MODE) // 如果按下的是KEY_MODE,而且你常按這按鍵也沒有用,

  { //它是不會執行第二次的哦 , 必須先松開再按下

      Mode++; // 模式寄存器加1,當然,這里只是演示,你可以執行你想

      // 執行的任何代碼

      }

      if (Cont & KEY_PLUS) // 如果“加”按鍵被按著不放

      {

      cnt_plus++; // 計時

      if (cnt_plus > 100) // 20ms*100 = 2S 如果時間到

      {

      Func(); // 你需要的執行的程序

      }

      }

      }

      不知道各位感覺如何?我覺得還是挺簡單的完成了任務,當然,作為演示用代碼。

      應用3:點觸型按鍵和開關型按鍵的混合使用

      點觸形按鍵估計用的最多,特別是單片機。開關型其實也很常見,例如家里的電燈,那些按下就不松開,除非關。這是兩種按鍵形式的處理原理也沒啥特別,但是你有沒有想過,如果一個系統里面這兩種按鍵是怎么處理的?我想起了我以前的處理,分開兩個非常類似的處理程序,現在看起來真的是笨的不行了,但

      是也沒有辦法啊,結構決定了程序。不過現在好了,用上面介紹的辦法,很輕松就可以搞定。

      原理么?可能你也會想到,對于點觸開關,按照上面的辦法處理一次按下和長按,對于開關型,我們只需要處理Cont就OK了,為什么?很簡單嘛,把它當成是一個長按鍵,這樣就找到了共同點,屏蔽了所有的細節。程序就不給了,完全就是應用2的內容,在這里提為了就是說明原理~~

評分

參與人數 3黑幣 +11 收起 理由
13956466557 + 5 很給力!
秋雨綿綿 + 1 j絕對的好貼
提刀書生 + 5 很給力!

查看全部評分

回復

使用道具 舉報

ID:134830 發表于 2017-12-29 17:15 | 顯示全部樓層
必須贊一個,哇,想法賊厲害
回復

使用道具 舉報

ID:269228 發表于 2017-12-30 16:05 來自手機 | 顯示全部樓層
666摩拜大神
回復

使用道具 舉報

ID:157975 發表于 2017-12-31 09:33 | 顯示全部樓層
人才啊,佩服
回復

使用道具 舉報

ID:67356 發表于 2018-2-26 06:02 | 顯示全部樓層
雖然沒看懂,還是再堅持學習
回復

使用道具 舉報

ID:293729 發表于 2018-3-18 20:35 | 顯示全部樓層
不錯
這個是自己先寫的
unsigned char this_key1;
unsigned char this_key2;
unsigned char this_key3;
unsigned char fst_key;
unsigned char key_l1;
unsigned char key_l2;
/*******************************************************
*»ñè¡I/OμÄÖμ
*******************************************************/
unsigned char read_gpio(void)
{
        unsigned char ret;
       
        ret = 0;
        //Õaàïìí¼ó¶áè¡I/OμÄÖμμÃóï¾ä£¬
        //×¢òa£¬retÖDμÄêǰ′λμļùèô0λ¶Ôó|P0^0,μú7λ¶Ôó|P^7
        return ret;
}
/*******************************************************
*
*******************************************************/
void        key_read_io(void)
{
        unsigned char ret;
       
        ret = read_gpio();
        this_key2 = this_key1 & ret;
        this_key1 = ret;
        fst_key   = this_key3 & (~this_key2);
        this_key3 = this_key2;
        if((this_key2 & 0x01) > 0)
        {
                key_l1++;
        }
        else
        {
                key_l1 = 0;
        }
        if((this_key2 & 0x02) > 0)
        {
                key_l2++;               
        }
        else
        {
                key_l2 = 0;
        }       
}
回復

使用道具 舉報

ID:293729 發表于 2018-3-18 20:43 | 顯示全部樓層
這個程序的精元在于,在主程序中while(1)循環中會不斷的調用按鍵程序,好處,代碼太精簡,壞處是,對長按鍵處理有點欠缺,當主程序執行的非常快是, 對cnt_plus的要求就大了,可能要unsigned long int,否則會將短按鍵(動作相對遲緩一點的短按鍵)誤認為是長按鍵,所以,主程序在執行一個循環得有一個估算,否則很難確定 cnt_plus設定為多少
回復

使用道具 舉報

ID:312179 發表于 2018-4-20 09:26 | 顯示全部樓層
好貼子,有很大幫助
回復

使用道具 舉報

ID:312179 發表于 2018-4-25 10:19 | 顯示全部樓層
秋雨綿綿 發表于 2018-4-20 09:26
好貼子,有很大幫助

吊炸天的感覺,太牛了
回復

使用道具 舉報

ID:312179 發表于 2018-4-25 14:31 | 顯示全部樓層
liuqihui66 發表于 2018-3-18 20:43
這個程序的精元在于,在主程序中while(1)循環中會不斷的調用按鍵程序,好處,代碼太精簡,壞處是,對長按鍵 ...

正解,長按鍵確實有待研究
回復

使用道具 舉報

ID:324844 發表于 2018-7-5 18:34 | 顯示全部樓層
看不懂,但是很了不起,膜拜大神
回復

使用道具 舉報

ID:366893 發表于 2018-7-7 21:47 | 顯示全部樓層
很早就一直在用這個按鍵掃描了,的確很精辟!
回復

使用道具 舉報

ID:379784 發表于 2018-7-27 11:28 | 顯示全部樓層
很精辟的程序,說得也很清楚,大贊。話說不用消抖嗎?
回復

使用道具 舉報

ID:281021 發表于 2018-10-9 09:34 | 顯示全部樓層
路過,看看
回復

使用道具 舉報

ID:535834 發表于 2019-9-29 23:28 | 顯示全部樓層
學渣與學霸 發表于 2018-7-27 11:28
很精辟的程序,說得也很清楚,大贊。話說不用消抖嗎?

看懂就知道不用啦
回復

使用道具 舉報

ID:665950 發表于 2019-12-17 10:01 | 顯示全部樓層
很好!!!!
回復

使用道具 舉報

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

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 国产美女黄色片 | 天天天天天天操 | 色婷婷av99xx | 精品在线看 | 一级做a爰片性色毛片16 | 亚洲成人久久久 | 一区二区三区免费在线观看 | 欧美日韩在线一区二区 | 亚洲av毛片成人精品 | 精品国产99 | 二区在线观看 | 久久久久久免费毛片精品 | 欧美一区二区三区在线视频 | 鸳鸯谱在线观看高清 | 精品国产精品一区二区夜夜嗨 | 欧美一a| eeuss国产一区二区三区四区 | 国产精品久久久久久婷婷天堂 | www.99re| 伊人伊人伊人 | 国产wwwcom | 免费国产一区二区视频 | 亚洲第一av | 四虎影院在线观看免费视频 | 日韩欧美国产精品 | 欧美日韩中文字幕在线播放 | 久久久蜜桃一区二区人 | 国产福利91精品 | 国产精品免费一区二区三区四区 | 一区二区三区国产在线观看 | 人人干人人舔 | 中文字幕视频三区 | 中文字幕在线精品 | 欧美大片一区 | 久久精品日 | 天堂国产 | 中文字幕在线视频观看 | 精品久久九九 | 欧美1页| 国产视频一区二区 | 日本三级黄视频 |