2016年5月21日土曜日

DigiSpark と FreeBSD の間の通信

 

 

 はじめに

Arduino の小型ボード DigiSpark は、ソフトウェア USB を実装していて HID デバイスとして認識されるので、しかるべきファームウェアを書き込めば、ホスト PC と通信ができる。

ホスト PC 側で実行するサンプルコードとして、DigiSpark に文字列を送信する "send" というソフトがあったので、これを FreeBSD でコンパイルして、FreeBSD に接続した DigiSpark の LED を制御する実験を行った。デフォルトでは FreeBSD で作成したプログラムから DigiSpark が認識されなかったため、FreeBSD のカーネルの再コンパイルが必要だった(最近の OS ではカーネルの再コンパイルは不要。追記参照)。

DigiSpark のスケッチの修正と書き込み

すでに HEX ファイルがある場合は FreeBSDでファームウェアを書き込めるが(「DigiSpark に FreeBSD から書き込む」参照)、今回ベースにしたコードは Arduino のスケッチなので、今のところ統合開発環境(IDE)が対応している OS で開発するしかない。今回は Windows 上でサンプルのスケッチを修正して、DigiSpark への書き込みまでを行った。

公式ページ https://www.arduino.cc/en/Main/Software/ から DigiSpark に対応した Arduino の統合開発環境(IDE) DigisparkArduino-Win32-1.0.4-May19.zip をダウンロードし、Windows にインストールする(ここで Windows 用のデバイスドライバも入れる必要がある)。

IDE(arduino.exe)を起動し、 メニューから [ファイル]→[スケッチの例]→[DigisparkUSB]→[Echo] を開く。このサンプルファームウェアは PC から USB 経由で送られてきた文字列をそのまま返すものだが、これに少しだけ追加して、大文字の "B" を受信したときは LED を ON, 小文字の "b" を受信したときは OFF にするようにした(文字 "B" を使ったのは、後から LED を増やして上の動画のように ABCD の4つの LED を制御するため)。

DigiUSB.write(lastRead);

の行の直後に次の行を追加する。
// [サンプル Echo の修正点]
// PB1 を出力ピンにする
DDRB = _BV(PB1);
// "B" 受信時に LED 点灯
if (lastRead == 'B') { PORTB |= _BV(PB1); }
// "b" 受信時に LED 消灯
if (lastRead == 'b') { PORTB &= ~_BV(PB1); }
修正はこれで OK。完成したスケッチを Windows の IDE で DigiSpark に書き込んでおく。

FreeBSD での文字列送信ソフトのコンパイル

FreeBSD 6.4 で DigiSpark に文字列を送るソフト send をコンパイルした。

Github https://github.com/digistump/DigisparkExamplePrograms/ から、通信用ソフトのサンプル DigisparkExamplePrograms-master.zip をダウンロードして FreeBSD 上に展開する。DigisparkExamplePrograms-master/C++/send/ に send のソースファイルがある。

libusb-0.1 を ports からインストールする(あれば。新しい OS では不要)。
コンパイル時には Makefile の修正が必要。OS 判定部分の
ifeq ($(shell uname), Linux)

ifeq ($(shell uname), FreeBSD)
に修正して make を実行すると、実行プログラム send が生成する。

(追加: FreeBSD 9.2 の場合)
libusb のインストールは不要(というかない?)。
send.cpp の先頭に
#include <libusb.h>
を追加する。また Makefile に
USBLIBS = `libusb-config --libs` -lusb
を追加する(USBLIBS は OS 毎に複数あるので FreeBSD 用の行を修正する)。

FreeBSD カーネルの再コンパイル


(注) 新しい OS では状況が違い、再コンパイルは不要。下の追記参照。

ここで FreeBSD に DigiSpark を接続して send コマンドを実行しても "No Digispark Found" と表示されて動作しない。http://nonakap.hatenablog.com/entry/2015/12/24/000700 を見ると、 カーネルが uhid や ukbd として認識した USB デバイスは libusb を使ったユーザプログラムから発見できないのが原因のようだ(上記 URL のサイトを参考にさせていただきました。どうもありがとうございます)。

