BLE Nano を使っていた自作キーボードだったが、Mac のアップデートとともにまた不安定になってしまい、使う気を失ってしまった (数時間ごとに再ペアリングが必要に)。直す気力もなくてしばらく放置していたが、そろそろ観念して USB 接続のキーボードに作りかえることとした。
作りかえるといっても、キーボードの部分は I2C GPIO のモジュールとして動くように作ってあるので、マイコンまわりを載せ変えて実装を書くだけになる。
ということで、タイトルの通り LPC11U35 を使ってキーボードを実装しなおした。
コード: https://github.com/cho45/keyboard-lpc11u35
mbed official の USBDevice
mbed official に存在するライブラリの USBDevice の中に USBKeyboard というのがある。USBHID を継承していて、レポートデスクリプタとかを予め設定してくれる便利クラスとなっている。これは LPC11U35 でもちゃんと動くので基本的にハマるようなところはない。
標準で便利なメソッドがいくつか定義されているが、実際にキーボードを作る場合はこれらは使わない。USBKeyboard に定義されているメソッドはデモ用と考えていいと思う。
普通に使う場合は、レポートデスクリプタ定義はそのまま使いつつ (特に変更する必要がないので)、USBHID#send() を直接呼んで HID_REPORT を自分で構成して送ることになる。
基本的なコード
#include "mbed.h"
#include "USBKeyboard.h"
#include "mcp23017.h"
#define REPORT_ID_KEYBOARD 1
#define REPORT_ID_VOLUME 3
USBKeyboard keyboard;
DigitalOut led(LED1);
DigitalIn key1(P0_4, PullUp);
int main() {
HID_REPORT report;
uint8_t modifier = 0;
uint8_t usage = 0;
report.data[0] = REPORT_ID_KEYBOARD;
report.data[1] = modifier;
report.data[2] = 0;
report.data[3] = usage;
report.data[4] = 0;
report.data[5] = 0;
report.data[6] = 0;
report.data[7] = 0;
report.data[8] = 0;
report.length = 9;
while(1) {
bool isKey1Pressed = key1.read() == 0;
if (isKey1Pressed) {
if (report.data[3] != 0x04) {
report.data[3] = 0x04 /* a */ ;
keyboard.send(&report);
}
} else {
if (report.data[3] != 0x00) {
report.data[3] = 0x00;
keyboard.send(&report);
}
}
wait_ms(10);
}
}
基本的にはこういう形です。USBKeyboard のメソッドではキーの「押しっぱなし」ができない実装なので、ちゃんとしたキーボードにするにはレポートを自分で管理する必要があります。実用的にはリポート内のキーの状態を管理するクラスが必要になるでしょう。
といっても、USBKeyboard をほとんど使わないようなら直接 USBHID を継承して MyUSBKeyboard を作ったほうがいい気がするので (レポート定義もいじれるようになるし)、実際はそうしています。
ハマりポイント
sleep() がうまくいかない
ハマることはないと書いたが、メインループで sleep() (Active Sleeep) を使ってデバイスを割り込み待ちにするコードを書いたところ、USBHID#send() が失敗するという状態になった。どうやらUSBの状態まで狂わすようだったのでビジーループに変えた。
なんでおかしくなるのか、実装を読んだりマニュアルを読んだりして調べてみてもよくわからない。USB のクロックは有効だし、USB まわりの電源も sleep で切れるようなものはないように思える。
割り込み用のピンのプルアップが弱い
内部プルアップ時の電流がスペックだと 50μA となっている。電源電圧 3.3V なら 66kΩ 相当のプルアップとなる。
I2C を 2kΩでプルアップ動かしていると、この内部プルアップのピンをかなり動かしてしまうようだった。とりあえずは大丈夫そうだったが、今後誤動作の原因となりそうだったので、こちらも同様に 2kΩ で外部プルアップとした。
その他くだらないハマり
- 一部のLANケーブルと相性がなぜか悪い
- I2C の容量を超えてるのかと思ったが、オシロでみると波形は正常にみえる
- 詳しく追ってない。実はクロスケーブルだったとか?
- → 4本しか結線されてないLANケーブルでした
- キーボード側で断線
- かなり細いパターンの部分が見えないレベルで断線しており、割込みがかからない状態であった
- sleep をやめたことによるバグ
- キー入力がないときは I2C バスをやすませるような動作にしていたが、条件判定用の数値が sleep をやめたことによりアンダーフローしていた。
- 時々キーが二重入力される
- USB の通信遅延?
- DEBUG 用に printf してるのが同期出力なのが原因っぽいので、本番で使う場合は必ず全 printf をオフにするように
接続
キーボード左右+USBコントローラという構成になった。USBコントローラとキーボードの左右はLANケーブルで接続する。BLE 版だとコントローラー基板はキーボードの左に付属していて、左右のキーボード同士をLANケーブルで接続していたが、実際はこのように配線を変えても動くような構成にしていたので、回路自体はすんなり変更できた。
電池がなくなったり、コントローラ基板を別にしたことで、キーボード全体の座高を低く、かつフラットにすることができた。今まで地味にキー位置が高くて、キーボード前にクッションが必要だったんだけど、その必要がなくなった。
筐体の作りなおし
3Dプリンタを得たのでより剛性の高い筐体になった。