2012년 12월 26일 수요일

[작업중] 블루투스 기계식 키보드 만들기

1. 서론

Bluetooth 기능이  필요하기 때문에 무엇보다도

HID 규격을 지원하는 Bluetooth 모듈의 선택이 우선되어야 한다.

회사에서는 어떨지 모르겠지만 개인이 구할 수 있는 것들은

그리 많은 것들이 보이지 않는다. 아래의 링크가 거의 유일한 것 같다.

https://www.sparkfun.com/products/10253

가격은 $15으로 착하지 못하다.

모듈이 SMD 방식으로 부착되어야 하기 때문에

우선은 $45나 하는 모듈로 구매해야한다.

https://www.sparkfun.com/products/10938

해볼까 말까?

Bluetooth 모듈 말고도 나머지 기계적인 부분과 하우징

그리고 Keyboard의 Matrix를 처리할 컨트롤러 같은 문제 들이 남는다.


2. 설계


2.1. 하우징 설계


이건 많은 키보드를 보고 설계를 하는 작업이 우선 되어야 겠다.

한번 제작하면 금액과 연결되는 부분이기 때문에

고급 스러운 알류미늄으로 제가하는 것 보다.

우선은 아크릴을 이용해서 제작해 보아야 겠다.

2.2. Keyboard Matrix 컨트롤러 


이것은 조금만 찾아 보면 수월하게 검색이 가능하다.

이미 USB 인터페이스로 제작해서 사용하고 있는 것들이 있다.

그 들 중 대부분은 AVR을 이용하고 있다.

가장 대표적인  것이 Aikon Keyboard 컨트롤러다.
혹은 Open source keyboard controller 를 키워드로 해서 검색하면 많은 것을 찾을 수 있다.

하지만, Aikon Keyboard 컨트롤러는 otd 사이트에 가입하지 않으면 볼 수 없다.

그래서 AVR을 가지고 비슷한 작업을 수행한 다른 공개된 작업들을 찾아 보았다.


위의 두 프로젝트의 회로도를 살펴보면 USB 통신을 위한 전용칩이 없다.

물론, AVR 내부 블럭에도 USB 통신을 위한 모듈이 존재 하지 않는다.

소스코드를 살펴보면 알 수 있겠지만, 

Full speed 의 USB Stack을 소프트웨어로 구현하고 있다.

이 방식은 이미 AVR의 ISP 프로그래머인 USBtinyISP AVR Programmer를 제작할때

인식하지도 않고 사용해본 경험이 있다.

가끔 연결이 해제되는 경우가 있었지만, 대체적으로 잘 작동하였던 것으로 기억한다.

다음 사이트에 가면 소스 코드를 구할 수 있다.


이것은 예전에 TinyAVR 프로그래머에서 사용하였던 기억이 있다. 잘 동작하였던 것으로 기억된다. 

2.2.1. 소프트웨어로 구현된 USB 방식의 Keyboard 컨트롤러


그럼 V-USB의 사용법을 살펴보고 나머지 두 작업의 소스코드를 살펴보아야 겠다.

rump 소스코드는 대부분이 V-USB의 코드가 차지 하고 있고

나머지는 키코드를 처리하는 main.c와 ibm 키보드의 키맵 해더파일이 차지하고 있다.

V-USB는 usbdrv.c와  oddebug.c의 c 언어로된 소스코드와

usbdrvasm.S의 어셈블리 코드로 구성되어 있다. 

모든 동작의 주된 알고리즘은 main.c에 구현되어져 있다.

main.c에 USB HID 장치로 인식되기 위한 descriptor가 정의 되어 있다.

이와 같은 부분은 용도에 따라 나중에 변경 가능하다.

아마도, VID, PID 것은 바꾸어야 할 것이다. 기존의 드라이버와 충돌이 발생할 수 도 있으니.

/* USB report descriptor (length is defined in usbconfig.h)
   This has been changed to conform to the USB keyboard boot protocol */
