欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 31460|回復: 16
打印 上一主題 下一主題
收起左側

行列掃描法,反轉法,狀態機法三種矩陣鍵盤掃描方法詳解 帶程序

  [復制鏈接]
跳轉到指定樓層
樓主
實現矩陣鍵盤掃描的三種方法(代碼見附件):
1.行列掃描法
2.反轉法
3.狀態機法(結合定時器中斷)
第一種和第二種方法的本質都是進行循環查詢,大量占用MCU的時間,效率比較低。初學者一般會學這兩種
第三種方法屬于狀態機方法,它是結合定時器中斷的,相比于前兩種采用延時函數的方法,大大提高了MCU的效率。
我們設計矩陣鍵盤的掃描函數時,要保證:既要及時的判斷按鍵是否被按下,又要讓MCU有時間去做其他的事。
代碼所對應的硬件電路:


完整的原理圖:
51start單片機開發板V3.0原理圖.pdf (805.5 KB, 下載次數: 88)


全部源碼下載:
矩陣鍵盤 反轉法.zip (55.58 KB, 下載次數: 220)

矩陣鍵盤 狀態機法.zip (40.12 KB, 下載次數: 243)

矩陣鍵盤(行列掃描法).zip (33.98 KB, 下載次數: 182)


意見整理:
這幾個方法其中包含的一些問題。(先說明一點,我只看了 “反轉法” 和 “狀態機” 法。)

“反轉法” 里面的松手檢測使用了 " while(...) " 這種方式(本人從前也是運用這種方式,所以現在就遇到問題了。所以在此建議大家在了解了基本的按鍵檢測方法后,要及時的學習更加高級和完善的方法,不要滿足現狀。), 這種松手檢測只能運用在極少數情況下才能使用,因為等待釋放的過程中 “CPU” 一直在 “等待”釋放,導致其余的任務都不被執行。假設一個系統中有動態數碼管顯示(不考慮定時中斷。這里說一點題外話,其實我們應該盡可能減少對外設的依賴。這樣做,一來能夠提高自己的編程能力;二來能提高系統穩定性,特別是中斷,開多了是件很  “危險” 的事情。),則數碼管會熄滅,又或者其他實時性較高任務無法被執行。所以這里提供的 “反轉法” 我認為只能提供一種思路,不大可能應用到實際中。

接著說一下 “狀態機法”(與其說是 “狀態機法” 倒不如說是“ 狀態機 + 線反轉 法” ) 。第一個問題我認為倒不是樓主所說的 “上電數碼管就亮” ,而是 “松手檢測” 沒有起作用。其實也只差一步,那就是判斷。源碼中不論經過任何 “狀態” 都會 “return” 最新的值,那么按下了就會被立即 “return” 。所以在 “return” 前加一個 “釋放” 判斷: “ if(key_state==key_state0) ” 便可。問題解決了么? 還真沒有,我們會在編譯時(在 keil 環境下) 獲得一條警告 “warning C291: not every exit path returns a value(大意是:某些情況下沒有返回值)” ,選擇 “無視” , 然后燒寫,運行(我開始也是這么做的)。好了,的確有了松手檢測,但是又出現 “反轉法” 中出現的情況——數碼管全滅。雖然無法判斷是 “卡死” ,還是 “return” 了個未知數,但可以確定的是,這顯然也是不符合我們的需要的。我們再回到當初的判斷那里,總結前面的問題,任何情況下我們都要 “return” 。靜下心來分析發現,只有兩種情況:一是沒有釋放, 那么就要“return” 上一次按鍵的值;二是已經釋放,則“return”這一次的值。于是我們就再引入一個變量  “last_key_value” ,用來存放 “上一次” 的按鍵值(這里我們要清楚,“上一次” 是一個相對的情況,因為 “這一次” 的按鍵值立刻就會變成 “上一次”)。 添加的代碼如下:
/***************CHANGE--START******************************/
if(key_state==key_state0) {
    last_key_value=key_value;
     return key_value;  
}
else    return last_key_value;
/***************CHANGE--END******************************/

