xd87キーボードのLEDやRGBLEDをArduinoプログラムで点灯する

概要

xd87 PCBの動作確認の続きになります。

xd87 BGLEDのテスト

バックグラウンドLEDとアンダーグロー用RGB LEDの動作を、Arduino IDEで書いたスケッチで確認した。

Arduino IDEでビルド (検証・コンパイル)して得られたhexファイルを、Atmel Flip を使ってATmega32u4のフラッシュメモリに書き込む方法で実施した。ビルド時のターゲットは、このPCBと同じATmega32u4 (16MHz動作)が載っている Arduino Leonardo とした。

Arduino Leonardoとxd87 PCBには相違点も多いので、そのあたりに気をつけながらやったこと、わかったことなど。

最初はLチカで

うちに届いたxd87 PCBには出荷時からTMKファームウェアが書かれていたので、LEDを適切に挿しさえすれば、キーキャップのバックグラウンドLED (以降、BGLED ) やCapsLockインジケータ (以降、CapsLED) が点灯することは確認できていた。とはいえ、せっかく回路もわかったので自前のプログラムでも確認することとし、まずはLEDをチカチカさせてみることにした。

BGLEDをチカチカさせる

前回書いたように、BGLEDの点灯回路はおそらく以下のようになっている。

Key Switch LED

すべてのキースイッチ位置にあるBGLEDは、PD0を出力方向とし、ロジックLを与えれば点灯しロジックHで消灯するはず。ということで、Arduinoで書いてみた。

まず、Arduino流ではBGLEDを制御するためのPD0はデジタルポート3 (D3) なので対象のポート番号として3を使う。マイコン(ATmega32u4) のポートと、Arduinoでプログラミングする際のポート番号との関係は pins_arduino.h で確認できる。スケッチは以下のようになる。

HIGHにした後とLOWにした後のdelay() 時間を変えてあるのは、どちらの論理のときに点灯するのかをはっきりさせるためで、点灯している時間の方が長くなるはず。

Arduino IDE (1.8.10版)のツール/ボードArduino Leonardo となっていることを確認し、スケッチ/検証・コンパイルでビルドすることで、ユーザーのTempフォルダのどこかに、ビルド結果 (.elf, .hexなど) が作成される。後述するAtmel Flipの使い方にしたがってhexファイルを書き込んだところ、以下の動画のように点滅を開始した。

消えている時間よりも点灯している時間の方が長いのは間違いない。

Windowsのデスクトップにアナログ時計のアプリを表示し、その前にxd87 PCBを置いて撮影した。今回使ったLEDは、オプトサプライ社のOSB5DL3E34B という小電流でも比較的高輝度な製品。

クロックプリスケーラに注意

ただ、どうも想定していたより点滅が遅い。点灯時間0.8秒が約6秒、消灯時間も0.4秒が約3秒になってしまった。つまり約8倍遅い。AVRマイコンで8倍という数字を見たら、ヒューズのCLKDIV8ビットクロックプリスケーラレジスタ (CLKPR) を思い出さなければならない。

ATmega32u4の出荷状態では、システムクロックソースとして低電力クリスタルオシレータ (Low Power Crystal Oscillator) が選択され、システムクロックプリスケーラの除数として8が選択されるようローバイトヒューズ (Fuse Low byte) がプログラムされている。

つまり、マイコンに接続された発振源 (水晶とか)の周波数が16MHzならば、CPUやI/Oのための内部クロック周波数は8で割った2MHzになるということ。内部クロックが16MHzになるよう、setup() を以下のように修正した。

CLKPRに0を書き込むことで、クロックプリスケーラを1/1 に変更している。CLKPRを更新するためには、まずビット7をセットし、次にビット7をクリアすると共にプリスケール値 (ビット3~ビット0)を与えるようにする。

3分間で修正してフラッシュに書き込んだら以下のように、意図通りに点滅した。

LEDの点灯/消灯時間が8倍長いのは、CPUの命令実行時間8倍遅いせいではなく、Arduino Leonardo用にビルドしたプログラムでは、計時のためのタイマー/カウンターが、内部クロック16MHzを前提として設定されているため。たとえば、16という整数から、1/16秒ごとに1引いていったら1秒間で0になるが、1/2秒ごとに1引くのでは、0になるまで8秒間かかる。

xd87のプログラムをArduinoに見立てて書くとき、CLKPRを上記のように設定するか、ローバイトヒューズのCLKDIV8をプログラムする必要があることが分かった。

CapsLockインジケータのLチカ

次はCapsLEDでのLチカで、以下のような回路を前提にスケッチを書いてみた。

xd87 CapLock インジケータ

こちらはPE2ポートを出力方向とし、LOWを与えれば点灯するはずである。ただ、Arduino LeonardoではPE2はGNDに直結されており、デジタルポートの番号が与えられていないから、BGLEDと同じ方法は使えない。そのため、BGLEDもあわせてポートを直接操作することにした。

