Arduino 4×4薄膜键盘按键扫描程序原理

下图左是一款常见的薄膜按键模块(hex keypad),有4×4或3×4(少了最右边一行的A,B,C,D键),它的内部如同下图右边的电路所示,是由16个按键(开关)交织而成。有些按键模块直接使用按键(微触)开关组装,连接电路与程序都和本文相同。

4×4按键模块有8个接脚,分成列、行两组,可以接在Arduino的任意8个接脚,笔者将它接在数位6~13脚:

为了简化接线,可以使用「长脚型」排针,将其中两个脚用尖嘴钳稍微折弯,即可插入Arduino的排母:

排针上头的黑色塑料,可以用蛮力让它移到中间的位置,以便衔接两边的排母:

4×4薄膜键盘的Arduino按键侦测程序

撰写Arduino按键侦测程序,最简单也是最好的方式,就是采用既有的程序库。本单元将示范使用Keypad程序库,把侦测到的按键字元显示在序列端口监控视窗。请按此下载Keypad程序库(原始出处在这篇keypad介绍文),解压缩之后,置入Arduino的libraries路径:

底下的程序,修改自Keypad程序库的“HelloKeypad”示例,使用此程序库,我们的代码需要定义按键模块的行(col)、列(row)数、连接Arduino的脚位以及按键所代表的字元

#include <Keypad.h>    // 引用Keypad程式庫

#define KEY_ROWS 4 // 按鍵模組的列數
#define KEY_COLS 4 // 按鍵模組的行數

// 依照行、列排列的按鍵字元(二維陣列)
char keymap[KEY_ROWS][KEY_COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte colPins[KEY_COLS] = {9, 8, 7, 6};	   // 按鍵模組,行1~4接腳。
byte rowPins[KEY_ROWS] = {13, 12, 11, 10}; // 按鍵模組,列1~4接腳。 

// 初始化Keypad物件
// 語法:Keypad(makeKeymap(按鍵字元的二維陣列), 模組列接腳, 模組行接腳, 模組列數, 模組行數)
Keypad myKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, KEY_ROWS, KEY_COLS);

void setup(){
  Serial.begin(9600);
}
  
void loop(){
  // 透過Keypad物件的getKey()方法讀取按鍵的字元
  char key = myKeypad.getKey();
  
  if (key){  // 若有按鍵被按下…
    Serial.println(key);  // 顯示按鍵的字元
  }
}

编译并上传代码,再开启「序列监控视窗」,按下薄膜键盘的任何按键,该字元将显示在序列端口监控视窗。

按键侦测与扫描原理

为了方便解说,笔者把4×4按键简化成3×1,像下图这样串连三个开关,连接到同一个微控制器的输入脚。此外,因为要简化开关电路,所以要启用微控器内部的上拉电阻

假设开关的「行1」~「行3」输入端全都输入高电位,无论开关是否被按下,Arduino将接收到高电位(1)。为了检测到其中按键被按下,程序必须依序将「行1」~「行3」脚位设定成低电位。

轮到「行2」脚输入低电位,此时,微控器的输入脚也将接收到低电位(0),由此可知连接「行2」的「开关B」被按下了

轮到「行3」脚输入低电位,由于「开关C」未被按下,因此微控器的输入脚接收到高电位(1)。

到此,侦测按键的程序必须再次回到「行1」,输入低电位…如此反复循环扫描,才能持续侦测到某个按键是否被按下。实际的程序需要运用双重循环,才能分批扫描每一列:

双重循环的练习,请参阅《超图解Arduino互动设计入门》第8章「LED矩阵动画与多维数组程序设计」一节,8-28页

自行撰写扫描按键的程序

根据以上说明,自行撰写扫描按键的代码:

const byte colPins[4] = {9, 8, 7, 6};     // 設定「行」腳位
const byte rowPins[4] = {13, 12, 11, 10}; // 設定「列」腳位
const char keymap[4][4] = {     // 設定按鍵的「行、列」代表值
    {'1','2','3','A'}, 
    {'4','5','6','B'}, 
    {'7','8','9','C'},
    {'*','0','#','D'}
};

byte i, j;      // 暫存迴圈的索引數字
byte scanVal;   // 暫存掃描到的按鍵值

void setup(){
  Serial.begin(9600);

  for (i = 0; i < = 3; i++) {
    pinMode(rowPins[i], INPUT);
    pinMode(colPins[i], OUTPUT);
    digitalWrite(colPins[i], HIGH);
    digitalWrite(rowPins[i], HIGH);
  }
}

void loop() {
  for (i = 0; i <= 3; i++) {
    for (j = 0; j <= 3; j++) {
      digitalWrite(colPins[j], LOW);
      scanVal = digitalRead(rowPins[i]);

      if (scanVal == LOW) {    // 如果輸入值是「低電位」…
        Serial.println(keymap[i][j]);  // 輸出按鍵代表的字元
        delay(200);  // 掃描按鍵的間隔時間
        digitalWrite(colPins[j], HIGH);
        break;       // 跳出迴圈
      }
      digitalWrite(colPins[j], HIGH);
    }
  }
}

Keypad程序库的运作方式大致上面的程序相同,主要是多了消除弹跳(debounce)的程序(相关说明请参阅第四章「解决开关信号的弹跳问题」一节,4-15页),而且按键扫描的间隔时间是透过比对时间差,而非使用delay。上面的程序用于帮助理解扫描按键的原理,在实际项目制作上,请使用Keypad程序库。

via

坐沙发

发表评论