키보드를 만듭시다. 어때요~ 참 쉽죠? (9)
키맵을 런타임에 업데이트 할 수 있도록 코드를 작성했다. 문제는 어떻게 업데이트 하느냐를 결정하는 것이다. 여러가지 방법이 가능하다. 먼저 가장 단순한 방법으로 UART 터미널을 통해 XMODEM으로 전송하는 것이다. 전통적이고 오래된 방법이다. 그런만큼 구현이 쉽다. 또는 DFU(Device Firmware Upgrade)라고 부르는 표준도 있다. 그리고 내가 사용하기로 결정한 방법인 MSC(Mass Storage Class)를 이용하기도 한다. MSC는 쉽게 말해서 대용량 저장 장치를 펌웨어로 만든는 것이다.
키맵을 런타임에 업데이트 하려고 생각했더니 같은 방법으로 펌웨어도 실시간 업데이트가 가능할 것같다는 생각이 들었다. 원리상 다를게 없다. 그래서 어떤 방식으로 키맵과 펌웨어를 업데이트하는게 편하고 좋을지 고민해 봤다.
키보드는 USB로 연결되 있다
키보드는 USB로 연결되 있다. 그래서 키보드에 다른 연결을 하지 않고도 충분히 USB를 이용하는 프로토콜을 구현할 수 있다. 펌웨어만 적당히 잘 만들면 된다. 그래서 나는 USB 만으로 할 수 있는 호스트와 키보드간 데이터 전송 방법을 몇개 찾아 봤다.
그리고 몇 가지 조건을 세웠다.
- 호스트(윈도우, 리눅스, 맥 등)에 별도로 디바이스 드라이버를 설치하지 않아야 할 것
- 펌웨어 구현이 쉬워야 할 것
- 호스트에 업데이트 전용 프로그램 구현이 쉬워야 할 것
USB로 전용 프로토콜을 디자인하고 디바이스 드라이버를 만들어서 호스트에 설치하면 호스트와 키보드 펌웨어간 바이너리 데이터 전송이 가능하다. 그러나 이렇게 하면 배보다 배꼽이 더 크다. 디바이스 드라이버 만드는게 어디 쉬운 일인가.. 그래서 세운 조건이 호스트에 별도로 디바이스 드라이버를 설치하지 않아야 한다는 것이다. 이러려면 필연적으로 표준 프로토콜을 사용해야한다. 그것도 널리 알려진 표준 프로토콜을 써서 운영체제에 기본으로 포함된 디바이스 드라이버가 동작하게 해야 한다.
펌웨어도 복잡한 구현이 들어가면 골치 아프다. 재미로 하고 있는 작업이 진지해진다. 되도록이면 예제 코드를 구하기 쉬운 프로토콜이어야 하고 예제를 거의 그대로 갖다 붙어서 최소한만 수정해서 기능 구현이 완료되는 것이어야 한다. 나는 레고 놀이를 하고 싶지, 레고를 만들고 싶지 않다.
개발을 완료한 지금은 의미없는 것이지만 처음 구상할 때만해도 어떤 형태로든 키맵과 펌웨어를 키보드로 전송하는 클라이언트 프로그램이 필요할 것 같았다. 왜냐면 하다못해 UART로 XMODEM을 쓰더라도 터미널 프로그램이 필요하고 해당 터미널 프로그램이 XMODEM으로 파일 전송하는 기능이 있어야 하니까. 그래서 웬만하면 범용 프로그램으로 다운로드 클라이언트를 대신할 수 있어야 하고 그게 안된다면 파이썬 등으로 쉽게 구현 가능한 프로토콜이어야 한다고 생각했다.
그렇게해서 추린 후보는 아래 셋이다.
- VCP (Virtual Com Port)
- DFU (Device Firmware Upgrade)
- MSC (Mass Storage Class)
모두 USB 표준 프로토콜이다. 각각에 대해 알아보자.
Virtual Com Port
원래 PC에서 디바이스의 UART와 연결하는 방법은 예전에 달려 있던 25핀 COM 포트로 연결 하는 것이었다. 이 COM 포트를 직렬 포트(serial port)라고 부른다. 그래서 UART를 시리얼 포트라고도 부르고 COM 포트라고 부르기도하고 막 구분없이 섞어 부르는 것이다.
한 십여년전 메인보드에는 저렇게 페레럴포트(흔히 프린터 포트라고 부르는 긴 포트)와 시리얼 포트(COM 포트)가 달려 있었다. 더 옛날 USB가 나오기 전에는 USB 포트도 없고 페레럴, 시리얼, PS/2 포트만 있었다. 요즘 나오는 메인보드는 페레럴 포트와 시리얼 포트가 달려 나오는게 거의 없다. 대신 그 자리에 내장 그래픽 카드로 구동되는 HDMI나 DVI 포트가 있는 제품들이 주류다.
아무튼 요즘은 시리얼 포트가 메인보드에 붙어 나오지 않으므로 임베디드 개발자들은 UART를 사용할 방법이 마땅찮아 졌다. 그래서 나온 제품들이 FT232R 칩이나 CP2101칩 같은 것들이다. 디바이스의 UART 신호를 USB로 바꿔서 처리해주는 칩이다. 사실 이 UART 프로토콜은 사용 용도가 엄청 다양하다. 내가 임베디드 개발자라서 주로 디버깅 용도로 쓰는거지 사용하기에 따라서 호스트와 디바이스 통신 용도로 얼마든지 사용할 수 있다.
그래서 이 UART to USB 혹은 Serial to USB 칩은 매우 많은 디바이스들이 쓰고 있다. 그러다보니 웬만한 범용 운영체제는 이 칩의 디바이스 드라이버를 기본으로 가지고 있다. 왜냐면 이들 칩이 사용하는 UART to USB 프로토콜이 USB 표준 프로토콜이기 때문이다. 그래서 표준 프로토콜을 구현한 디바이스 드라이버만 운영체제가 지원하면 디바이스는 FT232R을 쓰든 CP2101을 쓰든 그냥 쓸 수 있다. 이 표준이 VCP다. 실제 하드웨어로 없는 COM 포트를 가상으로 만들어서 접속한다고 Virtual이 붙은거다.
컨트롤러에 USB 컨트롤러 하드웨어가 있으므로 펌웨어 구현으로 UART to USB 프로토콜을 구현하면 별도 칩을 따로 쓰지 않아도 VCP로 호스트와 연결할 수 있다. 그러면 윈도우에서는 COMn(COM8, COM9 같은 번호)으로 접속할 수 있고 리눅스에서는 /dev/ttyUSBn(ttyUSB0, ttyUSB1 같은 번호)로 접속할 수 있다.
가상 COM 포트가 만들어지면 예전에 시리얼 터미널 사용하던 것과 똑 같은 방법으로 접속해서 데이터를 디바이스와 주고 받을 수 있다. 그중 바이너리 데이터를 주고받을 수 있는 또 다른 프로토콜이 있다. 여러 종류가 있는데 그 중 하나가 XMODEM이다. 한 20년 전 쯤에는 전화선으로 접속하는 PC 통신이라는 것이 있었는데, 사실 telnet을 이용해 구축한 커뮤티니 서비스였다. 이 PC 통신에서 사용했던 파일 전송 규격중 하나가 XMODEM이다. 그만큼 역사가 긴 프로토콜이다.
중요한건 VCP로 시리얼 포트 연결을 하고 나서 XMODEM으로 호스트의 바이너리 데이터를 디바이스로 가져올 수 있다는 것이다. 이 바이너리 데이터는 키맵일 수도 있고 펌웨어 일 수도 있다. 그러면 펌웨어가 알아서 업데이트 하면 된다.
DFU (Device Firmware Upgrade)
일단 이름부터 아예 대 놓고 펌웨어 업그레이드 용으로 만든 프로토콜이다. 그래서 연결 이후에 바이너리 데이터를 제어하는 방법이 아주 단순하고 간단하다. 이름이 목적을 말해주고 있기에 스펙 자체도 단순하고 널리 쓰이기에 예제도 많다. 심지어 어떤 MCU들은 아예 부트롬 부트로더에 DFU를 포함하고 있기도 하다. 무슨 말이냐면, DFU를 지원하는 칩을 키보드 컨트롤러로 선택하면 런타임 펌웨어 업데이트는 그냥 된다는 말이다. 내가 선택한 컨트롤러 칩이 STM32F103인데 이 칩보다 조금 더 비싼 상위 제품 중에 STM32F4 시리즈가 있다. 이 칩은 부트롬 코드에서 DFU가 기본으로 된다고 한다.
리눅스에서는 dfu-util이라는 툴을 대부분 배포판에서 패키지 매니저로 다운받을 수 있다. 윈도우에서는 별도로 다운받으면 된다. 혹은 DfuSe같은 GUI 툴을 쓸 수 도 있다.
MSC (Mass Storage Class)
원래는 DFU로 구현을 하려고 다른 디바이스들은 DFU을 어떤식으로 지원하는지 찾던 중, 키보드랑 전혀 상관 없는 어떤 디바이스가 펌웨어 업데이트 기능을 MSC로 처리하는 것을 보고 나도 따라하기로 마음 먹었다. MSC는 흔히 윈도우에서 대용량 저장 장치로 인식되는 바로 그 장치다. 쉽게 말해 USB thumb 드라이브가 MSC다. 아니면 SD 카드 같은 것을 카드 리더기에 꼽고 USB를 연결하면 대용량 저장 장치로 나온다. 그런 것들에서 쓰는 프로토콜이 MSC다.
MSC는 목적이 파일 전송이다. 호스트의 파일을 USB 디바이스에 있는 스토리지에 저장하거나 디바이스의 스토리지에 있는 파일을 호스트로 읽거나. 그래서 그냥 파일 복사하듯 호스트에서 키맵 파일이나 펌웨어 바이너리 파일을 대용량 저장 장치에 복사하면 파일을 받아서 업데이트를 진행하면 된다.
최종 결정은 MSC
프로토콜 세 개를 검토한 결과 최종 선택은 MSC로 했다.
우선 디바이스 드라이버 설치가 필요없다. 대용량 저장 장치는 어디에서나 쓰기 때문에 대용량 저장 장치 디바이스 드라이버가 없는 범용 PC 운영체제는 없다고 봐도 된다. 마찬가지로 대용량 저장 장치는 디바이스 종류가 많다. 얼마나 많은 USB thumb 드라이브가 있는지 생각해보라. 또 얼마나 많은 카드리더기가 있는지 생각해 보라. 따라서 MSC 펌웨어는 SDK 기본 예제 중 하나다. 펌웨어 구현이 전혀 어렵지 않다. 엄밀히 말해서 펌웨어 구현 자체는 꽤 복잡하다. SCSI도 구현해야 하고 막 그런다. 그러나 예제가 많다. 내가 직접 구현할 것이 별로 없다. 남의 펌웨어 이해만 하면된다. 즉, 레고 놀이를 할 수 있다. 마지막으로 호스트에 아무런 프로그램을 설치하지 않아도 된다. 그냥 대용량 저장 장치로 인식되고 나면 윈도우 탐색기나 리눅스 파일 매니저에서 파일 복사하면 된다. 키보드가 없어도 된다. 마우스로 파일 잡아다가 던지면 된다.
모든 면에서 가장 좋은 선택이 MSC 였다. 더욱이 키보드를 사용하지 않아도 키맵이나 펌웨어를 업데이트 할 수 있다는 것이 마음에 들었다. 키보드 펌웨어를 업데이트 하는 것이기 때문에 구현에 따라 키보드를 사용하지 못할 수도 있기 때문이다.
첨부 | 파일 크기 |
---|---|
Serial_port.jpg | 84.08 KB |
댓글 달기