macOSからPi Picoを使う - その2 [Pi Pico]
前回Pi PicoをmacOSと一緒に使う方法を考えた。結論としては
ただし、pico_stdio_usbでは大きなデータはやりとりできない。またデータはフラットなバイト列としてしかみることができないので、なんらかの上位プロトコルを決める必要がある。
でもまあ、これでPi Pico側は大きなプログラムになることはなく、管理しやすい。
ところでこれを前提にすると、USB経由でPi Picoをリセットできることをみつけた。リセットの状態はBOOTSELモードとオンボードのflashストレージから読み込む通常モードの両方が選べる。これは便利だと思う...
基板上の30番ピン「RUN」端子をGroundに落とせばリセットできるけど、年寄りの目ではチカチカして間違いそうになる(こんなまんなかへんじゃなくて、ボードの端の近くにしてほしかった)。ハードウェアスイッチを作ったりしてる人がいるけど、あまり僕の趣味じゃない。
ということでほんとはUSBからリセットできると便利なんだけど、前回USBPorberでみたときにinterface#2に「Reset」とあったのでなにかそういう機能がありそうではある。ところが情報が完全ではない。
pico_stdio_usbの記述に
とあるのが、このことだろうけど、どうすればいいのか何も書いてない。
となっている。itf_num変数はinitialize時点でデスクリプタのbInterfaceNumberに設定されているようである。
ここに出てくるbRequestの定数はreset_interface.hに定義があって
などとなっている。全部がわかったわけではないけど、このinterface#2にRESET_REQUEST_BOOTSEL、つまり0x01をリクエストすればBOOTSELボタンを押した状態でリセットするのではないか、という淡い期待を抱いでちょっと書いてみる。
いちおうTinyUSBのtusb_control_request_tを調べて
となっていて、USBのcontrol transactionのヘッダと同じ意味だというのを確認した。
macOS側でlibusbを使って、
として、VendorIDとproductIDでデバイスをオープンする(libusbって簡単でいいなあ。I/O Kitはうんざりするよ)。
ちゃんとオープンできたら、
でitnerface#2を要求する。
そしてinterface#2に対してcontrol transactionをかける。RequestTypeは
こうして、libusbをリンクすればUSB経由でPi Picoをリセットできるはずである。
ということでやってみるとうまくいった。あてずっぽうがうまく行くことも、たまにある。ラッキー。
他に似たようなことをやってる人を見かけなかったので、この便利さをお裾分けしようと思ってgithubにあげた。
これはmacOSだけでなくRaspberry Pi OSやその他のlinuxでも同じ。Windowsはわからん。ごめん。
- Pi Picoのコードにpico_stdio_usbモジュールを含める
- pico_stdio_usbをデフォルト状態で使う
- そのPi PicoをmacOSにUSBで接続する
- macOSにCDCクラスのデバイスファイル(/dev/tty.*)ができる
- そのデバイスファイル経由でPi Picoと通信する
ただし、pico_stdio_usbでは大きなデータはやりとりできない。またデータはフラットなバイト列としてしかみることができないので、なんらかの上位プロトコルを決める必要がある。
でもまあ、これでPi Pico側は大きなプログラムになることはなく、管理しやすい。
ところでこれを前提にすると、USB経由でPi Picoをリセットできることをみつけた。リセットの状態はBOOTSELモードとオンボードのflashストレージから読み込む通常モードの両方が選べる。これは便利だと思う...
4 USB経由でPi Picoをリセットする
Pi PicoをリセットするのにいちいちUSBコネクタを抜き差ししているとコネクタがへたってきてしまう。ケーブルがダメになるくらいならいいけど、基板の上の小さなmicroUSBのポートは基板ごと壊れそうだし、Mac側のUSB-Aやtype-Cだと強そうだけど、これが壊れると修理費用にPi Pico何個ぶんが必要になるかわならない。基板上の30番ピン「RUN」端子をGroundに落とせばリセットできるけど、年寄りの目ではチカチカして間違いそうになる(こんなまんなかへんじゃなくて、ボードの端の近くにしてほしかった)。ハードウェアスイッチを作ったりしてる人がいるけど、あまり僕の趣味じゃない。
ということでほんとはUSBからリセットできると便利なんだけど、前回USBPorberでみたときにinterface#2に「Reset」とあったのでなにかそういう機能がありそうではある。ところが情報が完全ではない。
pico_stdio_usbの記述に
This library also includes (by default) functionality to enable the RP2040 to be reset over the USB interface. (このライブラリには、RP2040 を USB インターフェースでリセットする機能も (デフォルトで) 含まれている)
4.1 手探りで探す
pico_stdio_usbモジュールのreset_interface.cというソースのなかに、TnyUSBのコントロールリクエストに対するコールバックらしいのがあって、主要部分を抜き出すとstatic bool resetd_control_request_cb(uint8_t __unused rhport, tusb_control_request_t const *request) { if (request->wIndex == itf_num) { if (request->bRequest == RESET_REQUEST_BOOTSEL) { .... if (request->wValue & 0x100) { gpio_mask = 1u << (request->wValue >> 9u); } reset_usb_boot(gpio_mask, (request->wValue & 0x7f) | PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK); } if (request->bRequest == RESET_REQUEST_FLASH) { watchdog_reboot(0, 0, PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS); return true; } return false; }
ここに出てくるbRequestの定数はreset_interface.hに定義があって
// reset to BOOTSEL #define RESET_REQUEST_BOOTSEL 0x01 // regular flash boot #define RESET_REQUEST_FLASH 0x02
いちおうTinyUSBのtusb_control_request_tを調べて
typedef struct TU_ATTR_PACKED{ union { struct TU_ATTR_PACKED { uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. uint8_t type : 2; ///< Request type tusb_request_type_t. uint8_t direction : 1; ///< Direction type. tusb_dir_t } bmRequestType_bit; uint8_t bmRequestType; }; uint8_t bRequest; uint16_t wValue; uint16_t wIndex; uint16_t wLength; } tusb_control_request_t;
macOS側でlibusbを使って、
int ret = libusb_init(NULL); libusb_device_handle *handle; uint16_t vendor_id = 0x2E8A; uint16_t product_id = 0x000A; handle = libusb_open_device_with_vid_pid(NULL, vendor_id, product_id);
ちゃんとオープンできたら、
int interface_number = 2; ret = libusb_claim_interface(handle, interface_number);
そしてinterface#2に対してcontrol transactionをかける。RequestTypeは
- 方向はホスト→デバイス
- requestTypeは標準USB
- requestTargetをinterface
uint8_t reqtype = (0 << 6) // standard request host to device | (1); // to interface uint16_t windex = interface_number; uint16_t wvalue = 0; ret = libusb_control_transfer(handle, reqtype, request, wvalue, windex, NULL, 0, 10);
ということでやってみるとうまくいった。あてずっぽうがうまく行くことも、たまにある。ラッキー。
他に似たようなことをやってる人を見かけなかったので、この便利さをお裾分けしようと思ってgithubにあげた。
これはmacOSだけでなくRaspberry Pi OSやその他のlinuxでも同じ。Windowsはわからん。ごめん。
2021-05-16 21:30
nice!(0)
コメント(3)
RasPi PicoのUSB関連記事をgoogle検索していて御ブログを発見しました。現在gcc + MakefileでPicoのプログラムをベアメタル環境で書いております。コンパイルと出来上がったuf2をPicoのストレージに投げるところまでmakeで行えていますが、肝心のPicoのリセット動作がそれから外れており、なんとかならないものかなぁ、、と思っていたところ、お作りになられたbootselBootを見つけubuntuで(udevを書くことで一般ユーザー権限でも)問題なく動作させることができ、この問題が解決されました。有益なプログラムを有効活用させていただきます。ありがとうございます。
by やまちゅう (2023-05-02 13:23)
コメントありがとうございます。
お役に立てて嬉しいです。
この後もPi Picoを使い倒そうといろいろ試しているのですが、一身上の都合でまとめる時間がなくて、停滞しています。
なんとか平常運転に戻そうとしていますので、そうなればまたお立ち寄りください。
by decafish (2023-05-02 20:26)
次回作、楽しみにしております!
by やまちゅう (2023-05-04 13:24)