說了一堆 “廢話” 終于把第一個問題解決了,接下來解決樓主所說的那個問題。其實這是一個識別的問題,也就是說要 “ 如何識別是首次上電呢? ”。
這里我們在 “ main.c ” 文件中引入一個標志位“  bit power_on=1;                                          /* 標志位為 1 表示上電后未按下任何按鍵的狀態 */    ”  
并在 “main” 函數中加入                                    
“  if (power_on==0)    Display(readkey);            /* 上電后有按鍵被按下,才顯示對應數據*/                ”
到這里,我們又引入了另一個問題, “ power_on ” 需要在哪里被 “清零” ,你也許會說,很簡單啊,在 “ if(key_state==key_state0) {...} ” 清零啊。我們把  “ power_on = 0 ; /*  注意要聲明  extern bit power_on;   */  ”  直接加入其中,然后編譯、燒寫、運行。接著發現,咦!怎么還亮。然后,憑借我的直覺又把它放到了 “ else ” ,再來一遍,然后你發現直覺真管用,不亮了。 趕緊按一個鍵試試,顯示沒錯,不過你會突然發現好像下去的時候顯示的是 “ 0 ”  松手以后,才顯示對應的數字。復位再來一遍,真的是這樣,怎么辦?認真分析代碼發現(這里就不廢話了,留給各位“ 廢 ”好了),你會發現,的確應該在 “ if(key_state==key_state0) {...} ” 中清零(第一感覺真的很準)。不過還要添加一個 “標志位” 可以是 “釋放標志位” 也可以是 “按下標志位”,為什么?就留給各位來想了。這里選擇 “釋放標志位”。修改的代碼如下:
/***************CHANGE--START******************************/
case key_state2:{                                                        //狀態2,判斷是否被釋放
    if(key_temp==no_key){                                        //釋放,轉回到狀態0
        key_state=key_state0;
        shifang=1;
    }
}break;

......
if(key_state==key_state0)   {
    last_key_value=key_value;
    if(bit_release)    power_on=0;     /* bit_release 也就是釋放標志位 */
    return key_value;
}
/***************CHANGE--END******************************/
完成以后,編譯、燒寫、運行,OK!

矩陣鍵盤 狀態機法主程序:
  1. /*==========================================================
  2. * 開發人員:laowang
  3. * 當前版本:V1.0
  4. * 創建時間:11/26
  5. * 修改時間:
  6. * 功能說明:對4*4矩陣鍵盤掃描,用8位數碼管進行顯示,剛開始時不亮,依次按下按鍵時
  7.                           數碼管依次顯示0-F,掃描方法為狀態機方法+定時器中斷
  8. *==========================================================*/
  9. #include<reg52.h>
  10. #include"Define.h"
  11. #include"display.h"
  12. #include"matrixkeyscan.h"

  13. void Timer0_init();                        //定時器初始化函數
  14. uint flag=0;                                  //按鍵掃描標志,每中斷一次,掃描一次

  15. //主函數
  16. void main()
  17. {
  18.         uchar key_state=0;        
  19.         uchar readkey;
  20.         readkey=0xff;

  21.         Timer0_init();
  22.         Display_init();                        //使之不亮
  23.         while(1)
  24.          {
  25.                   if(flag)
  26.                   {
  27.                            flag=0;
  28.                            readkey=Keyscan();
  29.                            Display(readkey);
  30.                   }
  31.          }
  32. }

  33. void Timer0() interrupt 1                     
  34. {
  35.         TH0=0xd8;          //10Ms產生一次中斷
  36.          TL0=0xf0;
  37.          flag=1;
  38. }

  39. void Timer0_init()
  40. {
  41.         TMOD=0x01;
  42.          TH0=0xd8;                          //12MHz  10Ms
  43.          TL0=0xf0;
  44.         EA=1;
  45.         ET0=1;
  46.          TR0=1;        
  47. }
復制代碼

