CH9350L用Arduinoライブラリ ch9350ifLibrary

ch9350ifLibraryについて

hoboNicola XIAO rev.01

以下の機能をもつArduinoライブラリ ch9350ifLibrary を作成した。このライブラリの目的は、USBキーボードでオン/オフされるキーのコードをArduinoスケッチで取得すること。

  • キーボードでオンまたはオフされたキーのHID Usage IDをメインスケッチに通知する。モディファイヤキー(修飾キー)についても、独自のコードで通知可能。
  • キーボードのShift, Ctrl, Altなどのモディファイヤキーがオンまたはオフされたとき、各モディファイヤキーの状態をまとめて通知する。この通知に際してtrueを返すことで、前記のモディファイヤキーのコードによる通知は抑止される。
  • CH9350Lから受信したデータフレーム(キーボード入力レポートを含むデータフレーム)の内容を、受信都度ごとに通知する。
  • 新しいフレームの受信の開始を通知する。
  • CH9350Lからの全受信データを1バイトごとに通知する。

各機能はCH9350Lの動作を調べるために用意した。これらの機能はライブラリに含む examples 内のスケッチで利用している。

このライブラリは、CH9350Lを使ったhoboNicolaアダプターのビルドにも用いる。

CH9350L + XIAO-m0 hoboNicola

ハードウェアとしては、前の投稿で作成したhoboNicolaアダプターで確認しているが、Aliexpressで売っているCH9350Lを使った各種ボードとArduinoボードの組み合わせでも大丈夫だろう。接続が必要なのは TX, RXとRST (および電源)だけなので。

ファイル構成と導入方法など

ch9350iflib100.zip をダウンロードし、Arduino IDEの スケッチ/ライブラリをインクルード/.ZIP形式のライブラリをインストール… によって導入する。Arduino IDE 1.8.19で動作を確認した。ダウンロードは hoboNicolaのダウンロードページから行ってください。

ZIPファイル内のライブラリは以下のようなファイル構成になっている。

library.properties  Arduinoライブラリ用プロパティ
gpl-3.0.txt         ライセンス
src / ch9350if.cpp  メインプログラム
      ch9350if.h    ライブラリ用のヘッダ
      ch9350if_hidkeys.h  キーの名前や説明の文字列と関数など
      ch9350if_settings.h 動作環境ごとの定義

examples / dump_frame / dump_frame.ino フレーム表示サンプル
           dump_keycode / dump_keycode.ino キー表示サンプル
           show_modifiers / show_modifiers.ino 修飾キー+キー表示サンプル
           ch9350_hobonicola / ch9350_hobonicola hoboNicola用メインスケッチ

ch9350_hobonicolaのビルドには、hoboNicolaライブラリの1.6.1が導入されている必要がある。ビルド方法などについては、1.6.1版についての投稿を参照。

使い方

CH9350Lとの接続について(シリアルポートとリセットポート)

マイコンボードとCH9350Lの通信速度は115200bpsに固定と考えており、接続先はハードウェアシリアルポートがいいでしょう。

ライブラリでは、Serial1 という名前のオブジェクトがCH9350Lと接続されたハードウェアシリアルを司っていることを前提としている。たとえば Serial2 とか Uart1 とかの場合、以下の内容をもつ src/ch9350if_settings.h 内の CH_SERIAL の値を適宜変更する。

// hardware serial object 
#define CH_SERIAL	Serial1
// reset port pin connecting to ch9350l rst pin.
#define CH_RST_PORT 2

CH9350Lをリセットするためのマイコン側のポートピンについても CH_RST_PORT を変更すること。ポート番号には、Arduinoの流儀のD1とかD2とかの数字を与える (初期値は2)。

ライブラリを利用するスケッチでは、インスタンスの宣言時に _ch9350 ch9350(CH_RST_PORT);  のようにコンストラクタの引数としてリセットポート番号を与える。何かの都合でリセットポートが接続できないような場合、ポート番号を省略するか 0xff を与えることで、ライブラリはリセットポートが接続されていないものとして動作する。

リセットを制御できない場合、CH9350Lとのやり取りが不完全になり、ステータスリクエストフレームの送信頻度を小さくできないことがある。リセット制御できる場合は1秒に1回程度になるのだが、リセット制御できないときは66msecごとに出力されてくるので、マイコンは大忙しになる。ステータスリクエストフレームの受信状況は、examples/dump_frame.inoで確認できる。

