2017年9月13日水曜日

Digispark+gcc USB HID 通信のチュートリアル


概要

 


Digispark+gcc チュートリアルの Part3 です(Part1, Part2)。

Digispark にファームウェアを書き込んで、HID デバイス (HID ベンダ定義デバイス) として認識させ、PC 側で動いているプログラムとの間で USB 経由で任意のデータの送受信をする方法を説明します。

サンプルでは PC 側のプログラムから送信された1バイトの値を Digispark が受信して、同じ値を送り返します。 このとき Digispark が受信した値が '1' の文字コードの時は、LED を点灯します。 '0' の文字コードの時は、LED を消灯します。 それ以外の場合は何もせず送り返すのみの動作をします。

Digispark 用のファームウェアは C言語で書いて avr-gcc でコンパイルし、USB で接続した Digispark に書き込みます (Arduino の開発環境を使いません)。 また PC 用の通信プログラムも C で書いて gcc でコンパイルします。

Raspberry Pi, FreeBSD で動作確認しました。

※ Windows のコマンドラインから Digispark に生のファームウェアを書き込む手順は、1回目の記事 を参照。


1. 開発環境の準備


以前の記事 「Digispark+gcc Lチカチュートリアル(RaspberryPi, Linux, FreeBSD 版)」 の 1. 2. を実行して、必要なツールをインストールし、Digispark への書き込みソフト micronucleus を実行できるようにしておく。

2. ソースコードのダウンロードとコンパイル 


(1) Digispark_VendorHID.zip をダウンロードして(「エラー」と表示された場合もそのままダウンロードできる)、ローカルに展開する。
(2) ファームウェアをコンパイルする。make コマンド(Raspberry Pi では make コマンド、FreeBSD では gmake コマンド) を実行すると main.c がコンパイルされてバイナリ (Intel HEX) 形式のファームウェア sample1.hex が生成する。
(3)  PC 側で動かす通信プログラムもコンパイルしておく。"COMMANDLINE" ディレクトリの中で make コマンドを実行すると vusb.c がコンパイルされて、実行ファイル vusb が生成する。

3. ファームウェアの書き込み


1. で準備した書き込みツール micronucleus をパスの通った場所に置き
sample1.hex  があるディレクトリで

# micronucleus --run sample1.hex

を実行する(管理者権限が必要)。

> Please plug in the device ...
> Press CTRL+C to terminate the program

 と表示されるので、この状態で USB 端子に Digispark を挿入するとファームウェアが書き込まれる。

4. 動作確認 


(1) ファームウェアを書き込んだ Digispark を PC の USB 端子に挿入すると、5-6 秒後に HID (ヒューマンインターフェースデバイス) として認識される。Raspberry Pi, FreeBSD では dmesg コマンドを実行すると、USB デバイスの名前が確認できる(ベンダID=0x16c0, デバイスID=0x05dc)。
(2) PC 側で 2. でコンパイルしたコマンド vusb を実行する (管理者権限が必要)。

vusb コマンドの引数には、次のように何か1文字を指定する。


# ./vusb A
libusb_set_configuration OK
libusb_claim_interface OK
A


正常に動作終了した場合は、引数に指定した文字が Digispark に送信され、そのまま送り返されて、上記のように画面に表示される。

文字 '0' と '1' を受信したときは Digispark 側で特殊処理を行っている。
 # ./vusb 1
とすると、Digispark 上の LED が点灯する。
# ./vusb 0
とすると、Digispark 上の LED が消灯する。

5. PC 側プログラムの説明


PC 側のプログラム vusb.c は、ライブラリ libusb-0.1 を使って HID デバイスとの通信を行う。このサンプルでは、通常のエンドポイント (通信用のバッファメモリ) を使用する通信ではなく、コントロール転送という通信方法で、単純にデータを1バイトずつ USB デバイスとの間で送受信している。

USB による通信では一般に、USB デバイスが自発的に送受信を要求することはない。送信も受信もホスト側(PC側) が API を呼び出すことによって行う。

送信(PC→外部USB機器): PC 側のプログラムで 「(5) ホスト→デバイス: 1バイト送信」 を実行するたびに、後述の Digispark 側のファームウェアで usbFunctionWrite() が呼び出される。Digispark 側ではこの関数内で受信を行う。