PD0 (BGLED)とPE2 (CapsLED)を出力方向とし、交互に点灯/消灯させる。BGLEDの方が長く光ればよい。以下のようになった。


CapsLEDが赤 (OSR5PA3E34B) 、BGLEDが青 (OSB5DL3E34B)。いずれも意図通りに点滅してくれた。

(最初の投稿時、CapsLock インジケータの回路に誤りがあったので訂正しました)。

アンダーグロー用RGB LEDの点灯

xd87 PCBのWS2812

うちに来たxd87 PCBは、出荷時にアンダーグロー用のRGBLED ( おそらくWorldsemi社のWS2812 )が14個実装されていた。そして、出荷時に書き込まれていたファームウェアによりPCのUSBポートに接続すると点灯もしていたが、自前のテストプログラムをフラッシュに書き込んでからは、当然のことだけど点灯しなくなった。

RGBLED

おそらく回路はこんな具合になっているので、PB7に必要なデータを所定の形式、タイミングで送り込めばよいだろう。ただ、800kHzほどの波形データを色に応じて送信する必要があり、コーディングや検証が厄介そうである。世の中に公開されているライブラリを利用することにした。WS2812Bのデータシートはこちら

light_ws2812ライブラリを利用

ws2812用のライブラリはいくつかあるようだが、とてもコンパクトな light_ws2812 ライブラリ (V2.4)を利用することにした (リンク先はGitHub)。GitHubからlight_ws2812_master をダウンロードしてくると、その中にはArduino用、8ビットAVR汎用、ARM用などのソースファイルが入っている。今回は、8ビットAVR汎用のCインタフェース版 (light_ws2812_AVR) を使うことにした。

Arduino版のライブラリならば、zipファイルをダウンロードしてきて スケッチ/ライブラリをインクルード/.ZIP形式のライブラリをインストール によってArduino IDEに組み込み、スケッチにライブラリをインクルード して使うこともできる。ただ、Arduino版ライブラリでは、RGBLED用のポート指定がArduino流のデジタルポート番号指定になっている。Arduinoの各種開発ボードで利用するときにはいいのだけど、今回のように素のATmega32u4をArduinoに見立てて使う際にはちょっとそぐわないかなと感じてCインタフェース版を選択した。

light_ws2812の組み込み

使い方は簡単で、ArduinoのスケッチのあるフォルダにAVR用のlight_ws2812.cとlight_ws2812.hをコピーしてくればよい。そしてスケッチの最初の方でヘッダファイルをインクルードする。

Cインフェースなので、extern “C” { } が必要なことに注意。データ出力に使うポートは、light_ws2812.h 内で以下のように定義されている。

light_ws2812の内部では、これらの定義を結合したり他のマクロと組み合わせたりして使っており、関数でポート名を指定するといったことはできない (ピン番号は指定可能)。xd87ではPB7がデータ出力ピンなので、上記のポート定義を変更する必要はなかった (ピン番号はAPIで直接指定する)。

単純にすべてのWS2812を白色(RGB同値)で点灯させるスケッチは以下のようになる。

1つのWS2812につき3バイト(RGB各色ごとに1バイト)必要で、点灯するすべて(RGB_COUNT 個)についてのデータをLED_BUFFER_SIZE バイトの領域に用意してからライブラリを呼び出す。

WS2812に対するRGBデータの設定はws2812_sendarray_mask() が行う。この関数の3番目のパラメータとして、xd87でのピン番号位置をセットしたバイトを指定している。データを送信した後は特に何もする必要がないのでloop() は空にしたが、loop() 内で各RGBLED用の値をインクリメントしたりデクリメントすることで、明るくしたり暗くしたり、あるいは色を変えることもできるだろう。

このスケッチではRGB各色に対して控えめに2を与えており、その様子は以下のようになった。

点灯中のWS2812B

 もう少し派手に光らせる

もうちょっと派手なサンプルということで、いろいろな色が走るようなスケッチも書いてみた。

xd87のws2812

最初はこの写真のように7つのWS2812に異なる色をセットしておき、その後与える色を1つずつずらしている。

最初が裏から見たところ、途中から表から見たところになっている。

xd87 PCBキットに付属の白いケースに収めた場合、裏側からは色は分かるだろうがさほど効果的とも思えないし、表から見たときも、現状はキースイッチ用の穴から発光している様子が分かるが、スイッチを取り付けたらほとんど見えないだろう。透明あるいはスモークした半透明アクリルケースでもあるといいのだけど。

スケッチは以下のとおり。

setup() 内で先頭から7つのRGBLEDにそれぞれ異なる色を設定しておき、loop() 内で1つずつRGBデータをずらしている。

