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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

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

51單片機8個IO口掃描檢測64個按鍵+仿真程序+算法實現和心法要點講解

  [復制鏈接]
跳轉到指定樓層
樓主
51單片機8個IO口掃描檢測64個按鍵+數碼管顯示+程序+Proteus仿真+算法實現和心法要點講解
  • 掃描原理想通了很簡單,就是通過一個IO拉低,其檢測這一組其他7個IO口狀態,如果檢測到有被拉低了,說明就有按鍵按下了,因為檢測到拉低的IO口,是被被用來檢測的IO口拉低的。相當于我用一個接GND的探針去碰一個高電平的IO口,肯定是會拉低的。電路原理的話,利用二極管單向導通特性,在檢查過程中,如果IO口被拉低,只能構成一個回路,依次不斷輪詢進行。
  • 知道了按鍵掃描原理然后去實現的過程并不是很艱難,我覺得最困難的是調試過程遇到的各種問題。掌握其原理并不那么復雜,在寫完之后,代碼并不是很順利按照自己的思路去運行的,也許就是一個項目的學習過程。一個DIY創意可能很簡單,真正讓其按照自己的想法運行還是有很多細節點要打通的。
  • 小bug折騰的時間比整個寫代碼花費的精力和時間多得多,想想一個穩定好用的產品都需要幾個版本的迭代。

總結要領     最難的地方是,控制56-64最后一排的數碼管顯示,因為,這個你是對P0總線端口自身的掃描,最容易出問題的地方,也是卡在這個地方最長時間。一定要了解其單片機運行和按鍵掃描原理。在沒有延時或打斷的情況下,按鍵動作的時間一定是快不過單片機運行的速度。所以在處理最后一排按鍵時,需要特別注意,顯示時要比其他行掃描處理的時間留長一點,不然就很容易跳數,按下的按鍵,和顯示的數值不是你想要的結果,下面我會將經驗一一寫下來。
仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載)

  • 在處理最后一排按鍵,我有想過兩個辦法來處理邏輯判斷問題:
  • 利用復合邏輯來寫,很直觀,但是代碼閱讀和可執行性看起來相對很臃腫一樣,寫的時候很爽,單片機處理邏輯,運行的時間會多一些。是我最先想到的第一種辦法,寫法如下:
    1. if(P0==0x7f||P0==0xbf||P0==0xdf||P0==0xef||P0==0xf7||P0==0xfb||P0==0xfd||P0==0xfe)
    復制代碼

  • 第二種辦法,通過二分查找的方式:(為什么可以采用二分查找算法來快速篩查對象,是有講究的):二分查找的條件就是注意事項,定義的數組必須是有序序列才行。最優方法看不懂不要緊,直接搜算法拿來用就行!寫法如下:
    1. int Search(uchar arr[], int len, int flag)
    2. {
    3. int right = len - 1;
    4. int left = 0;
    5. while (left <= right)
    6. {
    7. int mid = (right + left) / 2;
    8. if (arr[mid] > flag)
    9. {
    10. right = mid - 1;
    11. }
    12. else if (arr[mid] < flag)
    13. {
    14. left = mid + 1;
    15. }
    16. else
    17. {
    18. return arr[mid];
    19. }
    20. }
    21. return 0;
    22. }
    復制代碼

  • 第三種遍歷方法,就不需要參照二分查找算法那樣考慮什么注意事項了,隨便寫一個簡單的遍歷程序即可,執行效率雖然慢一點,起碼實現起來簡單,代碼可敲性強,比起二分查找算法寫起來。
    1. uchar libian(uchar a[], int value, int n)
    2. {
    3.         int i;
    4.   for (i = 0; i < n; i++)
    5.   {
    6.     if (value == a[i])
    7.     {
    8.       return a[i];
    9.     }
    10.   }
    11.   return 0;
    12. }
    復制代碼