受信外部USB機器PC: PC 側のプログラムで 「 (6) デバイス→ホスト: 1バイト受信」 を実行するたびに、Digispark 側のファームウェアで usbFunctionRead() が呼び出される。Digispark 側ではこの関数内で送信を行う。

今回プログラム内で使用した関数 (libusb0.1 の API) を解説する。


(1) USB の初期化
r = libusb_init(NULL);

USB デバイスの使用を開始する。 最初に呼び出す必要がある。
成功したら 0、失敗したらエラーコードを返す。エラーコードは負の値。
今回はエラーコードを libusb_error_text() 関数で文字列に変換している。

(2) USB デバイスの検索
devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);

ベンダID とプロダクトID を指定して USB デバイスを1個検索する。
デバイスハンドル (libusb_device_handle 型構造体) を返す。
今後の USB デバイスの操作にはこのデバイスハンドルを指定する。

(3) USB デバイスのコンフィグレーション(configration) の設定
r = libusb_set_configuration(devh, 1);

USB デバイスが内部に持つコンフィグレーションの設定を行う。
インタフェースの要求の前に実行する必要がある。
成功したら 0、失敗したらエラーコードを返す。

(4) インタフェースの要求
r = libusb_claim_interface(devh, 0);

USB デバイスとの通信に使用するインタフェースを要求する。
成功したら 0、失敗したらエラーコードを返す。

(5) ホスト→デバイス: 1バイト送信
r = libusb_control_transfer(devh, CTRL_OUT, HID_SET_REPORT,
     (HID_REPORT_TYPE_FEATURE<<8) | 0x00, 0, buf,  PACKET_CTRL_LEN, TIMEOUT);
  
buf[0] に送信する値を入れて実行する。
(今回は PACKET_CTRL_LEN を 1 にしているので、1バイトだけが送信される)。
送信に成功したバイト数を返す。エラーの場合はエラーコードを返す。

(6) デバイス→ホスト: 1バイト受信
r = libusb_control_transfer(devh,CTRL_IN,HID_GET_REPORT,
     (HID_REPORT_TYPE_FEATURE<<8) | 0x00, 0, buf,  PACKET_CTRL_LEN, TIMEOUT);

実行すると buf[0] に USB デバイスから受信したデータが格納される。
(今回は PACKET_CTRL_LEN を 1 にしているので、1バイトだけが受信される)。
受信に成功したバイト数を返す。デバイス側で送信するデータがなかった場合は 0 を返す。
エラーの場合はエラーコードを返す。

(7) インタフェースの解放
libusb_release_interface(devh, 0);

インタフェースの使用を終了する。

(8) USB デバイスのクローズ
libusb_close(devh);

USB デバイスの使用を終了する。


 

6. Digispark 側プログラムの説明


ソースファイル
usbconfig.h で HID デバイス用の設定を行っている。
ファームウェアの実体は main.c に書かれている。

メインルーチン
main() 内ではまず USB の初期化を行う。
メインループ内では 50ms 以内の間隔で usbPoll() を実行する必要がある。

USB 通信の処理
USB に関するリクエストが発生したときには usbFunctionSetup() が呼ばれる。
その後、デバイス→ホストの通信 (クラスリクエストの GET_REPORT) だった場合は usbFunctionRead() が呼ばれる。この関数内に USB デバイス側の送信処理を記述する。
ホスト→デバイスの通信 (クラスリクエストの SET_REPORT) だった場合は usbFunctionWrite() が呼ばれる。この関数内に USB デバイス側の受信処理を記述する。

割り込み
USB の通信のために、外部割り込み(PCINT4) のみを使っている (Digispark では PCINT4 端子が USB コネクタの D+ に接続されている)。これ以外の割り込みは自由に使用できる。タイマ割り込みも使用できるが、正確な間隔で割り込むためには、一時的に外部割り込みを禁止する必要がある(その間は通信に応答しなくなる)。
  
USB 通信中かの確認
USB 通信中に cli() で割り込みを禁止すると通信エラーが発生するため、一時的に割り込みを禁止する場合は、事前に main.c 中の USBNOTBUSY() マクロで、通信中かを確認して、通信中の場合は待つ必要がある。

レポートディスクリプタ
HID デバイスに必要なレポートディスクリプタを main.c 内で定義している。このレポートディスクリプタのサイズが usbconfig.h 内の USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH に書いてある。