# usbdevs -v
(または # usbconfig dump_device_desc)

コマンドで調べると
port 1 addr 4: low speed, power 100 mA, config 1, DigiUSB(0x05df), digistump.com(0x16c0), rev 1.00
と表示されるので、動作中の DigiSpark のベンダ ID は 0x16c0, プロダクトID は 0x05df とわかる。このデバイスを無視するように FreeBSD(FreeBSD 6.4) のソースを修正する。/usr/src/sys/dev/usb/usb_quirks.c
を修正して赤字部分を追加し、カーネルを再コンパイルする。
/* Devices which should be ignored by both ukbd and uhid */
{ USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_WISPY,
ANY, { UQ_KBD_IGNORE }},
{ 0x16c0, 0x05df,
ANY, { UQ_KBD_IGNORE }},
{ 0, 0, 0, { 0 } }
};
FreeBSD を再起動すると send が動作するようになる。

実行

send の実行には root 権限が必要。一般ユーザでは "No Digispark Found" と表示される。
FreeBSD 機の USB 端子に DigiSpark を接続する。正しく認識されていると dmesg コマンドで
ugen0: digistump.com DigiUSB, rev 1.10/1.00, addr 4
等と表示される。ここで
# send "B"
で DigiSpark  の LED が点灯し
# send "b"
で消灯する。



【追記】
新しい OS では USB の挙動が異なる。FreeBSD9.2 ではカーネルの再構築は不要で、dmesg コマンドで
uhid1: <digistump.com DigiUSB, class 0/0, rev 1.10/1.00, addr 2> on usbus0
と表示されている状態で、カーネルには手を加えなくても send が動作した。
/sys/dev/usb/quirk/usb_quirk.c の修正が必要ではないか。man usb_quirk によると、無視するデバイスをusbconfig(5) コマンドで動的に追加できるかもしれない

補足1(Windows PC から制御する場合)


Windows ではドライバ
DigisparkArduino-Win32\DigiUSB Programs\DigiUSB Windows Driver
を手動インストールすると
DigisparkExamplePrograms-master\Python\DigiUSB\windows\send.exe
が動作するようになる。ドライバのインストール前は send.exe 実行時に"No DigiUSB Device Found" と表示されて動作しない。

ただしサンプルの send.exe でなく、 libusb1.0 を使って作成した自作した Windows 用アプリケーションでは Windows7/2000 にドライバを入れなくても動作したので、必須ではないと思われる。

補足2(Arduino を使わず V-USB を直接使う)


Arduino を使わず V-USB を直接 AVR-GCC でコンパイルしてテストしたので覚え書き。Tiny85 に HID で通信するファームウェアを書き込んで動かすところまではできた。
最初はサンプルのファームウェア (vusb-20121206\examples\hid-custom-rq\firmware\main.c) 中の下記のコードの赤文字の部分が 0x01 になっていたが、その状態だと Linux, FreeBSD との通信はできたが、Windows (+libusb1.0 +MinGW) の libusb_control_transfer() という関数で Tiny85→Windows の通信時、データを受信していないのに 1バイト受信したという間違いの値が返ってきた。
USB の原理やディスクリプタについて分かっていないのが問題。要調査。

/* ------------------------------------------------------------------------- */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */

PROGMEM const char usbHidReportDescriptor[22] = {   /* USB report descriptor */
    0x06, 0x00, 0xff,              // USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                    // USAGE (Vendor Usage 1)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x08,                    //   REPORT_COUNT (1)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0xc0                           // END_COLLECTION
};
/* The descriptor above is a dummy only, it silences the drivers. The report
 * it describes consists of one byte of undefined data.
 * We don't transfer our data through HID reports, we use custom requests
 * instead.
 */

0 件のコメント:

コメントを投稿