このスケッチ ( rgbled_test1.ino ) をビルドして得られるrgbled_test1.elf をavr-objdump.exe を使って逆アセンブルしてみたところ、void loop() 内にある ws2812_sendarray_mask() の呼び出しおよび実体はインライン展開されてわずか60バイトのコードになっていた。なお、全体のコードサイズは3700バイトほどで、大部分はArduinoのUSBやシリアル関連のコードが占めていた。

消費電流について

キーボードが派手に光ってくれるのはいいのだけれど、果たしてどれほどの電流が流れるのかをUSB電流計で調べてみた。

USB電流計

USB電流計の左側にあるのはELECOM社のUSB3.0セルフパワーハブで、電源が5.0V、4A出力のACアダプタだから少々の負荷は問題ない。

何も点灯させない状態での電流計の読みは以下のようになった。

  • リセットをかけてブートローダーモードした場合は約30mA。
  • ビルドしたプログラムを開始すると約40mA。

ちなみに、Pro Micro(5V/16MHz版)をつないでみると約30mAだった。Pro Microではパワーインジケータが点灯していること、xd87では14個のRGBLEDが消灯中とはいえ電流を消費していることを考えると妥当な数字だと思う。

LEDの消費電流

点灯確認にも使った青色LEDのOSB5DL3E34B の仕様上のVFは2.8~3.6V (@IF=30mA) と書いてある(データシートはこちら)。

OSB5DL3E34B 10本入(秋月で購入)

xd87 PCBに挿してアノードとカソード間の電位差を測ったところ、かなり電流が制限されているせいか約2.7Vだった。電流ソースとLEDのアノード間には330Ωの電流制限抵抗が入っているから、LEDを流れる電流IFは、(5 – 2.7) ÷ 330 ≒ 7.0mA となる。たしかに、LEDを一本立てたときの電流計の読みは7mA増加した。この電流計は案外と正確なのかもしれない。

xd87 BGLED

こんな感じで10個立ててみると、電流計の読みは70mA増加して110mAほどになった。調子に乗ってたくさんつけない方が良さそうである。

xd87の場合、BGLEDはATmega32u4のPD0 (OC0B) に接続されているので、Timer/Counter0 (TC0) ユニットを使ったPWM出力により、明るさをコントロールしたり消費電流を抑えたりできるはず。ただ、今回はインジケータとして使うので検討しなかった。

RGBLEDの消費電流

rgbled_test.ino (すべてのRGBLEDに、RGB同値を与える)を使って、値を変えながら電流計の読みを観察したところ、以下のようになった(BGLEDは無し)。

RGB値 電流(mA) 1個あたり(mA)
0x000000 40
0x202020 78 2.7
0x404040 142 7.3
0x808080 264 16.0
0xffffff 500 32.9

1個あたりの電流は、(全体の電流 – 40)÷14 で求めている。

WS2812の消費電流についての情報はいろいろなwebサイトや掲示板から得ることができる。事前に読んだ情報からRGBデータとして0xffffff を与えたときには1個あたり50mA程度消費すると思っていたのだけど、今回確認した限りでは最大で32.9mAになった。電源ラインが細いせいなのか、実は低消費電流版なのか、理由はよくわからない。

少なくとも、0xffffffで点灯するとUSB2.0で保証されている最大電流に達してしまい、BGLEDやCapsLEDを点灯する余裕はまったくないことが分かった。

USBのCONFIG_POWER

USBキーボードをPCなどのホストに接続したとき、デバイスはホストの求めに応じて構成情報を送信するが、その中には消費電流に関する項目も含まれている(ここでは、CONFIG_POWERと表現する)。

PCに接続されているUSBデバイスの構成情報 (Configuration Descriptor) を見るには、Windows用のUsbTreeView というツールが便利である (リンク先はUsbTreeViewの作者のページでダウンロードもできる ) 。手持ちのキーボードをいくつかつないで確認してみたところ、CONFIG_POWERとして65~100mA という数字を申告しているものが多かったし、実際そんなところだろう。

Arduino LeonardoやSparkFun Pro Micro でUSBデバイスをこしらえると、基本的にはCONFIG_POWERは500mAとなる。それは、これらのボードのプログラムに組み込まれるUSBインタフェースのソースファイル (USBCore.h) に以下のように記述されているため。

USB_CONFIG_POWER_MA(500); というのはマクロになっており、実際のデータ(Configuration Descriptor) にはこれを1/2した250 (0xfa)という値が入っている 。1/2するのはフィールド長が1バイトだから。

CONFIG_POWERが100mAを超えていると、”too much power” と怒って接続させてくれないホストもあるらしく、BGLEDやRGBLEDをつけ放題なのに、100mAと申告する悪い習慣もあるとか。とはいえ、ホスト側のUSBポートに100mA以上流さないような仕組みが入っているわけでもなさそうなので、100mAどころか500mA以上の電流を流したとしても、幸運にも、あるいは、とりあえずうまくいくようである。

