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.
 */

DigiSpark に FreeBSD から書き込む


はじめに


(2017/08/31 追記) 手順がわかりにくいので新記事として書き直しました。こちらのページをご覧ください。


DigiSpark は ATTiny85 を使った Arduino 互換の小型ボード。下記のようにいろいろとすごい。
  • 小さくて安い。USB コネクタに直挿しできる。正規品でも 1000円以下。クローン品だと200円以下。
  • AVR マイコンのソフトウェア(V-USB) だけで USB を実現していて、HID デバイスとして認識され、ツール(micronucleus) を使ってファームウェアを書き込める。
  • USB 通信機能は書き込み用だけでなくファームウェアからも呼び出せて、しかるべき通信をするファームウェアを書き込めば、USB 経由で PC と通信できる。
本来は Arduino 互換ボードとして使うものだが、0番地から始まる通常の ATTiny85 のファームウェアを書き込めば(ファームのサイズが 6kB 以下なら) 動作するので、C言語(avr-gcc) で開発したファームウェアを書き込んで ATTiny85 ボードとして使える。今回はこのような使い方が前提。

専用の書き込みツールはもともと Windows, Mac, Linux,OpenBSD に対応しているようだが、試してみたところ FreeBSD からも書き込みができたので報告する。通信用のファームウェアを書き込んで FreeBSD と USB で通信する方法も別稿で紹介する。

コマンドライン版書き込みツール micronucleus (Windows 版)

DigiSpark に対応した Arduino の統合開発環境 

https://sourceforge.net/projects/digistump/files/DigisparkArduino-Win32-1.0.4-May19.zip/download
(または  https://www.arduino.cc/en/Main/Software/ )


をダウンロードする。Windows 用の書き込みツール Digispark-Arduino-1.0.4\hardware\tools\avr\bin\micronucleus.exe がこの統合開発環境に含まれている(Windows では書き込み前にドライバのインストールが必要。ドライバが古いと書き込みソフトが応答しなくなる。 こちらの手順を参照して Digistump.Drivers.zip をインストールする)。

Windows での使い方は
C:\> micronucleus.exe --run FILENAME.hex
実行後、一度 DigiSpark を USB コネクタから抜いて挿しなおすと FILENAME.hex が DigiSpark に書き込まれる。

FreeBSD 用 micronucleus のコンパイル

この書き込みツール micronucleus の FreeBSD 版をコンパイルする。開発元のページ http://digistump.com/wiki/digispark/tutorials/connecting/ から、書き込みツール micronucleus の github にリンクがある。ここから micronucleus-master.zip をダウンロードして展開する。書き込みツールのソースファイル micronucleus-master/commandline/ を FreeBSD に置く。

コンパイル時に Makefile の修正が必要。OS 判定部分の
else ifeq ($(shell uname), OpenBSD)

else ifeq ($(shell uname), FreeBSD)
に修正する。make を実行すると micronucleus が生成する(FreeBSD6.4 で確認。libusb-0.1.12_2 を ports でインストールしてある)。

micronucleus の実行には root 権限が必要。
# micronucleus --run FILENAME.hex
で FILENAME.hex が書き込める。

備考

ATTiny85 には他の高機能のマイコンと異なり、専用のブートローダ領域がなく、割り込みベクタも移動できない。どうやってブートローダを実現しているのかと思ったら、下記のような工夫をしているそうだ(フラッシュメモリの先頭にある割り込みベクタをブートローダから書き換える。ブートローダはフラッシュの末尾に置く)。
http://digistump.com/board/index.php?topic=168.5;wap2

C言語(avr-gcc) で開発したファームウェアを書き込んだところちゃんと動いたが、ウォッチドッグリセットがうまくいかない(ウォッチドッグリセットの発生後フリーズしてしまう。ファームウェアの書き込みモードに移行せず、書き込んだファームウェアも再始動しない)。リセットせずウォッチドッグ割り込みを発生するだけなら正常に動作する。

参考

DigiSpark 国内販売店
http://www.elefine.jp/SHOP/Digispark.html/

DigiSpark 回路図
https://s3.amazonaws.com/digispark/DigisparkSchematicFinal.pdf

ArduinoのHEXファイルを残す方法
http://d.hatena.ne.jp/licheng/20130826/p1
参考にさせていただきました。どうもありがとうございます。

Digispark のヒューズ
hfuse=5FH, lfuse=F1H, efuse=FEH (RESET 無効, 低電圧プログラミング不可, BOD なし)