行列掃描法的主程序:
  1. /*==========================================================
  2. * 開發人員:laowang
  3. * 當前版本:V1.0
  4. * 創建時間:11/19
  5. * 修改時間:
  6. * 功能說明:讓數碼管的前兩位顯示顯示一個二位數(0-59),
  7.              通過三個按鍵控制,按下key1num加1,按下key2num減1,按下key3num清零
  8. *==========================================================*/
  9. /*==================硬件電路=============================================
  10. *
  11. *                                                     
  12. *                  MCU=89x51/52                         8位數碼管(共陰)
  13. *              +---------------+     +------+      +------------+------------+     
  14. *              |           p1.0|-----|      |---->a|                |                  |  
  15. *              |             . |     | 573  |  .   |8. 8. 8. 8. |8. 8. 8. 8. |
  16. *              |             . |     |      |  .   |                 |                  |
  17. *              |           p1.7|-----|      |--->dp|                |                   |        
  18. *     +--key1--|P2.0       p3.4|-----| 段選 |      +------------+------------+  
  19. *     |        |               |     +------+           |                  |
  20. *     |--key2--|P2.1           |                                      | . . . . .  .        |
  21. *     |        |               |     +------+           |              |                                 
  22. *     |--key3--|P2.2       p3.5|-----|      |           |                       |
  23. *     |        |           p1.0|-----|        573  |-----------|                        |
  24. *    GND       |            .  |     |      |                                            |
  25. *              |                  .  |     |      |                                        |
  26. *                         |                p1.7|-----| 位選 |--------------------------|                                       
  27. *                    |                         |     +------+
  28. *                        |                     |
  29. *              |               |
  30. *              +---------------+                  
  31. *                                       
  32. * 說明:此例程中只用到了數碼管的前兩位
  33. *=================================================================================*/
  34. #include<reg52.h>
  35. #include"display.h"
  36. #include"matrixkey.h"

  37. void main()
  38. {
  39.         while(1)
  40.         {
  41.                 Matrixkeyscan();
  42.                 Display(key);        
  43.         }

  44. }
復制代碼

完整的源碼請到本帖頂部下載附件.




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

使用道具 舉報

沙發
ID:135835 發表于 2017-3-13 16:45 | 只看該作者
在劉平老師的《深入淺出玩轉51單片機》里學習過狀態機法,看得不是太懂。謝樓主分享了。
回復

使用道具 舉報

板凳
ID:184846 發表于 2017-4-15 17:14 | 只看該作者
就是沒有金幣了
回復

使用道具 舉報