なお、Arduino LeonardoやSparkFun Pro Microとそのコピー品では、USBコネクタのVBusを受けるところに、500mAでトリップするポリヒューズ(リセッタブルヒューズ) が入っていて、ボード内での短絡による過大電流からホストやハブのUSBポートを保護するようになっている。
それに対して、このxd87 PCBではVbusと回路のVccが直結されているから、過大な電流や短絡に注意しておく必要があるだろう。

Atmel Flipでのビルド結果の書き込み方法

Atmel Flipを使ったマイコンのフラッシュメモリへの書き込みは今後よく使うことになるので、使い方のメモを記しておく。

Atmel Flipを開くと以下のようなウィンドウが表示される。

Atmel Flip (3.4.7版)

操作の手順は以下の繰り返しになる。

Devices / Selectを選択 (または、ツールバー左端のICのような絵をクリック)し、ターゲットデバイスとして、ATmega32u4 を選択する。一度やると覚えてくれるので、ウィンドウ右上のデバイス名で確認するだけでよくなる。

ターゲットデバイスを外部リセットし、DFUモードとする (見た目ではわからない)。xd87ならばISPポートのRESETとGNDをショートしてやる。

Settings / Communicationを選択 (またはツールバー左から二番目のUSBプラグのような絵をクリック)し、接続に使うポートとしてUSB を選択する。

ターゲットがDFUモードになっていない、USBケーブルが接続されていないなどの場合、以下のようにUSBがオープンできないと表示される。何度やってもオープンできないときでも、Flipを一度閉じて開き直すとオープンできたりする。

Atmel Flip 接続できない

ターゲットと接続できた場合、最初はグレイアウトしていた表示がアクティブになる。

Atmel Flip 接続できた

この状態になると、ターゲットのフラッシュメモリに書き込む.hexファイルを選択できるようになる。File / LoadHexFile… を選択するとファイルダイアログが開くので、Arduinoが作成したTempフォルダを探して内容を確認する。

Flip LoadHexFile

こんな具合に、2つのhexファイルが作成されているが、 …ino.with_bootloader.hex ではない方 を選択する。間違えて選択してしまっても、ブートローダ領域にかかるファイルは読み込まないようになっているはずだが。

正しい方のファイルを読み込むと、ウィンドウの中央部の表示が更新され、以下のように表示される。

Atmel Flip 書き込み情報

これは、ATmega32u4の28KBytesのフラッシュメモリ (ブートローダー領域を除いたサイズ) に対し、今回ロードしたプログラムサイズは3956バイトであり、アドレス範囲は0x0000-0x0f73ですよ、ということ。

hexファイルがロードできたら、ウィンドウ左下の方にある Run ボタンをクリックしてフラッシュ書き込みを行う。

Atmel Flip 書き込み

Run ボタンをクリックすると、このグループ内でチェックがついているオプションが上から順番に実行されていく。成功すると、オプション名左側の○が緑になるし、失敗すると赤になって停止する。

Atmel Flip 書き込み失敗と成功

左が失敗で右が成功。失敗するとそれより下の処理は行われない。

Program (書き込み) 時に失敗することがよくあるのだけど、そういうときは一度Atmel Flipを終了して再度開き、USB接続からやり直すと成功するようである。

Programが成功し最後のVerifyも成功したら、いよいよターゲットに書き込んだプログラムを実行する。そのためには、ウィンドウの右下あたりにある、Start Application ボタンをクリックする。

Atmel Flip アプリケーションの実行

このとき、Reset というチェックを外しておくこと。そうしないと、書き込んだプログラムの動作が開始しなかった。

なお、hexファイルのロードやフラッシュへの書き込みを行わなくても、Start Applicationによって書き込み済のプログラムを先頭から実行させることもできる。これにより、USBケーブルを抜き差ししてパワーオンリセットをかける手間が省ける。

ATmel Flipのダウンロードは、Microchip社のページから行うことができる。ダウンロードしたアーカイブには、DFUデバイスクラスのドライバファイルも含まれており、DFUブートローダーを使ったフラッシュ書き込みの前に導入しておく必要がある。

きょうのまとめ

BGLEDやCapsLED、そして、RGBLEDの点灯の実験はうまくいった。今回使ったLEDでキーキャップの下から照らしたとき、効果的かどうかは今後の話になる。

DFUブートローダーとAtmel Flipを使ったフラッシュメモリ書き込みについてもすっかり慣れてきた。Arduino 用にビルドしたプログラムなのでCaterinaのようなCDCブートローダーを入れようかと思っていたのだけど、このまま使うことにする。

次回は、キースイッチマトリックスをスキャンし、オンになっているスイッチ番号をシリアル経由で出力するArduinoプログラムの話になる予定です。