概要
SparkFun社のPro Micro(のコピー品)に、秋月電子の4×3キーパッドを接続し、PCの入力装置(HIDキーボード)にしてみる。
Arduino Pro Microと書いてしまったけれど、Pro MicroはArduinoオリジナルではなく、SpakFun社が開発した Arduinoコンパチブルボードという位置づけで、オリジナルのArduino Leonardo と同じマイコン(ATmega32u4)が載っているから、開発時にはオンボードのUSBコネクタを介して直接PCに接続できるし、実行時にはPCから見たUSB周辺装置として振る舞わせることができる。
Pro Microを使うからにはUSBを介してPCのHID(Human Interface Device) として構成してみたいから、手元にあった秋月電子の4×3キーパッド作成キット (AE-KIT45-KEYPAD4X3) を使って、12キーのキーボードを作ってみることにした。この作成キットにはプリント基板、タクトスイッチ、ダイオード、抵抗など必要な部材が含まれているし、回路図やマトリックススキャンのためのサンプルスケッチも公開されているから、はんだ付けさえ厭わなければ手軽に使える。
構成
ハードウェアとしては、Pro MicroのGPIO端子にキーパッドを接続するだけになる。4×3のキーマトリックスを使うためには7ビット(出力3本、入力4本)必要なので、D2~D4を出力用、D5~D8を入力用に使うことにした。
Pro Microを使うのは今回がはじめてなので、調べたことを記録として残しておくことにする。おもにSparkFun社の Pro Micro & Fio V3 Hookup Guide を参考にした。
SparkFun Pro Microの回路図はこちら。今回入手した互換品もほとんど同じ構成なのだけど、LEDの色や直列抵抗値が違っていたり、LDOレギュレータの品番が違っていたりした。
Pro Micro 3.3V/8MHz版
Pro Microには+5V/16MHz動作のものと、+3.3V/8MHz動作のものがあるが、今回は+3.3V/8MHzの製品を使った。5V/16MHzの製品には+5V出力のLDOレギュレータと16MHz発振器が載っているし、+3.3V版には+3.3V出力のレギュレータと8MHz発振器が載っている。
国内で8MHz版を扱っているショップが見つからなかったので、中国から取り寄せた(USD $5程度)。+3.3V動作のボード(シールド)と組み合わせることがありそうならば、+3.3Vの製品を選んだほうがよいだろう。
3.3V版と5V版
Pro Microに載っているATmega32u4自体には、+5V版とか+3.3V版といったバリエーションはないのだが、動作電圧によってクロック周波数の上限が制限されている。ATmega32u4のデータシートの 29.6 Maximum speed vs. VCC によれば、動作電圧範囲は+2.7~+5.5V(最大+6.0V)だが16MHzで確実に動作するとされているVCCは+4.5V程度以上になっているのに対し、+3.3Vを与えた場合の上限周波数は10MHz程度のようである。8MHzクロックならばVCCが+2.7Vでも動作するから、省電力を考えるなら8MHzクロック版を選択することになる。
国内の大手通販サイトでPro Microを探していたら、3.3V、16MHz動作をうたう(騙る?)製品があった(なぜかちょっと高い)。実は動くのかもしれないが、規格外である。
電源周り
Pro Microの回路図を見ると、ボード内の電源(VCC)の生成方法にもいくつかのオプションがあることがわかる。
上の写真のようにUSBコネクタの周囲には、ジャンパJ1、ダイオード、リセッタブルヒューズ、LDOレギュレータなどが実装されている。LDO表面には DE=A1D と刻印されていた。調べてみると、Richteck社のRT9193-33GBという3.3V出力CMOS LDOレギュレータのようだ。これらを簡単に回路にすると以下のようになっていると思われる。
この回路から、以下のようなことがわかる。
- USB VBUSと内部の回路のあいだには500mAでトリップするポリスイッチが入っており、ホスト側ポートを保護している。
- J1を閉じる(ハンダを流す)と、VBUSとVCCは(ポリスイッチを介して)直結される(約+5V電源)。
- J1をオープンにしておくと、LDOの入力はVBUSまたはRAW端子となり、VCC電圧はLDOの出力電圧になる。
- +5V出力のLDOの入力にVBUSを使うと、LDOのドロップアウトの分だけ5Vを下回ってしまう(J1はオープン)。+5V動作ならばRAW端子に+6V以上の外部電源をつなぐか、J1を閉じておいた方がいいだろう。
- +3.3V動作の場合、電源はVBUSでもRAW端子の外部電源でもいいだろう。なお、J1にハンダを流してある場合、+3.3V動作はあり得ない。
- VBUSを使うならば、いずれの動作電圧の場合も大きな負荷は避ける。
- VBUSとRAWの間にはダイオードが入っているので、RAW端子に外部から+9Vとか与えてもVBUSには影響しない。また、RAW端子に外部電源を接続しない場合、RAW端子にはVBUS電圧(ちょっと降下している)が現れる。
- VCC端子に外部電源を接続してもよい。その場合、J1はオープンにしておくべきだろう。
オンボードLED
回路図によれば、Pro Microには3つの表面実装型のLEDが実装されていて、
- 赤(LED1) : VCC直結の電源インジケータ
- 緑(RXLED) : PD5に接続。LOWで点灯。USBコネクタを上にしたとき、オシレータの左側。
- 黄(TXLED) : PB0に接続。LOWで点灯。同じく右側。
となっているようだ。しかしながら、今回購入したボードでは、RXLED, TXLEDともに緑色だった。スケッチから何かのインジケータとして利用するのは容易だが、USBを介した送受信時にチカチカするようになっているようだし、かなりまぶしい。
キーパッド
秋月電子の4×3キーパッドにはダイオードが10本、抵抗が4本、タクトスイッチが12個入っている。飽きずにはんだ付けして完成。
下段の両脇のスイッチの色を変えてみた。裏側はこんな具合になった。
電話機と同じ配列が3列、4行の構成で印刷されている。むろん、この印字のとおりに使う必要はないだろう。はんだ面に貼り付ける絶縁用のシートが付属しているともっとよかった。
回路図
キーパッドを含めた回路は以下のようになる。RAW端子にはUSB VBUSからの+5V(実測では+4.8V)が出る。
左側の10KΩ×4の抵抗にVCC電圧を与えることで入力側のプルアップ抵抗として機能する。しかしながら、今回はATmegaに接続するのでマイコンが内蔵しているプルアップ抵抗を使うこととし、基板には抵抗をはんだ付けしたものの、VCCとは接続しないことにした。
このキーパッドのマトリックスにはすべてのスイッチにダイオードが入っているので、複数のキーを同時に押下したとしても、いわゆる「回り込み」による悪影響がない。そのため12キーロールオーバーのキーパッドが実現できる。
ブレッドボードに載せるとこうなった。
左側上部にはリセットスイッチを置いた。スイッチを接続したRST端子はボード上で10KΩでプルアップされている(はず)なので、追加の抵抗は不要。Pro Microのボードにリセットスイッチが載ってるといいのに。
Arduino IDEの準備と接続
Pro Micro用のスケッチをビルドするためには、Arduino IDEに対してPro Microのことを教えてやる必要がある。そのためには、ボードマネージャを使う。なお、Arduino IDE 1.8.3 を使っているので、他のバージョンでは話が違うかもしれない。
まずファイル/環境設定を開き、追加のボードマネージャのURL: に次の内容を入力する。 https://raw.githubusercontent.com/sparkfun/Arduino_Boards/master/IDE_Board_Manager/package_sparkfun_index.json 。そしてOKでダイアログを閉じる。
次に、ツール/ボードで開くリストの先頭にあるボードマネージャを選択するとIDEがサポートするボードの一覧が表示されるので、その中にある”SparkFun AVR Boards by SparkFun Electronics“を選択してインストールする。インストールしたバージョン1.1.12だった。ボードマネージャを閉じ、もう一度ツール/ボードを開くと、下図のように導入されたSparkFun社のボードが選択できるようになる。
ボードメニューからSparkFun Pro Micro を選択し、プロセッサとしてATmega32u4 (3.3V 8MHz) を選択する。
ようやく準備ができたのでPCにつないでみる。
PCにつないでみると、最初はLilyPadだった
商品に付属のピンヘッダをはんだ付けし、USBケーブルでPCにつないでみた。USBケーブルとしては、AコネクタとマイクロBコネクタが付いたスマホ用のものを使った。
つないでみると、PC(Windows10)に、LilyPad USB用のUSBシリアルが自動的にインストールされたようだった(COM5になった)。Arduino IDE (1.8.3)で開いてツール/ボード情報を取得 を開くと、
のように、ボード名として”LilyPad Arduino USB“となっており、あれ?となってしまった。LilyPad Arduino USBというのは服に縫い付けるやつ。Pro Microと似たような構成(ATmega32u4、USBコネクタ付き、8MHz 3.3V動作)ということで、ブートローダーを流用しているのだろうか。
一度スケッチを書き込むとPro Microになった
簡単なスケッチを書いてビルドして書き込むと、自動的に Pro Micro用のドライバ(USBシリアル)が導入され、今度はCOM8が追加された。
ツール/シリアルポートをCOM8に変更し、ツール/ボード情報を取得 を開いてみると、
今度は、「不明なボード」になった。Arduinoオリジナル以外のボード名は「不明なボード」になるので識別できないが、PID に示されている値 (0x9204) はSpakfun Pro Micro を示している。 [user-dir]\AppData\Local\Arduino15\packages\SparkFun\hardware\avr\1.1.12\boards.txt の中の、 promicro.menu.cpu.8MHzatmega32U4.build.pid=0x9204 によって確認できる。
VID = 0x1B4FはUSBベンダーIDを表している。VIDの流用や、USBを使う機器の販売には微妙なところがあるようだ。SparkFun社はArduino.ccからサブライセンスを受けているのかもしれないが、そのコピー品はどうなんだろう。
この段階で、Arduino PRO MINIを使っていたときのように、ビルドして書き込んだり、Serial.println() を使ってシリアルモニタに出力したりできるようになった。ただ、シリアルモニタを開いたままではスケッチ/マイコンボードに書き込むが失敗してしまう。シリアルモニタを閉じればいいだけだが。
キーパッド用スケッチ1(ふつうのArduinoとして)
まずは4×3キーパッドをスキャンして、キーに対応するコードを出力するスケッチを作ってみた。HIDではなくシリアルモニタに出力するだけ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
/** * 秋月の4x3キーパッドをpro microにつないで、定期的にキースキャンする。 * 新たに押下されたキーがあれば、そのキーに対応するキー番号をシリアルモニタに出力する。 * 押下かリリースかの情報も出力する。 * * HIDではなく、ふつうにシリアルポートに出力する。 */ const uint8_t scan_port[] = { 2, 3, 4}; const uint8_t data_port[] = { 5, 6, 7, 8}; void setup() { Serial.begin(115200); for(int i = 0; i < sizeof(scan_port); i++) { pinMode(scan_port[i], OUTPUT); digitalWrite(scan_port[i], HIGH); } for(int i = 0; i < sizeof(data_port); i++) pinMode(data_port[i], INPUT_PULLUP); } // 全キースキャンして、押下されているキーのビットを1にしたビットマップを返す。 int key_scan() { int key_pressed = 0; for(int col = 0; col < sizeof(scan_port); col++) { digitalWrite(scan_port[col], LOW); int row_data = 0; for(int row = 0; row < sizeof(data_port); row++) row_data |= (digitalRead(data_port[row]) ? 0 : 8 >> row); key_pressed |= (row_data << (sizeof(data_port) * col)); digitalWrite(scan_port[col], HIGH); } return key_pressed; } const uint8_t key_codes[] = {1, 4, 7, 10, 2, 5, 8, 11, 3, 6, 9, 12}; void loop() { static int last_key_pressed = 0; int key_pressed = key_scan(); int change = key_pressed ^ last_key_pressed; if (change) { int mask = 1; uint8_t code; for(int i = 0; i < sizeof(key_codes); i++) { if (change & mask) { code = key_codes[i] | (key_pressed & mask ? 0 : 0x80); char tmp[4]; sprintf(tmp, "%02X ", code); Serial.print(tmp); } mask = mask << 1; } last_key_pressed = key_pressed; Serial.println(); } delay(100); } |
動作の概要
setup()
スキャン用(出力用)はOUTPUT, データ用(入力用)はINPUT_PULLUPとする。キーパッドの抵抗にVCCを与えていない代わりにATmegaの内蔵プルアップ抵抗を有効にしている。
key_scan() キーボードマトリックスのスキャン
スキャン対象の列に対応するポート( scan_port[col] )をLOWとする。その状態で4行分のデータ( data_port[row])を読むと、オンになっているキーがLOW、オフになっているキーがHIGHになっているので、row_data の対応するビット位置にオンなら1、オフなら0をセットする。これを3列分繰り返し12ビット分の押下状態データをkey_pressedに得てリターンする。
loop() メインループ
定期的に key_scan() を呼び、int key_pressedに全キー(12個)の押下状態をビットマップとして得る。LSBがSW1でビット11がSW12になる。
key_pressedと前回キースキャン時のキー状態( last_key_pressed )とのXORを得ることで、状態が変化したキーのみを抽出してint changeに格納する。変化したキーに対応するビットが1に、変化のないキーのビットが0になる。changeが0なら変化したキーはない。
changeの各ビットについて、1になっているキーに対応するキーコードを、const uint8_t key_codes[] から得る。キーコードは回路図に示したスイッチの番号とした。そのキーが押下されたのならばそのまま、リリースされたのなら0x80をORし、2桁の16進数に書式化してシリアル出力している。
このスケッチについて
この実装をこのままPRO MINIなどで使うこともできる。簡単な実装なので、delay(100); で100msec単位にキースキャンと出力を行っているが、タイマー割り込みによってスキャンするタイミングを得てもいいだろう。また、最後に出力したコードが押下されたままならば、リピート出力するなどの変更も容易だろう。
キーパッド用スケッチ2(HIDキーボードとして)
ながながと書いてきてようやくここまで来た。HIDキーボードとして実装するため、今回はArduino標準のライブラリを使うことにした。スケッチ/ライブラリをインクルードから、Keyboard を選択することで、スケッチの先頭に #include <Keyboard.h> が挿入された。このヘッダにはKeyboard_クラスの定義と、スケッチで利用するインスタンス Keyboard の宣言がある。
キースキャン部分やキーコードを得る箇所はほとんど同じだが、出力部分は Keyboard_クラスの、Keyboard_::press(uint8_t k); と、Keyboard_::release(uint8_t k); を使うようにした。それぞれ、キーの押下検出時とリリース検出時に呼び出すことになっている。引数の k には、出力したい文字コードを指定する。文字コードを与えてやると、ライブラリ(Keyboard_クラス)の中でHIDキーボード用の内部コードに変換されるようだ。
スケッチは以下のようにした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#include <Keyboard.h> const uint8_t scan_port[] = { 2, 3, 4}; const uint8_t data_port[] = { 5, 6, 7, 8}; void setup() { for(int i = 0; i < sizeof(scan_port); i++) { pinMode(scan_port[i], OUTPUT); digitalWrite(scan_port[i], HIGH); } for(int i = 0; i < sizeof(data_port); i++) pinMode(data_port[i], INPUT_PULLUP); Keyboard.releaseAll(); } // 全キースキャンして、押下されているキーのビットを1にしたビットマップを返す。 int key_scan() { int key_pressed = 0; for(int col = 0; col < sizeof(scan_port); col++) { digitalWrite(scan_port[col], LOW); int row_data = 0; for(int row = 0; row < sizeof(data_port); row++) row_data |= (digitalRead(data_port[row]) ? 0 : 8 >> row); key_pressed |= (row_data << (sizeof(data_port) * col)); digitalWrite(scan_port[col], HIGH); } return key_pressed; } const uint8_t key_codes[] = { 0x31, 0x34, 0x37, KEY_LEFT_SHIFT, 0x32, 0x35, 0x38, 0x30, 0x33, 0x36, 0x39, KEY_RETURN}; void loop() { static int last_key_pressed = 0; int key_pressed = key_scan(); int change = key_pressed ^ last_key_pressed; if (change) { int mask = 1; uint8_t code; for(int i = 0; i < sizeof(key_codes); i++) { if (change & mask) { code = key_codes[i]; if (key_pressed & mask) Keyboard.press(code); else Keyboard.release(code); } mask = mask << 1; } last_key_pressed = key_pressed; } delay(50); } |
内容は最初のスケッチとほとんど同じ。変更点は、const uint8_t key_codes[] に数字キー相当のアスキーコードを入れ、SW10(緑色)をUSBキーボードの左SHIFTキー、SW12(青色)をEnterキーとした。そして、押下検出時には Keyboard.press(code); を、リリース検出時は Keyboard.release(code); を呼び出すようにした。
KEY_LEFT_SHIFTやKEY_RETURNなどの定義はKeyboard.h 内にある。
スイッチを押したままにしておくと、スケッチでは何もしていないのにキーがリピートする。タイパマチックの開始遅延やリピート出力はPC側のドライバが処理しているようだ。スケッチの最後をdelay(50); としているのは、キーのリリースをすばやく検出できるようにするため。
Keyboard_クラスの中身
Keyboard_クラスの中では、KeyReport構造体の変数_keyReportに押下中のキーやモディファイヤキー(SHIFT, ALT, CTRL, GUI。左右あるので合計8キー)の情報が格納されている。上記のpress() や release() というメソッドは、_keyReport内の各種情報を書き替えてからsendReport(); を呼び出している。sendReport(); メソッドはHIDクラス内のSendReport();を使ってUSBへの送信を行っているようだ。
KeyReport構造体内には押下中のキーを示す配列があるが、この配列の大きさは6バイトしかない。そのため有効な同時押下数は6キーということになる。モディファイヤキーについては8ビット長のビットマップで表現されキーコードを持たないので、文字キーを5つ押した状態で8つの修飾キーをすべて同時に押すといった操作は有効のようだ。
COM9が追加されキーボードになった
このスケッチをビルドして送り込むまではシリアルポートはCOM8だったのだけど、書き込みが完了したら再度Pro Micro用のドライバのインストールが始まりCOM9が追加され、COM8は消えてしまった。
その後、少々の手直してとボードへの書き込みを繰り返したが、Pro Micro側に用意したリセットスイッチに触らなくても、Arduino IDEのマイコンボードに書き込む をクリックするだけでビルドしたスケッチが書き込まれた。
書込みを始める時点でPro Microにリセットがかかり、一時的に現れるCOM7を介して転送が行われているようだった。転送したスケッチの実行が始まる時点でCOM7は消えCOM9が現れていた。そして、Pro Microに接続したキーパッドでの操作結果(文字出力)は、PC側からみてキーボード入力として扱われるようになった。
これはあくまでもWindows10のPCで、Arduino IDE(1.8.3)を使って開発しているときの話で、別バージョンのWindowsや別のOSでは違った動きをするのかもしれないし、あらかじめドライバを用意しておく必要があるのかもしれない。
Windowsアクセサリにある「メモ帳」を開いて、キーを順に打つと以下のようになった。
1行目がそのまま、2行目が緑色のキー(SW10 = SHIFT)を押しながら入力したところ。シフト側は、日本語キーボードの記号になっている。
Windows7のノートPCにつないでみるとドライバを探しに行った
あっさりと動いてくれたので、USBケーブルをWindows7のノートPCにつないでみた。プラグアンドプレイに対応していることも確認したかったので、あらかじめWindowsを立ち上げ、デスクトップを表示した状態で行ってみた。
まずは上のように、SpakFun Pro Microのドライバを探しにいった。Windows10にはあらかじめ仕込まれているようだが、古いWindows7 Pro には用意されていないようだ。
しばらく待ってみたが見つからないようなので、検索をキャンセルした。すると以下のようになった。
SparkFun Pro Micro用ドライバはあきらめたものの、「USB入力デバイス」として使えるようになったようなので、やはりメモ帳を開いてキーを押してみると、Windows10のときと同様に入力することができた。入力デバイスとしてはプラグアンドプレイ対応している、ということだろう。
SparkFun Pro Micro用ドライバは、おそらくWindows7(8.1も同様か?)でArduino IDEを使って開発を行うときに必要になるのだろう。SparkFun社のページにはドライバのアーカイブをダウンロードするためのリンクもあるし、その内容はgithubでも参照できる。
きょうのまとめ
秋月電子のキーパッド工作キットは安価で工作もしやすい。もうちょっと大きいと実用的。
ふつうのキーボードのように、キーマトリックスのスキャン、キーの押下リリースの検出、キーコードの生成を実装してみた。今回は12キーだが120キーでも基本的には同じだろう。
むかし偉い人に、「キーボードは素人さんでも口が出せるから云々」という話を伺ったことがあるが、Pro Microを使えば簡単に手も出せることが分かった。面白そう。
Keyboardライブラリには、press()や release() の他に、write(); というメソッドも用意されている。このクラスは Printクラスを実装しているので、Keyboard.print(“ohayougozaimasu.”); とか書けば、キーを1つ押すだけで挨拶できるようになる。ただ、PC側でIMEが開いていてローマ字かな入力が可能になっている必要がある。次回はこのあたりのことにあたってみる予定。
追記 同じキーパッドを ProMicroピン互換BLEモジュールのISP1807 Microボード に接続した話も書きました。