|
发表于 2019-9-1 15:22:29
|
显示全部楼层
本帖最后由 shouzama 于 2019-9-1 15:39 编辑
我在去年也有做一個專題,那個專題要用4*4的矩陣按鍵
為了用最少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[4] = { A5, A4, A6, A7 }; //設定掃瞄縱列(輸入埠順序)
- int MAX[4] = { 1023+MISS, 767+MISS, 507+MISS, 251+MISS }; //設定上限陣列
- int MID[4] = { 0, 1, 2, 3 }; //指向按鍵排(0=橫排1,1=橫排4,2=橫排7,3=橫排0)
- int MIN[4] = { 1023-MISS, 767-MISS, 507-MISS, 251-MISS }; //設定下限陣列
- int K_VALUE[3] = { 0,0,0 }; //定義按鍵讀值 3 筆
- unsigned long KEY_PRESS_TIME = 0; //記錄此次按鍵時間
- //設定按鍵陣列 KEY[縱列][橫排] 指向的特定按鍵值
- int KEY[4][4] = { { 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[I] = analogRead( SCAN[SCAN_LINE] ); //讀取輸入值(0~1023)
- if ( K_VALUE[I] <= MAX[0] && K_VALUE[I] >= MIN[0] )
- K_VALUE[I] = MID[0]; //若符合 1023 電壓範圍,則將電壓值轉成按鍵排值0
- else
- if ( K_VALUE[I] <= MAX[1] && K_VALUE[I] >= MIN[1] )
- K_VALUE[I] = MID[1]; //若符合 767 電壓範圍,則將電壓值轉成按鍵排值1
- else
- if ( K_VALUE[I] <= MAX[2] && K_VALUE[I] >= MIN[2] )
- K_VALUE[I] = MID[2]; //若符合 507 電壓範圍,則將電壓值轉成按鍵排值2
- else
- if ( K_VALUE[I] <= MAX[3] && K_VALUE[I] >= MIN[3] )
- K_VALUE[I] = MID[3]; //若符合 251 電壓範圍,則將電壓值轉成按鍵排值3
- else
- K_VALUE[I] = I+4; //若不符電壓範圍(浮動電壓)則將排值設為 I+4 (不存在&不會相同的排值)
- delay( GAP_TIME ); //暫停,繼續讀下一次(共讀3次)
- }
- //判斷是否穩定(3 筆讀值需相同),若穩定,特定出按鍵別("0"~"F")
- if ( K_VALUE[0] == K_VALUE[1] && K_VALUE[0] == K_VALUE[2] )
- RESULT = KEY[SCAN_LINE][K_VALUE[0]]; //依掃瞄線別 SCAN_LINE 及電壓讀值 K_VALUE[0] 特定出被按下的按鍵
-
- 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(); //記錄此次按鍵時間
- }
- }
复制代码
|
|