char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] PROGMEM = {
0x05, 0x01,            // USAGE_PAGE (Generic Desktop)
0x09, 0x06,            // USAGE (Keyboard)
0xa1, 0x01,            // COLLECTION (Application)
0x05, 0x07,            //   USAGE_PAGE (Keyboard)
0x19, 0xe0,            //   USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7,            //   USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00,            //   LOGICAL_MINIMUM (0)
0x25, 0x01,            //   LOGICAL_MAXIMUM (1)
0x75, 0x01,            //   REPORT_SIZE (1)
0x95, 0x08,            //   REPORT_COUNT (8)
0x81, 0x02,            //   INPUT (Data,Var,Abs)
0x95, 0x01,            //   REPORT_COUNT (1)
0x75, 0x08,            //   REPORT_SIZE (8)
0x81, 0x03,            //   INPUT (Cnst,Var,Abs)
0x95, 0x05,            //   REPORT_COUNT (5)
0x75, 0x01,            //   REPORT_SIZE (1)
0x05, 0x08,            //   USAGE_PAGE (LEDs)
0x19, 0x01,            //   USAGE_MINIMUM (Num Lock)
0x29, 0x05,            //   USAGE_MAXIMUM (Kana)
0x91, 0x02,            //   OUTPUT (Data,Var,Abs)
0x95, 0x01,            //   REPORT_COUNT (1)
0x75, 0x03,            //   REPORT_SIZE (3)
0x91, 0x03,            //   OUTPUT (Cnst,Var,Abs)
0x95, 0x06,            //   REPORT_COUNT (6)
0x75, 0x08,            //   REPORT_SIZE (8)
0x15, 0x00,            //   LOGICAL_MINIMUM (0)
0x25, 0x65,            //   LOGICAL_MAXIMUM (101)
0x05, 0x07,            //   USAGE_PAGE (Keyboard)
0x19, 0x00,            //   USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65,            //   USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00,            //   INPUT (Data,Ary,Abs)
0xc0                   // END_COLLECTION
};

하드웨어 구성은 간단하기 때문에

포트를 설정하고 데이터를 감시하는 기능을 위주로 살펴 보도록 하자.

int main(void) {
uchar updateNeeded = 0;
uchar idleCounter = 0;
memset(reportCache, 0, sizeof(reportCache));
wdt_enable(WDTO_2S); /* Enable watchdog timer 2s */
hardwareInit(); /* Initialize hardware (I/O) */
odDebugInit();
usbInit(); /* Initialize USB stack processing */
sei(); /* Enable global interrupts */
/* Main loop */
for (;;) {
/* Reset the watchdog */
wdt_reset();
/* Poll the USB stack */
usbPoll();
/* Scan the keyboard for changes */
updateNeeded |= scankeys();
/* Check timer if we need periodic reports */
if (TIFR & (1 << TOV0)) {
/* Reset flag */
TIFR = 1 << TOV0;
/* Do we need periodic reports? */
if (idleRate != 0) {
if (idleCounter > 4) {
/* Yes, but not yet */
/* 22 ms in units of 4 ms */
idleCounter -= 5;
} else {
/* Yes, it is time now */
updateNeeded = 1;
idleCounter = idleRate;
}
}
}
/* If an update is needed, send the report */
if(updateNeeded && usbInterruptIsReady()) {
updateNeeded = 0;
usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
}
}
return 0;
}
위의 코드의 초기화 과정을 간단히 시간 순서대로 풀어 써보면 다음과 같다.

  1. 캐시 변수 초기화
  2. 왓치도그 타이머 초기화(비정상적인 상황 발생시 강제적인 하드웨어 리셋을 하기위한 기능으로 추정됨)
  3. 하드웨어 초기화 (키보드의 메트릭스를 구성하는 Row와 Column입력을 감시하기위한 입력 포트 설정과 Keyboard의 상태 LED를 표시하기위한 출력 포트 설정을 할 것으로 추정됨)
  4. 그리고 나머지 디버깅을 위한 도구 초기화와 V-USB의 초기화, 마지막으로 모든 가능한 인터럽트처리를 위한 전역 인터럽트를 사용 가능하도록 설정하여 모든 준비를 마친다.
  5. 그리고 메인 루프의 처리과정은 다음과 같이 풀어 쓸 수 있다.
    1. 왓치도그 타이머 리셋(강제적인 리셋이 안되도록 주기적으로 타이머를 갱신 시켜줌)
    2. 소프트웨어 USB 스텍 처리
    3. Keyboard의 값 처리 (이 부분이 무엇보다도 중요한 부분이 되겠다.)
    4. 일정 시간 주기로 어떠한 작업을 하기 위한 타이머 처리 루틴.
    5. 그리고 일정 시간이 되고 USB의 인터럽트 전송의 처리가 가능한 상태이면 USB의 인터럽트 전송을 이용하여 reportBuffer값들을 호스트 컴퓨터에 전송한다. 다른 말로 키보드 값을 전송한다.
  6. 대부분의 것들은 명확해 졌지만 가장 중요한 함수인 Keyboard의 Matrix 구조에서 입력되는 값을 처리하는 루틴인 scankeys()은 아직 미지의 영역이다. 이 부분에 대해서 좀더 자세히 살펴보도록 하자.
  7. scankeys()함수를 알아보기 이전에 Keyboard의 matrix 구조가 어떻게 이루어 지는지 그리고 Mechanical Keyboard의 배선과 고려해야 할 사항은 없는지 기존의 자작 Mechanical Keyboard를 중심으로 제작 과정을 살펴보아야 한다.