0-64個按鍵,需要考慮消抖的只有最后一排56-648個按鍵的響應。為什么這么說呢?這是因為按鍵從設計原理和實現來看的。0-56的按鍵不管你怎么長按還是短按,單片機給你的響應數值都是一樣的不會變,但是在處理56-64這8個按鍵時,是做了特殊處理,連接的是GND,如果敲代碼沒注意的話,就很容易造成跳數字,單片機掃描是通過按照規定先給指定的IO口拉點,再去檢測其他7個IO的電平狀態,所以在處理第56-64按鍵時,如果你操作的按鍵按下時,單片機掃描按鍵的速度已經從你按下那一到彈起前已經超過了你的速度,那么會造成,單片機讀取到錯誤的響應數據,處理方式如下:
  1. /****************自身端口讀取*********************/
  2.                 P0=0xff;//掃描第8行
  3. //    delay(5);
  4.                 tmp=P0;
  5.     if (Search(arr, 8, tmp))
  6.     {
  7.         //將檢測到的P0狀態值賦值給臨時變量
  8.         switch(Search(arr, 8, tmp))
  9.         {   //臨時變量對逐個IO口進行查詢
  10.         case 0xfe:
  11.             keynum=57;
  12.             break;//第1行第1個按鍵按下
  13.         case 0xfd:
  14.             keynum=58;
  15.             break;//第1行第2個按鍵按下
  16.         case 0xfb:
  17.             keynum=59;
  18.             break;//第1行第3個按鍵按下
  19.         case 0xf7:
  20.             keynum=60;
  21.             break;//第1行第4個按鍵按下
  22.         case 0xef:
  23.             keynum=61;
  24.             break;//第1行第5個按鍵按下
  25.         case 0xdf:
  26.             keynum=62;
  27.             break;//第1行第6個按鍵按下
  28.         case 0xbf:
  29.             keynum=63;
  30.             break;//第1行第7個按鍵按下
  31.         case 0x7f:
  32.             keynum=64;
  33.             break;//第1行第8個按鍵按下
  34.         }        
  35.                         display();//這里必須單獨處理P0的IO檢測和顯示,否則容易跳數
  36.                         delay(80);//阻塞按鍵掃描,防止數碼管跳變,其他行掃描不需要此處的延時。
  37.     }
