wangtao 发表于 2019-7-29 11:37:26

用ADC读取按键

写了一个用ADC读取按键的方法,分享给大家
https://www.arduino.cn/thread-90698-1-1.html

shouzama 发表于 2019-9-1 15:22:29

本帖最后由 shouzama 于 2019-9-1 15:39 编辑

我在去年也有做一個專題,那個專題要用4*4的矩陣按鍵
https://img.pcstore.com.tw/~prod/M07530108_big.jpg?P=1560388092
為了用最少I/O來做按鍵讀取功能,也稍微想了一下,依照
一般掃瞄方式就像您說的需要 4掃瞄線+4讀取線=8 個
PORT,後來我是用 4 個 ADC PORT 來達成,原理跟您的
類似,但我的做法在硬體上比較簡單,細部則交給軟體去
處理

比方說雜訊濾除,我沒用任何電路去做,但軟體要負責
判斷是雜訊或是真正按鈕,彈跳處理也是軟體來做,分壓
則很簡單用 4 顆 1Kohm 電阻將 5V 分成四等份:
1.23V、2.48V、3.75V、5V ,再將這些分壓接到矩陣
按鍵的掃瞄端,輸出端則接到 A4、A5、A6、A7
(A6 A7是 PRO MINI 才有的 ADC,它們不能當OUTPUT,
拿來做 INPUT 剛好合適 )

這個程式只是用來讀取矩陣按鈕,算是該專題全面進行
前的功能測試,後來改成副程式只是讓它在專題主程式
中比較好呼叫處理而已
/*
4*4 按鍵讀取測試程式,2018.09.27 作成
接腳 1:+5.00V 1023
接腳 2:+3.75V 767
接腳 3:+2.48V 507
接腳 4:+1.23V 251
依次讀取 A,B,C,D 腳位是否有穩定的輸入電壓(連讀2次間隔 GAP_TIME),
依讀取的位置(A~D)及讀到的電壓來判斷是哪個按鍵被按下,顯示於MONITOR內

2018.09.29 改版:以副程式呼叫,掃瞄讀取按鍵值並回傳,追加按鍵按住不放判斷
*/

#define MISS 5          //讀取值的可容許公差(+/-)

#define BUZZER 3      //定義蜂鳴器接腳為 3
#define BZ_TIMER 30   //按鍵回饋的響音長度
#define GAP_TIME 5      //連續讀取按鍵的間隔時間
#define SCAN_TIME 100   //按鍵回應時間

int SCAN_LINE = 0;                                          //內定由 1 縱列開始掃瞄(再往2,3,A縱列)
int SCAN = { A5, A4, A6, A7 };                           //設定掃瞄縱列(輸入埠順序)
int MAX = { 1023+MISS, 767+MISS, 507+MISS, 251+MISS };   //設定上限陣列
int MID = { 0, 1, 2, 3 };                                  //指向按鍵排(0=橫排1,1=橫排4,2=橫排7,3=橫排0)
int MIN = { 1023-MISS, 767-MISS, 507-MISS, 251-MISS };   //設定下限陣列
int K_VALUE = { 0,0,0 };                                 //定義按鍵讀值 3 筆
unsigned long KEY_PRESS_TIME = 0;                           //記錄此次按鍵時間

//設定按鍵陣列 KEY[縱列][橫排] 指向的特定按鍵值
int KEY = { { 1,2,3,10 }, { 4,5,6,11 }, { 7,8,9,12 }, { 0,15,14,13 } };


int READ_KEY()
//無輸入引數,讀取 4*4 按鍵狀態並回傳 1 短整數的按鍵值:(16=該掃瞄列無按鍵被按下,0~15="0"~"F")
{
int RESULT = 16;                        //回傳值,內定 16 (無按鍵按下)
//連續 3 次讀電壓值
for ( int I = 0; I<3; I++ )
{
    K_VALUE = analogRead( SCAN );   //讀取輸入值(0~1023)
    if ( K_VALUE <= MAX && K_VALUE >= MIN )
      K_VALUE = MID;                        //若符合 1023 電壓範圍,則將電壓值轉成按鍵排值0
    else
      if ( K_VALUE <= MAX && K_VALUE >= MIN )
      K_VALUE = MID;                      //若符合 767 電壓範圍,則將電壓值轉成按鍵排值1
      else
      if ( K_VALUE <= MAX && K_VALUE >= MIN )
          K_VALUE = MID;                  //若符合 507 電壓範圍,則將電壓值轉成按鍵排值2
      else
          if ( K_VALUE <= MAX && K_VALUE >= MIN )
            K_VALUE = MID;                  //若符合 251 電壓範圍,則將電壓值轉成按鍵排值3
          else
            K_VALUE = I+4;                     //若不符電壓範圍(浮動電壓)則將排值設為 I+4 (不存在&不會相同的排值)
    delay( GAP_TIME );                            //暫停,繼續讀下一次(共讀3次)
}
//判斷是否穩定(3 筆讀值需相同),若穩定,特定出按鍵別("0"~"F")
if ( K_VALUE == K_VALUE && K_VALUE == K_VALUE )
    RESULT = KEY];    //依掃瞄線別 SCAN_LINE 及電壓讀值 K_VALUE 特定出被按下的按鍵

return RESULT;                        //傳回按鍵值(0~16)
}


void setup()
{
pinMode(BUZZER, OUTPUT);            //指定蜂鳴器為輸出用
//初始化串列通訊並指定速率(BPS):
Serial.begin(9600);
}

void loop()
{
START:

SCAN_LINE++;                        //切往下一掃瞄縱列(0→3=1→A)
if ( SCAN_LINE == 4 )               //限制掃瞄縱列範圍0~3
    SCAN_LINE = 0;

int NEW_KEY = READ_KEY();             //讀取按鍵值

if ( NEW_KEY != 16 )                  //若有正確&穩定的新回傳按鍵值
{
    if ( (millis()-KEY_PRESS_TIME) < SCAN_TIME )//若前後按鍵時間短則以按鍵未放開處理
    {
      KEY_PRESS_TIME = millis();      //更新按鍵時間
      goto START;                     //重新掃瞄按鍵
    }

    Serial.print( NEW_KEY );            //顯示按鍵值&換列
    Serial.print( "\n" );
    digitalWrite( BUZZER, HIGH );       //響音
    delay( BZ_TIMER );
    digitalWrite( BUZZER, LOW );
    KEY_PRESS_TIME = millis();          //記錄此次按鍵時間
}

}


页: [1]
查看完整版本: 用ADC读取按键