2.2.2. USB 기능이 내장된 AVR을 이용한 Keyboard 컨트롤러







Keyboard 컨트롤러의 초기화시 USB 접속이 이루어 졌는지 아닌지를 판단하거나

Bluetooth를 통해 호스트 컴퓨터와 접속이 가능한지에 대한 

선택이 앞으로 추가해야할 작업으로 판단되어 진다.

우선은 USB 연결과 Bluetooth 연결이 모두 가능하도록 하는 것이 목적이지만,

 AVR의 성능에 따라서 여러가지의 선택사항을 고려해야 한다.
      • USB의 접속이 이루어 졌는지 판단하는 루틴
      • Bluetooth 접속이 가능하지 판단하는 루틴
      • 이것도 저것도 아니면 대기하는 루틴
      • 혹은 물리적인 스위치로 USB 연결과 Bluetooth 연결을 선택하도록 하는 루틴
      • 아니면 USB 전송과 Bluetooth 전송을 동시에 가능하도록 해야할 지 판단하는 루틴
    • 그리고 VUSB를 사용하기 위해서 메인 클럭에 제한이 있다. 12Mhz, 12.8Mhz, 15Mhz, 16Mhz, 16.5Mhz, 18Mhz, 20Mhz등이다. 우선 CPU로 사용하려고 하는 ATmega16을 16Mhz로 동작하도록 해야 겠다. 그럼 5V로 동작해야 한다.
    • Bluetooth로 동작할 경우 Battery를 전원으로 사용해야 하기 때문에 lithum-Ion 혹은 Lithum-Polymer Battery의 3.7V를 5V로 Step Up 해주는 레귤레이터의 사용이 불가피하다. 그리고 전원 효율을 생각하면 무조건 Switching 방식으로 가야하고 그와 더불어 USB포트로 연결되었을때 3.7V Battery를 충전하는 IC도 추가적으로 사용해야 할 것 같다.
    • 결과적으로 전원부의 설계가 많이 복잡해 질것같다. 다행히 Arduino를 지원하는 Battery를 포함한 Shield들이 있으서 그것을 참고하면 많은 도움이 될 것 같다.
  • 우연히 얻게된 
  • 키보드 PCB 제작
    • 자작으로 단면 PCB 제작이 답인 것 같다.
    • 우선 단면 동판이 구입 가능한지 물론 A4용지 정도의 크기로 말이다. 그럼 우선 제작할 수 있는 키보드는 Tenkeyless만 가능 할 것 같다.
    • 하드웨어의 구성은 AVR 우선적으로 필요한 포트는 USB 접속을 위한 2개의 포트, Bluetooth 모듈과의 연결에 필요한 RS232 규격의 Tx, Rx포트, 나머지 Keyboard의 Matrix구조와 연결할 24개의 포트가 필요하다. 그러면 총 28개의 포트가 최소한 필요로 한다.

작업중..........................