スケッチでの利用方法 (dump_frame.ino)

このライブラリを利用するスケッチでは、ch9350if クラスを派生し必要な通知機能の実装を記述する。examples/dump_frame.inoでは以下のようにしている。

class _ch9350 : public ch9350if {
public:
  _ch9350(uint8_t rst) : ch9350if(rst) {}
  void newframe() { Serial.println(); }
  void rx_byte(uint8_t c) { dump_byte(c); }
};

_ch9350 ch9350(CH_RST_PORT);

void setup() {
  ch9350.ch9350_setup();
  Serial.begin(115200); // for Serial monitor.
  while(!Serial)
    delay(10);
}

void loop() {
  ch9350.ch9350_loop();
}

( 1バイトごとにHEX文字列として表示する dump_byte() は、ch9350if_settings.h に含まれている)。

プログラムの基本形は以下のようになる。

  • 実装したい機能をもつch9350ifクラスの派生クラス(_ch9350 )を記述し、インスタンス (ch9350) を宣言する。
  • Arduinoの setup()と loop() から、それぞれ ch9350_setup()、ch9350_loop() を呼び出す。ライブラリの各種通知は、すべて ch9350_loop() 内から行われる。

このサンプルスケッチでは、フレームの開始通知( newframe() ) でシリアルモニタに改行を出力し、受信データ通知 ( rx_byte() ) が来るとその内容をHEX文字列として出力している。シリアルモニタには以下のように表示される。

57 AB 82 A3 (ステータスリクエストフレーム)
57 AB 82 A3 
57 AB 87    (バージョンリクエストフレーム)
57 AB 82 A3 
57 AB 82 A3 
57 AB 82 A3 
57 AB 83 0B 14 02 00 00 00 00 00 00 00 02 04 (データフレーム)
57 AB 83 0B 14 00 00 00 00 00 00 00 00 03 03 
57 AB 82 A3 
57 AB 83 0B 14 00 00 04 00 00 00 00 00 04 08 
57 AB 83 0B 14 00 00 00 00 00 00 00 00 05 05 
57 AB 82 A3 
.....

キーコードの取得 (dump_keycode.ino)

dump_keycode.ino では、void dataframe() と void key_event() を実装することで、キーのオン/オフイベントをシリアルモニタに表示する。同時に、キーボードでCapsLock、ScrLock、NumLock の各キーがオンオフされると、キーボード上のLEDをオンオフするようにしている。

このサンプルではキーのオン/オフを検出すると入力レポートを含むデータフレームの内容、イベントの元になったキーのコード(HID Usage ID)および名前(あるいは文字)をシリアルモニタに出力する。

14 00 00 04 00 00 00 00 00 4D 51  : 04  ON  A
14 00 00 00 00 00 00 00 00 4E 4E  : 04  OFF A
14 02 00 00 00 00 00 00 00 4F 51  : E1  ON  L_Shift
14 00 00 00 00 00 00 00 00 51 51  : E1  OFF L_Shift
14 02 00 00 00 00 00 00 00 52 54  : E1  ON  L_Shift
14 02 00 04 00 00 00 00 00 54 5A  : 04  ON  A
14 02 00 00 00 00 00 00 00 55 57  : 04  OFF A
14 00 00 00 00 00 00 00 00 58 58  : E1  OFF L_Shift
14 01 00 00 00 00 00 00 00 59 5A  : E0  ON  L_Ctrl
14 01 00 04 00 00 00 00 00 5C 61  : 04  ON  A
14 01 00 00 00 00 00 00 00 5D 5E  : 04  OFF A
14 01 00 16 00 00 00 00 00 5F 76  : 16  ON  S
14 01 00 00 00 00 00 00 00 61 62  : 16  OFF S
14 00 00 00 00 00 00 00 00 63 63  : E0  OFF L_Ctrl

このサンプルが表示するデータフレームには、前回の投稿で書いた接続情報 (device_kind)からチェックサムバイトまでが含まれている。

チェックサムの1バイト前のシーケンス番号は、 データフレーム通知においては必ずしも連続しない。ある程度の時間キーを押し続けると、CH9350L自体がキーリピートを生成しているようで同じ内容のデータフレームを繰り返し受信してしまう。このためライブラリ内で同じデータフレームは無視するようにしている。