地板
ID:184846 發表于 2017-4-19 08:30 | 只看該作者
下載看了,覺得上電數碼管亮,好像是初始化顯示函數Display_init();那里一開始就把位選全打開了,而每10Ms產生一次中斷,主函數中一開始readkey=0xff;然后if(flag){flag=0;readkey=Keyscan();Display(readkey);一開始10ms就顯示0XFF,覺得這個位選應該在按鍵松手才打開,修改如下
void Display_init()
{
        P1=0xFF;                 //將所有的位選關閉,所有數碼管不亮,
        wela=1;
        wela=0;
        P1=0x00;           //將段選關閉,也就是剛開始的時候數碼管都不亮
        dula=1;
        dula=0;
}
void Display(uchar num1)
{
        wela=1;
        P1=0x00;                 //將所有的位選打開,所有數碼管同時亮,
        wela=0;
        dula=1;
        P1=smg_duan[num1];
        dula=0;       
}
回復

使用道具 舉報

5#
ID:184846 發表于 2017-4-19 08:31 | 只看該作者
哦,我看的是狀態機那個。
下載看了,覺得上電數碼管亮,好像是初始化顯示函數Display_init();那里一開始就把位選全打開了,而每10Ms產生一次中斷,主函數中一開始readkey=0xff;然后if(flag){flag=0;readkey=Keyscan();Display(readkey);一開始10ms就顯示0XFF,覺得這個位選應該在按鍵松手才打開,修改如下
void Display_init()
{
        P1=0xFF;                 //將所有的位選關閉,所有數碼管不亮,
        wela=1;
        wela=0;
        P1=0x00;           //將段選關閉,也就是剛開始的時候數碼管都不亮
        dula=1;
        dula=0;
}
void Display(uchar num1)
{
        wela=1;
        P1=0x00;                 //將所有的位選打開,所有數碼管同時亮,
        wela=0;
        dula=1;
        P1=smg_duan[num1];
        dula=0;       
}
回復

使用道具 舉報

6#
ID:184846 發表于 2017-4-19 09:11 | 只看該作者
下載看了狀態機,覺得上電數碼管亮,好像是初始化顯示函數Display_init();那里一開始就把位選全打開了,而每10Ms產生一次中斷,主函數中一開始readkey=0xff;然后if(flag){flag=0;readkey=Keyscan();Display(readkey);一開始10ms就顯示0XFF,覺得這個位選應該在按鍵松手才打開,修改如下
void Display_init()
{
        P1=0xFF;                 //將所有的位選關閉,所有數碼管不亮,
        wela=1;
        wela=0;
        P1=0x00;           //將段選關閉,也就是剛開始的時候數碼管都不亮
        dula=1;
        dula=0;
}
void Display(uchar num1)
{
        wela=1;
        P1=0x00;                 //將所有的位選打開,所有數碼管同時亮,
        wela=0;
        dula=1;
        P1=smg_duan[num1];
        dula=0;       
}

評分

參與人數 1黑幣 +100 收起 理由
admin + 100 回帖助人的獎勵!

查看全部評分

回復

使用道具 舉報

7#
ID:213328 發表于 2017-7-24 14:24 | 只看該作者
目前用的反轉,出了些問題,剛好學習學習
多謝樓主了
回復

使用道具 舉報

8#
ID:168002 發表于 2018-3-7 13:33 | 只看該作者
之前都是用反轉法,現在試試狀態機
回復

使用道具 舉報

9#
ID:203654 發表于 2018-5-24 10:46 | 只看該作者
感謝樓主分享,學習一下
回復

使用道具 舉報

10#
ID:286251 發表于 2018-5-26 15:32 | 只看該作者
很有用。非常感謝分享
回復

使用道具 舉報

11#
無效樓層,該帖已經被刪除
12#
ID:430148 發表于 2018-11-20 20:07 | 只看該作者
學習一下
回復

使用道具 舉報

13#
ID:380988 發表于 2018-12-2 14:45 | 只看該作者
感謝樓主分享,學習一下
回復

使用道具 舉報

14#
無效樓層,該帖已經被刪除
15#
無效樓層,該帖已經被刪除
16#
ID:413383 發表于 2019-6-10 00:03 | 只看該作者
多謝樓主了
回復

使用道具 舉報

17#
ID:558624 發表于 2019-7-13 22:00 | 只看該作者
感謝樓主
回復

使用道具 舉報

18#
ID:207882 發表于 2019-7-14 17:20 | 只看該作者
寫得挺好的,點贊,,我下載了你的狀態機程序,,為了省點黑幣,其他兩個我就沒下載了,,我看了后自己有些看法,,如果我同一行一個按鍵壓死,,然后我在同一行按下另一個按鍵呢????還有主函數10Ms檢測一次,,為什么數碼管顯示也放在這個里面???上電數碼管亮我覺得不是上面6#說的,                           readkey=Keyscan();
Display(readkey);
上電按鍵沒按的換Keyscan()返回的應該是0吧,,display(0)對應你的數組不就是全亮???,我覺得應該是這樣,試試把數組的全滅數據也就是0x00,放在第一個,,剩下的就按你的按鍵去排顯示的數據就好了
回復

使用道具 舉報

19#
ID:644656 發表于 2019-11-18 21:52 | 只看該作者
哇,感謝樓主的分享,給我們這些小白提供了學習的資料
回復

使用道具 舉報

20#
ID:658887 發表于 2019-12-8 15:50 | 只看該作者
感謝分享
回復

使用道具 舉報

21#
無效樓層,該帖已經被刪除
22#
無效樓層,該帖已經被刪除
23#
無效樓層,該帖已經被刪除
您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表