16개의 버튼을 사용할 일이 얼마나 많을지는 모르겠습니다만… 아두이노의 기초 과정인 digital input, digital output 을 연습하기에는 더할나위 없는 모듈이 키패드 모듈입니다.

키패드 모듈은 16개의 푸시 버튼이 4×4 행렬로 배치되어 있습니다.

모듈은 8개의 핀을 가지고 있는데, 1~4 까지 4개의 핀은 버튼을 가로로 연결하고 있습니다. 그리고 5~8까지 4개의 핀은 버튼을 세로로 연결하고 있습니다. (이 배치는 모듈에 따라 틀립니다.)

keypad_2

즉, 1~4 가로로 연결된 회로와 5~8 세로로 연결된 회로가 만나는 지점, 16개의 교차점에 버튼이 올라가 있는겁니다. 버튼을 누르면 두 개의 회로가 서로 연결되겠죠.

hex-keypad-arduino

따라서 각 버튼이 눌러졌는지 확인하기 위해서는 5~8 핀에 순서대로 HIGH 또는 LOW 신호를 넣어주고, 1~4 핀으로 들어오는 신호가 무엇인지를 체크하면 됩니다.

예를 들어 2행, 2열에 있는 버튼을 체크한다고 가정해보죠.

keypad_example

그럼 세로로 연결된 6번 핀을 OUTPUT 모드로 초기화하고 HIGH 신호를 넣어줍니다. 그리고 2번 핀을 INPUT 모드로 초기화 하고 HIGH 신호가 들어오는지 읽으면 됩니다. 물론 값이 제대로 들어오기 위해서는 2번 핀에 pull-down 저항을 연결해줘야 합니다. 그래야 버튼이 눌러지지 않았을 때는 LOW 값을 읽습니다.

예제 소스

간단하게 1행1열 버튼과 2행 2열 버튼이 눌러졌는지 확인하는 소스코드를 확인해 보겠습니다. 키패드 모듈을 구했는데 8개의 핀이 어떻게 연결되어 있는지 모를 때 이 코드를 응용해 보시면 됩니다.

void setup() {
    Serial.begin(9600);
    pinMode(2, INPUT_PULLUP);
    pinMode(3, INPUT_PULLUP);
    pinMode(6, OUTPUT);
    pinMode(7, OUTPUT);
    digitalWrite(6, HIGH);
    digitalWrite(7, HIGH);
}


void loop() {
    digitalWrite(6, LOW);
    Serial.print("pos 1, 1 = ");
    Serial.println(digitalRead(2));
    digitalWrite(6, HIGH);
    
    digitalWrite(7, LOW);
    Serial.print("pos 2, 2 = ");
    Serial.println(digitalRead(3));
    digitalWrite(7, HIGH);
    delay(1000);
}

1행1열 버튼과 2행 2열 버튼을 체크하기 위해서는 총 4개의 디지털 핀을 사용해야 합니다.

keypad_example2

여기서는 1행과 2행으로 들어오는 신호를 읽을 디지털 핀 2개 – D2, D3 를 사용합니다. 5열과 6열로 신호를 보내기 위해서 D6, D7 핀을 사용했습니다.

  • 키패드 1 –> 아두이노 D2
  • 키패드 2 –> 아두이노 D3
  • 키패드 5 –> 아두이노 D6
  • 키패드 6 –> 아두이노 D7

그리고 여기서는 저항을 사용하지 않고 값을 읽기 위해서 아두이노의 내부 풀업저항(internal pull-up) 기능을 사용합니다. 내부 풀업저항을 활성화 하기 위해서 아래와 같이 D2, D3 핀을 INPUT_PULLUP 모드로 초기화 했습니다.

    pinMode(2, INPUT_PULLUP);
    pinMode(3, INPUT_PULLUP);

이제 D2, D3 핀은 버튼이 눌러지지 않았을 때 내부 풀업저항에 의해 HIGH 신호를 읽게 됩니다. 버튼이 눌러졌을 때는 LOW 신호가 읽어지도록 해야겠죠. 그래서 loop() 반복함수 안에서 output 신호를 주는 D6, D7 핀을 순서대로 LOW 출력으로 바꿉니다.

D6 핀을 LOW 상태로 바꾸고 D2, D3 핀으로 들어오는 값이 어떻게 되는지를 판단하죠. 그리고 D7 핀을 LOW 상태로 바꾸고 D2, D3를 체크합니다.

이렇게 체크하면 1행1열 버튼부터 2행2열 버튼까지 총 4개의 버튼이 눌러졌는지 판별할 수 있습니다.

그럼 16개의 버튼 전부를 체크하고 싶다면?? 1~4행을 담당하는 4개의 디지털 핀(INPUT 체크)과 5~8열을 담당하는 4개의 디지털 핀(OUTPUT 제어)을 사용하면 됩니다.

  • 키패드 1~4 –> 아두이노 D2~D5 (INPUT 체크)
  • 키패드 5~8 –> 아두이노 D6~D9 (OUTPUT 제어)

아두이노 D6~D9번 핀은 digitalWrite() 함수를 이용해서 순서대로 LOW 신호를 주고, 아두이노 D2~D5번 핀은 digitalRead() 함수를 통해 들어오는 신호를 읽으면 되겠죠. 저항을 사용하기 싫은 경우 1~4번 핀(아두이노 D2~D5) 핀은 pinMode() 함수로 초기화 할 때 INPUT_PULLUP 모드로 초기화 해줍니다.

아래는 각 키 입력을 체크하는 소스코드입니다.

setup() 함수까지는 아래와 같이 처리합니다.

const int numRows= 4;
const int numCols= 4;

int pinRows[numRows] = {2, 3, 4, 5};
int pinCols[numCols] = {6, 7, 8, 9};

void setup() {
    Serial.begin(9600);    // init Serial communication
    
    // initialize row pins as INPUT_PULLUP
    for(int i=0; i<numRows; i++) {
        pinMode(pinRows[i], INPUT_PULLUP);
    }
    
    // initialize column pins as OUTPUT
    for(int j=0; j<numCols; j++) {
        pinMode(pinCols[j], OUTPUT);
        digitalWrite(pinCols[j], HIGH);    // set initial output as HIGH
    }
}

loop() 반복 함수에서는 for 반복문을 2중으로 돌면서 버튼입력을 확인합니다.

void loop() {
    // Check input
    for(int j=0; j<numCols; j++) {
        digitalWrite(pinCols[j], LOW);    // set as LOW to check button press
        for(int i=0; i<numRows; i++) {
            if( digitalRead(pinRows[i]) == LOW ) {    // check input. LOW is pressed
                Serial.print("Pressed row=");
                Serial.print(i);
                Serial.print(", column=");
                Serial.println(j);
            }
        }
        digitalWrite(pinCols[j], HIGH);    // set as default (HIGH)
    }
    delay(500);
}

버튼을 누르면 눌려진 버튼이 무엇인지 아두이노 개발환경의 Serial monitor 를 통해 확인하실 수 있습니다.

직접 코드를 만들어서 사용하셔도 되고, 아래의 라이브러리를 이용해서 사용하셔도 됩니다.