SNSへはこちら

ラズピコでTinyUSBやってみよう(3) - USB HIDの実装と動作

さて、前回の記事では込み入った内容を取扱いました。今回は実際に動く(?) USB HID デバイスを作ってみます。

取り敢えず、ということで2つの(特に何の役にも立たない) USB HID デバイスを作成しました。これは、TinyUSB における2つのデータやり取り方法をお見せするためです。

  1. 数を送り続けるデバイス
  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 デバイスの実装自体は至極簡単でした。
高レイヤであるという難点はありますが、一度理解すればとてもスムーズに実装できていいと思います。現状、上級者向けだけどね