아두이노 커스텀 기계식 키보드 만들기 [#1 키보드 메트릭스 테스트]
프로젝트/커스텀 키보드

아두이노 커스텀 기계식 키보드 만들기 [#1 키보드 메트릭스 테스트]

반응형

최근 대학원 연구실 친구를 따라 커스텀 키보드에 입문하게 되어서 커스텀 키보드 하나를 만들게 되었다.

 

 

나름대로 윤활도 하고 잘 사용 중인데 이참에 스위치를 제외한 내부 부품까지 자작해보고 싶다는 생각이 들어서 자료를 찾아보게 되었다.

 

일반적으로 키보드는 70 키 이상의 많은 수의 스위치를 사용하게 되는데 아두이노와 같은 컨트롤러는 이렇게 많은 수의 스위치를 제어하는 것이 사실상 불가능하다. 그래서 키보드 메트릭스라는 방법을 사용해야 하는데 아래와 같은 회로로 구성된다.

 

https://worklab.tistory.com/3

 

각각의 Col에 전기신호를 순차적으로 주고 스위치를 누르면 어떤 위치의 Row에 전기 신호가 들어오는지 읽어 컨트롤러의 핀 개수를 줄이는 방법이다. 이때 다이오드가 필수적인데, 2개 이상의 스위치를 눌렀을 때 엉뚱한 위치에 신호가 들어오는 고스트 키 현상을 막기 위한 방법이다.

 

위 회로는 흔히 넘패드라 불리는 숫자키를 위한 키보드 매트릭스인데 실제 아두이노에 붙여 사용하기 위해서는 HID 동작이 가능한 아두이노 레오나르도, 프로 마이크로 종류가 필요하다. 아래와 같이 회로를 구성해 봤다.

 

 

최종적으로 OLED 디스플레이도 달아볼 계획이기 때문에 테스트용으로 디스플레이도 달아두었다. 이제 기판을 설계해야 하는데 항상 자주 사용하는 EasyEDA를 사용해 기판을 제작했다.

 

 

EasyEDA 사용법은 전에 필자가 강좌를 만들어둔 것이 있으므로 참고하면 된다. 사실 그 사이 인터페이스가 많이 바뀌었는데 핵심적인 기능들은 그대로라 도움이 될 것이다.

 

반응형
 

'PCB 제작 강좌' 카테고리의 글 목록

 

diy-project.tistory.com

 

필자가 설계한 PCB의 거버 파일은 아래 링크에서 다운이 가능하다.

 

Gerber_PCB_Keyborad_test.zip
0.12MB

 

이제 PCB를 주문 제작 단계가 남았다. 가장 쉽고 저렴하게 이용 가능한 업체는 JLCPCB이다.

 

https://jlcpcb.com/KOR

 

PCB Prototype & PCB Fabrication Manufacturer - JLCPCB

모든 단계에서 시간과 비용 절감한다. 클릭하시면 동영상을 볼 수 있습니다.

jlcpcb.com

필자가 오래전부터 애용하는 업체인데 (한국에 거의 알려지지 않았던 시기부터) 국내 PCB 제작 업체에 비해 훨씬 저렴하고 배송도 빠르다. 위 링크는 현재 JLCPCB에서 한국어로 되어있는 랜딩 페이지이며 위 링크로 접속시 새로운 한국 고객을 위해서 30달러의 쿠폰도 제공한다!

 

먼저 Order now로 들어가서 본인의 거버 파일을 업로드한다.

 

 

기판 크기가 꽤 큰 편임에도 9.7달러로 상당히 저렴하다. 게다가 대부분의 업체의 경우 기판의 색을 변경하면 추가금이 꽤 붙는데 JLCPCB는 추가금 마저 없다. 저렴한 배송업체를 사용하면 배송비도 적다.

 

주문하면 위와 같이 공정 단계도 알려준다. 배송까지 포함하면 5일 정도면 PCB를 받아볼 수 있다.

 

보라색이 참 마음에 든다

 

마지막으로 조립 단계가 남았다. 스위치는 집에 굴러다니던 저렴한 기계식 키보드에서 뽑은 오테뮤 갈축을 사용했고, 컨트롤러는 아두이노 프로 마이크로 (호환 보드), 다이오드는 일반적인 대부분의 다이오드를 사용 가능하다. OLED 디스플레이는 128 X 32 사이즈의 I2C 방식을 사용한다.

 

코드는 아래와 같이 작성하여 업로드했다. (https://worklab.tistory.com/3의 코드를 참고했는데, 행열 번호를 변경했고 디스플레이 코드를 추가했다.)

 

#include <Keyboard.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

#define OLED_RESET     3
Adafruit_SSD1306 OLED(OLED_RESET);

#define PIN_ROW_1 19
#define PIN_ROW_2 18
#define PIN_ROW_3 15
#define PIN_ROW_4 14
#define PIN_ROW_5 16
#define PIN_COL_1 4
#define PIN_COL_2 5
#define PIN_COL_3 6
#define PIN_COL_4 7

#define MATRIX_SIZE 20

const byte keycode[MATRIX_SIZE] = {219, 220, 221, 222,   // N / * -
                                   231, 232, 233,   0,   // 7 8 9
                                   228, 229, 230, 223,   // 4 5 6 +
                                   225, 226, 227,   0,   // 1 2 3
                                   234,   0, 235, 224};  // 0   . E

bool bufferA[MATRIX_SIZE];
bool bufferB[MATRIX_SIZE];

bool *prevBuffer = &bufferA[0];
bool *curBuffer = &bufferB[0];
bool *tempBuffer;

#define DEBOUNCE_DELAY 20

unsigned long lastChange[MATRIX_SIZE];
unsigned long curMillis;

int WAKE_COUNT = 0;

void setup() {
  for (int i = 0; i < MATRIX_SIZE; i++) {
    bufferA[i] = 1;
    bufferB[i] = 1;
  }

  pinMode(PIN_ROW_1, OUTPUT);
  pinMode(PIN_ROW_2, OUTPUT);
  pinMode(PIN_ROW_3, OUTPUT);
  pinMode(PIN_ROW_4, OUTPUT);
  pinMode(PIN_ROW_5, OUTPUT);

  digitalWrite(PIN_ROW_1, HIGH);
  digitalWrite(PIN_ROW_2, HIGH);
  digitalWrite(PIN_ROW_3, HIGH);
  digitalWrite(PIN_ROW_4, HIGH);
  digitalWrite(PIN_ROW_5, HIGH);

  pinMode(PIN_COL_1, INPUT_PULLUP);
  pinMode(PIN_COL_2, INPUT_PULLUP);
  pinMode(PIN_COL_3, INPUT_PULLUP);
  pinMode(PIN_COL_4, INPUT_PULLUP);

  OLED.begin();
  OLED.clearDisplay();
}


void loop() {
  digitalWrite(PIN_ROW_1, LOW);
  curBuffer[0] = digitalRead(PIN_COL_1);
  curBuffer[1] = digitalRead(PIN_COL_2);
  curBuffer[2] = digitalRead(PIN_COL_3);
  curBuffer[3] = digitalRead(PIN_COL_4);
  digitalWrite(PIN_ROW_1, HIGH);
  digitalWrite(PIN_ROW_2, LOW);
  curBuffer[4] = digitalRead(PIN_COL_1);
  curBuffer[5] = digitalRead(PIN_COL_2);
  curBuffer[6] = digitalRead(PIN_COL_3);
  digitalWrite(PIN_ROW_2, HIGH);
  digitalWrite(PIN_ROW_3, LOW);
  curBuffer[8] = digitalRead(PIN_COL_1);
  curBuffer[9] = digitalRead(PIN_COL_2);
  curBuffer[10] = digitalRead(PIN_COL_3);
  curBuffer[11] = digitalRead(PIN_COL_4);
  digitalWrite(PIN_ROW_3, HIGH);
  digitalWrite(PIN_ROW_4, LOW);
  curBuffer[12] = digitalRead(PIN_COL_1);
  curBuffer[13] = digitalRead(PIN_COL_2);
  curBuffer[14] = digitalRead(PIN_COL_3);
  digitalWrite(PIN_ROW_4, HIGH);
  digitalWrite(PIN_ROW_5, LOW);
  curBuffer[16] = digitalRead(PIN_COL_1);
  curBuffer[18] = digitalRead(PIN_COL_3);
  curBuffer[19] = digitalRead(PIN_COL_4);
  digitalWrite(PIN_ROW_5, HIGH);

  curMillis = millis();
  
  for (int i = 0; i < MATRIX_SIZE; i++) {
    if (prevBuffer[i] == curBuffer[i]) continue;
    if (curMillis - lastChange[i] < DEBOUNCE_DELAY) {
      curBuffer[i] = prevBuffer[i];
      continue;
    } else
      lastChange[i] = curMillis;

    lastChange[i] = curMillis;

    if (curBuffer[i] == 0){
      Keyboard.press(keycode[i]);
      if (WAKE_COUNT == 0){
        OLED.begin();
        OLED.clearDisplay();
        OLED.setTextSize(2);
        OLED.setTextColor(WHITE);
        OLED.setCursor(0, 0);
        OLED.println("Hello!");
        OLED.display();

        WAKE_COUNT++;
      }
    }
    else{
      Keyboard.release(keycode[i]);
    }
  }
  
  tempBuffer = prevBuffer;
  prevBuffer = curBuffer;
  curBuffer = tempBuffer;
}

 

이상하게 보드를 재부팅후 디스플레이가 보이지 않는 문제가 있어서 키보드 입력을 받으면 한번 디스플레이를 초기화 하는 코드를 넣어 주었다. 완성 이미지는 아래를 참고하자.

 

다이오드 개수가 부족해서 하나만 종류가 다르다. 물론 동작에는 이상이 없다.

키보드 입력도 정상적이다. 테스트해보면 최대 7~8키 정도 동시입력이 되는 것 같다.

 

넘패드 테스트가 완료되었으니 조만간 실제 크기의 키보드를 제작해볼 계획이다.

 

이 글은 JLCPCB로부터 원고료를 지원받았습니다.

 

반응형
    # 테스트용