댓글 7개:

  1. 작성자가 댓글을 삭제했습니다.

    답글삭제
  2. 스위치를 누르면 키보드의 특정키를 입력해 주는 장치를 만들고 싶은데요.
    PC와 연결은 USB 또는 Bluetooth 방식으로 하고 싶고요.
    작성하신 글을 읽고 도움을 받으면 가능할 것 같다는 생각이 들어서 이렇게 댓글을 남기게 되었습니다.
    스위치는 2개만 사용하고 딥스위치로 세팅해서 각각 세팅한 특정키를 입력해주면 되는데요
    어떻게 시작해야 할지 모르겠네요. 도움을 부탁드립니다.

    답글삭제
    답글
    1. 4thefree님이 원하시는 기능을 가지고 있는 Arduino보드를 구매하시면 쉽게 구현이 가능하실 겁니다. http://arduino.cc/en/Reference/MouseKeyboard 이 링크에 보시면 마우스와 키보드 기능을 이용해서 PC에 입력하는 기능을 살펴 보실 수 있을 겁니다. 좀 더 구체적으로 보시려면 Examples을 살펴보세요. 제가 이해하기로는 4thefree님이 원하시는 기능가 가장 흡사한 KeyboardMessage: Sends a text string when a button is pressed(http://arduino.cc/en/Tutorial/KeyboardMessage).을 살펴보세요. 하지만, Bluetooth 기능은 구현해 볼 기회가 없어서 뭐라 이야기 해드릴 것이 없습니다.

      삭제
    2. 아 그리고 위의 예제를 구현하려면 Arduino Leonardo, Micro, or Due board 중의 하나의 arduino 보드를 구매하셔야 합니다.

      삭제
  3. 빠른 댓글 감사합니다. 말씀해 주신 내용을 좀 더 공부해야겠네요.
    제가 프로그램 개발자는 아니라서 많이 배워야 할 것 같아요. ^^
    감사합니다.~~

    답글삭제
  4. 안녕하세요? 지난번 스위치로 특정키를 넘겨주는 장치 만드는 것에 여러 가지고 도움을 주셔서 감사합니다.
    인터넷을 찾다가 키보드IC인 Easy Input이라는 IC가 있어서 그걸로 개발이 가능할 것 같아 하나를 구매했는데요.
    Clock Low/High 라는 내용이 이해가 되질 않아서 염치없이 이렇게 문의 드립니다.
    www.radovan.org 에서 판매하는 제품인데요. 메뉴얼을 보아도 잘 이해가 되질 않습니다.
    http://www.radovan.org/EasyInput/spec.pdf
    클럭을 Low에서 High로 주고 데이터를 주고 다시 Low 로 해야 특정글자가 들어간다는 것 같은데요.
    클럭을 Low/High로 주는 방법을 잘 모르겠습니다.
    혹시 클럭을 발생하는 부품을 달아주어야 하나요?
    데이터를 전달하기 위해서 8개 짜리 딥스위치 2개와 모드용 2개 짜리 딥스위치 1개 그리고 특수키용 3개 짜리 딥스위치 1개를 연결하고
    버튼 스위치 2개를 이용해서 나름 회로도를 그렸는데 작동이 되질 않네요. ㅠ.ㅠ
    현재 막힌 것이 데이터를 전송하기 위해서 클럭을 로우에서 하이로 올렸다가 다시 데이터를 올려 놓고 클럭을 로우로 떨어 뜨려야 데이터가
    전달 되는 것 같은데요.
    8개 짜리 딥스위치에 버튼을 달아서 그것을 누르면 설정한 문자가 전달되는 방식으로 구현이 어려울까요?
    혹시 클럭을 로우/하이로 자동 발생시키는 부품을 달아야 하나요?
    너무 답답해서 이렇게 길게 댓글을 씁니다.
    답글 주시면 너무 감사하겠습니다.

    답글삭제
    답글
    1. 안녕하세요. 해당칩의 메뉴얼을 보니 병렬 혹은 직렬 방식의 인터페이스를 가진 USB Keyboard를 에뮬레이션 해주는 칩으로 판단됩니다. 이 칩만에 몇 가지 스위치를 이용해서 사용하시고자 하는 의도가 느껴지는데 제 생각에는 이칩의 주된 용도는 기존에 개발되어 있던 시스템에 키보드 용도를 추가하고자 할때 도움을 주는 정도입니다. 인터 페이스 자체가 병렬 혹은 직렬 인터페이스를 다룰 수 있는 독립적인 시스템을 요구하고 있습니다. 꼭 이칩을 사용하시고자 한다면 간단한 8bit 씨피유라도 추가적으로 사용하셔야 클럭이라던가 딥스위치입력에 따른 데이터 생성이 가능할 겁니다. 만약 기존에 구성된 시스템이 없이 새로 시작 하시는 것이라면 저라면 이와 같이 복잡하게 시스템을 구성하는 것보다. USB 키보드 기능이 가능하면서 dip 스위치 입력에 따른 데이터 생성이 가능한 arduino 보드를 사용할 겁니다. 프로그램을 걱정하시는 것 같은데 그리 어렵지 않습니다. 4thefree님이 조금만 찾아 보시면 혹은 저에게 이와 같이 질문 주시면 쉽게 해결 하 실 수 있는 문제입니다.

      삭제