さて、前回の記事では込み入った内容を取扱いました。今回は実際に動く(?) USB HID デバイスを作ってみます。
取り敢えず、ということで2つの(特に何の役にも立たない) USB HID デバイスを作成しました。これは、TinyUSB における2つのデータやり取り方法をお見せするためです。
- 数を送り続けるデバイス
- ホストから送られたデータに1を足したものを返送するデバイス
なお、送受信に使用するライブラリは hidapi を用いました。Mac なら以下のコマンドでインストール出来ます。
$ brew install hidapi
数を送り続けるデバイス
常に送り続けます。深い意味は無いのですが、69 と言うデータを送ることにしました。深い意味は無いです。
マイコン側のコード
ソースコードはこちら。中でも tud_hid_ready()
を用いているのがポイントです。おそらくですが、この関数が true
を返すのは IN
エンドポイントのバッファに空きがあるときだと考えられます。ですからデータを送る際にこのような利用法もあるということです。
int main() {
board_init();
tusb_init();
while(1) {
tud_task();
if( tud_hid_ready() ) { // main側で対応。ホストへの送信準備ができているとき
char buf[] = {69};
tud_hid_n_report(0, 0, buf, 1);
}
}
return 0;
}
PC側のコード
検証用コードです。これによってデータ受信を確認します。
#include <stdio.h>
#include <stdlib.h>
#include <hidapi.h>
unsigned char buf[10];
int main(int argc, char** argv) {
hid_device* handle = hid_open(0xcafe, 0xabcd, NULL);
if( handle == NULL ) {
fprintf(stderr, "Failed to open device.\n");
return 1;
}
hid_read(handle, buf, 1);
printf("Data Read: %d\n", buf[0]);
hid_close(handle);
hid_exit();
return 0;
}
あとは次のようにビルドし、実行すれば良いわけです。
$ gcc $(pkg-config --cflags --libs hidapi) -Wall -o hid main.c
$ ./hid
Data Read: 69
ホストから送られたデータに1を足したものを返送するデバイス
お次は双方向のデータ通信を行う HID デバイスです。先程とは違って、デバイスがデータを受信したタイミングで処理を行うという構成にしました。具体的にはコールバック関数内で加工したデータを返送しています。
マイコン側のコード
ソースコードはこちら。今度は main 関数では何もせず、PC からデータを受信した時に実行されるコールバック関数でデータ返送をしています。
void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
uint8_t buf[] = {
buffer[0] + 1
};
tud_hid_n_report(0, 0, buf, 1);
return;
}
PC側のコード
PC からデータを送った後に受信を待つプログラムです。
#include <stdio.h>
#include <stdlib.h>
#include <hidapi.h>
unsigned char buf[10];
int main(int argc, char** argv) {
if( argc != 2 ) {
fprintf(stderr, "Extra or lacking arguments.\n");
return 1;
}
hid_init();
hid_device* handle = hid_open(0xcafe, 0xabcd, NULL);
if( handle == NULL ) {
fprintf(stderr, "Failed to open device.\n");
return 1;
}
buf[0] = 0; // report ID
buf[1] = atoi(argv[1]);
int ret = hid_write(handle, buf, 2);
printf("Data sent. Return Code: %d\n", ret);
hid_read(handle, buf, 1);
printf("Data Read: %d\n", buf[0]);
hid_close(handle);
hid_exit();
return 0;
}
ビルドコマンドは先程と同様。実行すればいい感じの値が出ると思います。
$ ./hid 69
Data sent. Return Code: 2
Data Read: 70
$ ./hid 0
Data sent. Return Code: 2
Data Read: 1
$ ./hid -1
Data sent. Return Code: 2
Data Read: 0
$ ./hid 100
Data sent. Return Code: 2
Data Read: 101
なお、PC 側のソースコードで送信データが 2Bytes ですが、これは hidapi のマニュアルに記載されています。Report ID がそれですが、取り敢えず複雑でない HID デバイスに於いては 0x00
固定で良いようです。実際に HID として設定されたデバイスの エンドポイントに送られるデータは 1Byte です。
〆
TinyUSB 自体のソースコード解読はキツイものがありましたが、USB HID デバイスの実装自体は至極簡単でした。
高レイヤであるという難点はありますが、一度理解すればとてもスムーズに実装できていいと思います。現状、上級者向けだけどね。