復制代碼
完整實現代碼:
  1. #include <reg52.h>
  2. #include<intrins.h>
  3. #define uchar unsigned char
  4. #define uint unsigned int
  5. //共陰極數碼管0~9
  6. uchar code table[]= {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//0-9共陰數碼管
  7. uchar code arr[] = {0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe};

  8. uchar duanZhi[]= {0,0};//保存每段數碼管顯示位數的數值
  9. sbit P36=P3^6;//數碼管時能端
  10. sbit P37=P3^7;
  11. sbit ST=P3^0;//定義74HC595移位寄存器
  12. sbit SH=P3^2;
  13. sbit DS=P3^1;
  14. sbit P33=P3^3;
  15. sbit P34=P3^4;
  16. sbit P35=P3^5;

  17. uchar shi,ge;//數碼管個位和十位顯示
  18. uchar tmp;//暫存P0的值
  19. static uchar keynum=0;//按鍵值
  20. unsigned char Trg;
  21. unsigned char Cont;
  22. static char count=1;
  23. //毫秒級延時
  24. void delay(uint z)
  25. {
  26.     uint x,y;
  27.     for(x=z; x>>0; x--)
  28.         for(y=110; y>>0; y--);
  29. }
  30. void SendTo595(uchar byteData);
  31. int Search(uchar arr[], int len, int flag)
  32. {
  33.     int right = len - 1;
  34.     int left = 0;
  35.     while (left <= right)
  36.     {
  37.         int mid = (right + left) / 2;
  38.         if (arr[mid] > flag)
  39.         {
  40.             right = mid - 1;
  41.         }
  42.         else if (arr[mid] < flag)
  43.         {
  44.             left = mid + 1;
  45.         }
  46.         else
  47.         {
  48.             return arr[mid];
  49.         }
  50.     }
  51.     return 0;
  52. }
  53. /*----------------------------------------------------------------------------------
  54.                                 顯示

  55. void display2()
  56. {
  57.     ge = keynum%10;
  58.     shi = keynum/10;
  59.     duanZhi[0]=table[ge];
  60.     duanZhi[1]=table[shi];
  61.     P34=0x00;
  62.     SendTo595(duanZhi[0]);                 //
  63.     delay(5);
  64.     P34=0x01;//消隱

  65.     P33=0x00;
  66.     SendTo595(duanZhi[1]);//
  67.     delay(5);
  68.     P33=0x01;//消隱

  69. }
  70. ----------------------------------------------------------------------------------*/
  71. void display()
  72. {
  73.     ge = keynum%10;
  74.     shi = keynum/10;
  75.     duanZhi[0]=table[ge];
  76.     duanZhi[1]=table[shi];
  77.     //顯示個位
  78.     P37=0;
  79.     SendTo595(duanZhi[0]);                 //
  80.     delay(2);
  81.     P37=1;//消隱
  82.     //顯示十位
  83.     P36=0;
  84.     SendTo595(duanZhi[1]);//
  85.     delay(2);
  86.     P36=1;//消隱

  87. }
  88. /***********************************************************
  89. *函數名                :SendTo595
  90. *功能                :串行發送8個比特(一個字節)的數據給595,再并行輸出
  91. *參數                :byteData
  92. ************************************************************/
  93. void SendTo595(uchar byteData)
  94. {
  95.     uchar i=0;
  96.     ST = 0;   //ST //先拉低,為后面的上升沿做準備
  97.     for(i; i<8; i++)
  98.     {
  99.         SH = 0;//先拉低,
  100.         if(byteData&0x80)DS=1;
  101.         else DS=0;
  102. //        DS = (byteData&0x80)?1:0;
  103.         byteData = byteData <<1;                //該字節右移一位

  104.         SH = 1;//上升沿,讓串行輸入時鐘變為高電平,并延時2個時鐘周期
  105.         _nop_();
  106.         _nop_();
  107.         SH = 0;        //上升沿,讓串行輸入時鐘變為高電平,并延時2個時鐘周期
  108.     }
  109.     /*位移寄存器數據準備完畢,轉移到存儲寄存器*/
  110.     ST =1;
  111.     _nop_();
  112.     _nop_();
  113.     ST = 0;
  114. }

  115. void key_scan()
  116. {
  117. //    P0=0xff;
  118. //    delay(6);

  119.     /********************第1行掃描**************************/

  120.     P0=0x7F;//掃描第1行0111 1111
  121.     delay(5);
  122.     if (!Search(arr, 8, tmp))//有按鍵按下
  123.     {
  124.         tmp=P0;//將檢測到的P0狀態值賦值給臨時變量
  125.         switch(tmp)
  126.         {   //臨時變量對逐個IO口進行查詢
  127.         case 0x7e:
  128.             keynum=50;
  129.             break;//第1行第1個按鍵按下
  130.         case 0x7d:
  131.             keynum=51;
  132.             break;//第1行第2個按鍵按下
  133.         case 0x7b:
  134.             keynum=52;
  135.             break;//第1行第3個按鍵按下
  136.         case 0x77:
  137.             keynum=53;
  138.             break;//第1行第4個按鍵按下
  139.         case 0x6f:
  140.             keynum=54;
  141.             break;//第1行第5個按鍵按下
  142.         case 0x5f:
  143.             keynum=55;
  144.             break;//第1行第6個按鍵按下
  145.         case 0x3f:
  146.             keynum=56;
  147.             break;//第1行第7個按鍵按下
  148.         }
  149.     }
  150.     /********************第2行掃描**************************/
  151.     P0=0xbf;//掃描第2行
  152.     delay(5);
  153.     if(P0!=0xbf)//有按鍵按下
  154.     {
  155.         tmp=P0;//將檢測到的P0狀態值賦值給臨時變量
  156.         switch(tmp)
  157.         {   //臨時變量對逐個IO口進行查詢
  158.         case 0xbe:
  159.             keynum=43;
  160.             break;//第2行第1個按鍵按下
  161.         case 0xbd:
  162.             keynum=44;
  163.             break;//第2行第2個按鍵按下
  164.         case 0xbb:
  165.             keynum=45;
  166.             break;//第2行第3個按鍵按下
  167.         case 0xb7:
  168.             keynum=46;
  169.             break;//第2行第4個按鍵按下
  170.         case 0xaf:
  171.             keynum=47;
  172.             break;//第2行第5個按鍵按下
  173.         case 0x9f:
  174.             keynum=48;
  175.             break;//第2行第6個按鍵按下
  176.         case 0x3f:
  177.             keynum=49;
  178.             break;//第2行第7個按鍵按下
  179.         }        
  180.     }
  181.     /********************第3行掃描**************************/

  182.     P0=0xdf;//掃描第3行
  183.     delay(5);
  184.      if(P0!=0xdf)//有按鍵按下
  185.     {
  186.         tmp=P0;//將檢測到的P0狀態值賦值給臨時變量
  187.         switch(tmp)
  188.         {   //臨時變量對逐個IO口進行查詢
  189.         case 0xde:
  190.             keynum=36;
  191.             break;//第3行第1個按鍵按下
  192.         case 0xdd:
  193.             keynum=37;
  194.             break;//第3行第2個按鍵按下
  195.         case 0xdb:
  196.             keynum=38;
  197.             break;//第3行第3個按鍵按下
  198.         case 0xd7:
  199.             keynum=39;
  200.             break;//第3行第4個按鍵按下
  201.         case 0xcf:
  202.             keynum=40;
  203.             break;//第3行第5個按鍵按下
  204.         case 0x9f:
  205.             keynum=41;
  206.             break;//第3行第6個按鍵按下
  207.         case 0x5f:
  208.             keynum=42;
  209.             break;//第3行第7個按鍵按下
  210.         }

  211.     }
  212.     /********************第4行掃描**************************/

  213.     P0=0xef;//掃描第4行
  214.     delay(5);
  215.     if(P0!=0xef)//有按鍵按下
  216.     {
  217.         tmp=P0;//將檢測到的P0狀態值賦值給臨時變量
  218.         switch(tmp)
  219.         {   //臨時變量對逐個IO口進行查詢
  220.         case 0xee:
  221.             keynum=29;
  222.             break;//第4行第1個按鍵按下
  223.         case 0xed:
  224.             keynum=30;
  225.             break;//第4行第2個按鍵按下
  226.         case 0xeb:
  227.             keynum=31;
  228.             break;//第4行第3個按鍵按下
  229.         case 0xe7:
  230.             keynum=32;
  231.             break;//第3行第4個按鍵按下
  232.         case 0xcf:
  233.             keynum=33;
  234.             break;//第4行第5個按鍵按下
  235.         case 0xaf:
  236.             keynum=34;
  237.             break;//第4行第6個按鍵按下
  238.         case 0x6f:
  239.             keynum=35;
  240.             break;//第4行第7個按鍵按下
  241.         }               
  242.     }
  243.     /********************第5行掃描**************************/

  244.     P0=0xf7;//掃描第5行
  245.     delay(5);
  246.     if(P0!=0xf7)//有按鍵按下
  247.     {
  248.         tmp=P0;//將檢測到的P0狀態值賦值給臨時變量
  249.         switch(tmp)
  250.         {   //臨時變量對逐個IO口進行查詢
  251.         case 0xf6:
  252.             keynum=22;
  253.             break;//第5行第1個按鍵按下
  254.         case 0xf5:
  255.             keynum=23;
  256.             break;//第5行第2個按鍵按下
  257.         case 0xf3:
  258.             keynum=24;
  259.             break;//第5行第3個按鍵按下
  260.         case 0xe7:
  261.             keynum=25;
  262.             break;//第5行第4個按鍵按下
  263.         case 0xd7:
  264.             keynum=26;
  265.             break;//第5行第5個按鍵按下
  266.         case 0xb7:
  267.             keynum=27;
  268.             break;//第5行第6個按鍵按下
  269.         case 0x77:
  270.             keynum=28;
  271.             break;//第5行第7個按鍵按下
  272.         }               
  273.     }
  274.     /********************第6行掃描**************************/

  275.     P0=0xfb;//掃描第6行
  276.     delay(5);
  277.     if(P0!=0xfb)//有按鍵按下
  278.     {
  279.         tmp=P0;//將檢測到的P0狀態值賦值給臨時變量
  280.         switch(tmp)
  281.         {   //臨時變量對逐個IO口進行查詢
  282.         case 0xfa:
  283.             keynum=15;
  284.             break;//第6行第1個按鍵按下
  285.         case 0xf9:
  286.             keynum=16;
  287.             break;//第6行第2個按鍵按下
  288.         case 0xf3:
  289.             keynum=17;
  290.             break;//第6行第3個按鍵按下
  291.         case 0xeb:
  292.             keynum=18;
  293.             break;//第6行第4個按鍵按下
  294.         case 0xdb:
  295.             keynum=19;
  296.             break;//第6行第5個按鍵按下
  297.         case 0xbb:
  298.             keynum=20;
  299.             break;//第6行第6個按鍵按下
  300.         case 0x7b:
  301.             keynum=21;
  302.             break;//第6行第7個按鍵按下
  303.         }
  304.     }
  305.     /********************第7行掃描**************************/
  306.     P0=0xfd;//掃描第7行
  307.     delay(5);
  308.     if(P0!=0xfd)//有按鍵按下
  309.     {
  310.         tmp=P0;//將檢測到的P0狀態值賦值給臨時變量
  311.         switch(tmp)
  312.         {   //臨時變量對逐個IO口進行查詢
  313.         case 0xfc:
  314.             keynum=8;
  315.             break;//第7行第1個按鍵按下
  316.         case 0xf9:
  317.             keynum=9;
  318.             break;//第7行第2個按鍵按下
  319.         case 0xf5:
  320.             keynum=10;
  321.             break;//第7行第3個按鍵按下
  322.         case 0xed:
  323.             keynum=11;
  324.             break;//第7行第4個按鍵按下
  325.         case 0xdd:
  326.             keynum=12;
  327.             break;//第7行第5個按鍵按下
  328.         case 0xbd:
  329.             keynum=13;
  330.             break;//第7行第6個按鍵按下
  331.         case 0x7d:
  332.             keynum=14;
  333.             break;//第7行第7個按鍵按下
  334.         }
  335.     }
  336.     /********************第8行掃描**************************/

  337.     P0=0xfe;//掃描第8行
  338.     delay(5);
  339.     if(P0!=0xfe)//有按鍵按下
  340.     {
  341.         tmp=P0;//將檢測到的P0狀態值賦值給臨時變量
  342.         switch(tmp)
  343.         {   //臨時變量對逐個IO口進行查詢
  344.         case 0xfc:
  345.             keynum=1;
  346.             break;//第8行第1個按鍵按下
  347.         case 0xfa:
  348.             keynum=2;
  349.             break;//第8行第2個按鍵按下
  350.         case 0xf6:
  351.             keynum=3;
  352.             break;//第8行第3個按鍵按下
  353.         case 0xee:
  354.             keynum=4;
  355.             break;//第8行第4個按鍵按下
  356.         case 0xde:
  357.             keynum=5;
  358.             break;//第8行第5個按鍵按下
  359.         case 0xbe:
  360.             keynum=6;
  361.             break;//第8行第6個按鍵按下
  362.         case 0x7e:
  363.             keynum=7;
  364.             break;//第8行第7個按鍵按下
  365.         }
  366.     }
  367. /****************自身端口讀取*********************/
  368.                 P0=0xff;//掃描第8行
  369. //    delay(5);
  370.                 tmp=P0;
  371.     if (Search(arr, 8, tmp))
  372.     {
  373.         //將檢測到的P0狀態值賦值給臨時變量
  374.         switch(Search(arr, 8, tmp))
  375.         {   //臨時變量對逐個IO口進行查詢
  376.         case 0xfe:
  377.             keynum=57;
  378.             break;//第1行第1個按鍵按下
  379.         case 0xfd:
  380.             keynum=58;
  381.             break;//第1行第2個按鍵按下
  382.         case 0xfb:
  383.             keynum=59;
  384.             break;//第1行第3個按鍵按下
  385.         case 0xf7:
  386.             keynum=60;
  387.             break;//第1行第4個按鍵按下
  388.         case 0xef:
  389.             keynum=61;
  390.             break;//第1行第5個按鍵按下
  391.         case 0xdf:
  392.             keynum=62;
  393.             break;//第1行第6個按鍵按下
  394.         case 0xbf:
  395.             keynum=63;
  396.             break;//第1行第7個按鍵按下
  397.         case 0x7f:
  398.             keynum=64;
  399.             break;//第1行第8個按鍵按下
  400.         }        
  401.                         display();//這里必須單獨處理P0的IO檢測和顯示,否則容易跳數
  402.                         delay(80);//阻塞按鍵掃描,防止數碼管跳變,其他行掃描不需要此處的延時。
  403.     }
  404.    display();//這里的顯示是保存上一次的顯示值

  405. }
  406. void main()
  407. {
  408.     keynum=0;
  409.     P0=0xff;
  410.     while(1)
  411.     {               
  412.         key_scan();
  413.     }
  414. }
復制代碼
仿真器件選選擇也是很有講究的。附上截圖


  • 注意:二極管選型,不能選擇發光二極管,有些其他的二極管也不好使,我使用的是10A01
  • 注意走線,線路連接中最好不要出現重復線或者點,有多余的線或者連接點最好刪除,不然出問題了排查問題帶來難度,有可能也會影響仿真效果,電阻選擇不能過大。仿真時,會比較占電腦內存。按鍵操作和響應速度可能會存在卡頓。





我會附上源碼,提供兩套不同實現方案的代碼。
8個IO口檢測64個按鍵.zip (118.94 KB, 下載次數: 42)

  • 創作不易,寫出來調試代碼,折騰了我好幾天,對于有幫助的朋友,希望能用上,避免重復的道路上采坑。

評分

參與人數 3黑幣 +119 收起 理由
waerdeng + 5 很給力!
wps10025 + 24 很給力!
admin + 90 共享資料的黑幣獎勵!

查看全部評分

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

使用道具 舉報

沙發
ID:333678 發表于 2021-9-12 07:51 | 只看該作者
感謝樓主的無私奉獻,下載學習了。
回復

使用道具 舉報

板凳
ID:284488 發表于 2021-9-12 09:14 | 只看該作者
感謝樓主的分享,學習了。
回復

使用道具 舉報

地板
ID:164385 發表于 2021-9-12 10:14 | 只看該作者
樓主很厲害,辛苦了
回復

使用道具 舉報

5#
ID:57657 發表于 2021-9-12 16:58 | 只看該作者
這個程序還可以優化很多,仿真測試OK。
  1. #include "reg51.h"
  2. #include "intrins.h"
  3. #define u8 unsigned char
  4. sbit P36 = P3 ^ 6;
  5. sbit P37 = P3 ^ 7;
  6. sbit STCP = P3 ^ 0;//定義74HC595移位寄存器
  7. sbit SHCP = P3 ^ 2;
  8. sbit DS = P3 ^ 1;

  9. u8 code table[16] = {                //共陰數碼管段碼16進制
  10.         0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
  11. };

  12. void delay(){
  13.     u8 a,b;
  14.     for(b=19;b>0;b--)
  15.         for(a=130;a>0;a--);
  16. }

  17. void txd595(u8 d) {
  18.         u8 i;
  19.         for (i = 0; i < 8; i++) {
  20.                 DS = d & 0x80;
  21.                 SHCP = 0;
  22.                 d <<= 1;
  23.                 SHCP = 1;
  24.         }
  25.         STCP=0;_nop_();STCP=1;
  26. }

  27. void display(u8 d) {
  28.         P37 = 0;
  29.         txd595(table[d%10]);
  30.         delay();
  31.         P37 = 1;
  32.         _nop_();
  33.         P36 = 0;
  34.         txd595(table[d/10]);
  35.         delay();
  36.         P36 = 1;
  37. }

  38. u8 b0(u8 dat) {                //取一個字節中有多少個二進制0 返回0~8
  39.         u8 i, j = 0;
  40.         for (i = 0; i < 8; i++) {
  41.                 if (!(dat & (1 << i))) {
  42.                         j++;
  43.                 }
  44.         }
  45.         return j;
  46. }

  47. u8 key_scan() {                //無按鍵返回0 有按鍵返回1~64 (禁止多個按鍵同時按下,出錯返回99)
  48.         u8 i, j, k, l, m;
  49.         P0 = 0xFF;
  50.         _nop_();
  51.         k = P0;
  52.         if (k != 0xFF) {
  53.                 if (b0(k) != 1) return 99;
  54.                 for (i = 0; i < 8; i++) {
  55.                         if (!(k & (1 << i))) {
  56.                                 return 57 + i;
  57.                         }
  58.                 }
  59.         }

  60.         for (i = 0; i < 8; i++) {
  61.                 j = ~(1 << i);
  62.                 P0 = j;
  63.                 _nop_();
  64.                 k= P0;
  65.                 if (k != j) {
  66.                         if (k & ~j || b0(k) != 2) return 99;
  67.                         m = 0;
  68.                         for (l = 0; l < 8; l++) {
  69.                                 if (i == l) continue;
  70.                                 m++;
  71.                                 if (!(k & (1 << l))) {
  72.                                         return i * 7 + m;
  73.                                 }
  74.                         }
  75.                 }

  76.         }
  77.         return 0;
  78. }

  79. void main() {
  80.         while (1) {
  81.                 display(key_scan());
  82.         }
  83. }
復制代碼
回復

使用道具 舉報

6#
ID:954686 發表于 2021-9-12 17:03 | 只看該作者
不錯,這個例程好。IO不夠的時候,之前用過4個IO口實現16個按鍵的方法,和樓主的電路差不多。
回復

使用道具 舉報

7#
ID:795427 發表于 2021-9-16 10:33 | 只看該作者
npn 發表于 2021-9-12 16:58
這個程序還可以優化很多,仿真測試OK。

感謝壇友的集思廣益,群策群力,在源代碼基礎上做的優化和改進。
回復

使用道具 舉報

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

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 欧美在线视频一区二区 | 欧美一区二区在线观看视频 | 日本精品视频在线 | 国产视频二区 | 午夜视频在线视频 | 日韩三级 | 久久亚洲精品国产精品紫薇 | 欧美日韩在线一区二区三区 | 日韩一区中文字幕 | 日韩三级在线观看 | 国产成人在线视频 | 国产成人短视频在线观看 | 视频三区| 91精品一区二区三区久久久久久 | 91色啪| 老司机免费视频 | 亚洲综合无码一区二区 | 99热在线观看精品 | 精品在线一区 | 久久中文视频 | 欧美做暖暖视频 | 午夜精品久久久久久久久久久久 | 日本精品视频在线观看 | 亚洲在线久久 | 少妇一区在线观看 | 在线观看h视频 | 在线亚洲一区二区 | 日韩中文字幕一区二区 | 欧美日一区二区 | 免费一区| 日本特黄a级高清免费大片 特黄色一级毛片 | 91视频网址| 亚洲第一av | 在线欧美a| 久草在线| 欧美在线一区视频 | 亚洲国产高清高潮精品美女 | 91免费版在线观看 | 国产精品7777777 | 超碰网址| 国产成人在线视频 |