このサンプルでキー名称などを表示するため、src/ch9350if_hidkeys.h というヘッダファイルを追加した。Pro MicroなどのAVRでキー名称文字列などがフラッシュメモリに格納されるよう PROGMEM 指定している (ARMとしてビルドする場合、PROGMEM指定は無視される)。

モディファイヤキーフラグの表示 (show_modifiers.ino)

dump_keycode.inoでは、モディファイヤキーを独自のコードで表現している (0xE0~0xE7) が、こちらのサンプルでは bool modifiers_changed() を実装することで、入力レポート内のモディファイヤキー状態ビットマップをビジュアル(?)に表現してみた。

C___+____                     // L_Ctrl
C___+____ : 16 S is ON  
C___+____ : 16 S is OFF 
____+____
_S__+____                    // L_Shift
_S__+____ : 04 A is ON  
_S__+____ : 04 A is OFF 
____+____
____+c___
____+c_a_
____+c_a_ : 4C Delete is ON  // R_Ctrl + R_Alt + Delete
____+c_a_ : 4C Delete is OFF 
____+__a_
____+____

モディファイヤキーを独自コードで表現しているのは、hoboNicola でCtrlキーと英数キーを入れ替えるとか、右側のAltキーをひらがなキーにするとかを実現する際、もともとのビットマップ表現では面倒だったからである。

ライブラリ内では、bool modifiers_changed() 通知は、key_event() 通知よりも先に行われる。このサンプルのように、bool modifiers_changed() の実装側が true を返すと key_event()ではモディファイヤキーのコード出力を行わないようにしている。

ビルドについて

ライブラリに含まれているサンプルを利用する場合のビルドについて。現在のところ、SparkFun Pro Micro、Seeeduino XIAO-m0、スイッチサイエンス ISP1807 MicroBoard でビルドと実行を確認している。

  • Pro Microについては、プロセッサの選択(8MHzか16MHzか)に注意するだけで特記事項無し。
  • スイッチサイエンスISP1807 MBについても、Adafruit nRF52  BSPの1.3.0 版でビルドと実行を確認した(導入についてはこちらの投稿を参照)。
  • XIAO-m0については若干の注意が必要だった。

Seeeduino XIAO-m0をつなぐときの注意点

Seeed studioのBSPを使う場合

Seeed StudioのSAMD用BSP 1.8.3 でビルドできることを確認している。

Seeed SAMD BSP

Arduino IDEに Adafruit_TinyUSB_Library を導入済みの場合、USB Stack: は Arduino を選択しておくこと。

Seeed SAMD BSP

Seeeduino用BSPを使うときUSB Stack:としてTinyUSB を選択するとビルドが失敗した。これは、このBSPがAdafruit_TinyUSB_ArduinoCore を含んでいるためで、Arduino IDEに Adafruit_TinyUSB_Library が導入済みの場合はビルドエラーになってしまうようである (なにかやり方があるのかもしれないが)。Adafruit_TinyUSB_Libraryが導入されていなければ、おそらく問題ないのだろう。

AdafruitのSAMD用BSPを使う場合

hoboNicolaライブラリ1.6.1でSAMD21に対応した際、BSPとは別に導入する Adafruit_TinyUSB_Library を利用できるようにするため、AdafruitのSAMD BSPで XIAO_m0用のビルドが行えるようにした(手順はこちらの投稿を参照)。

Adafruit SAMD BSP

今回のサンプルやCH9350L用hoboNicolaをビルドする際にも、同じ方法でビルドできることを確認した。

なお、ch9350_hoboNicola以外のサンプルをビルドする場合のUSB StackはArduinoとTinyUSBのいずれでもよい (HIDやMSCを利用しないので)。

きょうのまとめ

当初はhoboNicola専用のつもりで始めたが、方針を変えて目的を問わないArduinoライブラリとしても使えるようにした(つもり)。

dump_frame.inoを実行したとき、ステータスリクエストフレーム (57 AB 82 A3 ) が高頻度で(1秒間に15,6回)表示されるようならば、キーボードを抜いて差すと低頻度(1秒間に1回)になる(はず)。ただし、RSTピンをマイコンから制御できない場合は改善しないことがある。

Seeed studio XIAOには、今回使ったSAMD21G18以外のマイコンを使った製品もあり、CH9350Lを使ったアダプターについても、nRF52840とRP2040を使ったボードに対応する予定です。