補足、要調査
  • Digispark のウォッチドッグリセットがなぜかうまく動作しない。
  • PC 側ライブラリに libusb-1.x というバージョンもあるが、使い方が異なる模様
  • PC 側ソフトウェアで、該当するUSB デバイスが1つだけしかないと分かっている場合は今回の方法が使えるが、同一 ID のデバイスが複数の場合は列挙する必要がある。  
  • Linux でも動作するはずだが、 CentOS7 ではライブラリのバージョンが異なるらしくコンパイルできない。
  • PC 側プログラムは Windows(MinGW環境でコンパイル) でも動作するように書いてあるが、まだ手順を書いていない。





2017年9月3日日曜日

Digispark+gcc USB キーボードデバイス作成チュートリアル

下の赤色LEDはCAPSランプになっていて、他のキーボードの CAPS LOCK キーにも連動して点滅する

概要


Digispark+gcc チュートリアルの Part2 です。 

Digispark にファームウェアを書き込んで、USB キーボードとして動作させる方法を説明します。これは Code and Life さんの記事 USB HID keyboard with V-USB を参考にして Digispark  で動作するようにアレンジしたものです。

ファームウェアを C言語で書いて avr-gcc でコンパイルし、USB  で接続した Digispark に書き込みます (Arduino の開発環境を使いません)。

Raspberry Pi, FreeBSD で動作確認しました。

※ Windows のコマンドラインから Digispark に生のファームウェアを書き込む手順は、1回目の記事 参照

1. 開発環境の準備


以前の記事 「Digispark+gcc Lチカチュートリアル(RaspberryPi, Linux, FreeBSD 版)」 の 1. 2. を実行して、必要なツールをインストールし、Digispark への書き込みソフト micronucleus
を実行できるようにしておく。

2.  ソースコードのダウンロードとコンパイル


(1) Digispark_kbd.zip をダウンロードして(「エラー」と表示された場合もそのままダウンロードできる)、ローカルに展開する。
(2) make コマンド(Raspberry Pi では make コマンド、FreeBSD では gmake コマンド) を実行すると main.c がコンパイルされてバイナリ (Intel HEX) 形式のファームウェア sample1.hex が生成する。

3. ファームウェアの書き込み


1. で準備した書き込みツール micronucleus をパスの通った場所に置き
# micronucleus --run sample1.hex
を実行する(root 権限が必要)。

> Please plug in the device ...
> Press CTRL+C to terminate the program.

と表示されるので、この状態で USB 端子に Digispark を挿入するとファームウェアが書き込まれる。

4. 動作確認


(1) ファームウェアを書き込んだ Digispark を PC の USB 端子に挿入すると、5-6 秒後に USB キーボードとして認識される。Raspberry Pi, FreeBSD では dmesg コマンドを実行すると、USB デバイスの名前が確認できる(ベンダID=0x16c0, デバイスID=0x05dc)。

Raspberry Pi の例

% dmesg
[522793.926120] usb 1-1.3: New USB device found, idVendor=16c0, idProduct=05dc
[522793.926166] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[522793.926185] usb 1-1.3: Product: V-USB
[522793.926201] usb 1-1.3: Manufacturer: KBD
[522793.945119] input: KBD V-USB as /devices/platform/soc/20980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/0003:16C0:05DC.0005/input/input4
[522794.000287] hid-generic 0003:16C0:05DC.0005: input,hidraw0: USB HID v1.01 Keyboard [KBD V-USB] on usb-20980000.usb-1.3/input0

FreeBSD の例

% dmesg
ugen0.5: <KBD> at usbus0
ukbd0: <KBD V-USB, class 0/0, rev 1.10/1.00, addr 12> on usbus0
kbd2 at ukbd0

Windows の例

Windows PC に挿した場合は HID キーボードデバイスとして認識されたことが、デバイスマネージャから確認できる。

 

(2) 基板上の LED (PB1 に接続) は CAPS LOCK ランプとして機能している。Digispark を接続した PC のキーボードの SHIFT+CAPSLOCKキーを押すと、本来のキーボードの CAPS LOCK ランプと同時に、Digispark 上の LED も点滅する。

(3) Digispark の P0 端子(PB0)と GND 端子をショートさせると、キーボードの "x" キーを押して離す動作が実行されて、画面上に x と表示される。