SSブログ

macOSからPi Picoを使う - その2 [Pi Pico]

前回Pi PicoをmacOSと一緒に使う方法を考えた。結論としては
  • Pi Picoのコードにpico_stdio_usbモジュールを含める
  • pico_stdio_usbをデフォルト状態で使う
  • そのPi PicoをmacOSにUSBで接続する
  • macOSにCDCクラスのデバイスファイル(/dev/tty.*)ができる
  • そのデバイスファイル経由でPi Picoと通信する
とする。こうするとUSBの面倒なvendor specificデバイスのdescriptor記述をしないですむし、PiPico側macOS側ともに直接USBのコードを書かずにすむので、機能の記述に集中できる。

ただし、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;
}
となっている。itf_num変数はinitialize時点でデスクリプタのbInterfaceNumberに設定されているようである。

ここに出てくるbRequestの定数はreset_interface.hに定義があって
// reset to BOOTSEL
#define RESET_REQUEST_BOOTSEL 0x01
// regular flash boot
#define RESET_REQUEST_FLASH 0x02
などとなっている。全部がわかったわけではないけど、このinterface#2にRESET_REQUEST_BOOTSEL、つまり0x01をリクエストすればBOOTSELボタンを押した状態でリセットするのではないか、という淡い期待を抱いでちょっと書いてみる。

いちおう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;
となっていて、USBのcontrol transactionのヘッダと同じ意味だというのを確認した。

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);
として、VendorIDとproductIDでデバイスをオープンする(libusbって簡単でいいなあ。I/O Kitはうんざりするよ)。

ちゃんとオープンできたら、
    int interface_number = 2;
    ret = libusb_claim_interface(handle, interface_number);
でitnerface#2を要求する。

そしてinterface#2に対してcontrol transactionをかける。RequestTypeは
  1. 方向はホスト→デバイス
  2. requestTypeは標準USB
  3. requestTargetをinterface
にして、windexをインターフェイス番号の2、付加データ無し、requestはBOOTSELだと0x01、FLASHだと0x02にしてcontrol transferを起動する。
    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);
こうして、libusbをリンクすればUSB経由でPi Picoをリセットできるはずである。

ということでやってみるとうまくいった。あてずっぽうがうまく行くことも、たまにある。ラッキー。

他に似たようなことをやってる人を見かけなかったので、この便利さをお裾分けしようと思ってgithubにあげた。

これはmacOSだけでなくRaspberry Pi OSやその他のlinuxでも同じ。Windowsはわからん。ごめん。
nice!(0)  コメント(3) 

nice! 0

コメント 3

やまちゅう

RasPi PicoのUSB関連記事をgoogle検索していて御ブログを発見しました。現在gcc + MakefileでPicoのプログラムをベアメタル環境で書いております。コンパイルと出来上がったuf2をPicoのストレージに投げるところまでmakeで行えていますが、肝心のPicoのリセット動作がそれから外れており、なんとかならないものかなぁ、、と思っていたところ、お作りになられたbootselBootを見つけubuntuで(udevを書くことで一般ユーザー権限でも)問題なく動作させることができ、この問題が解決されました。有益なプログラムを有効活用させていただきます。ありがとうございます。
by やまちゅう (2023-05-02 13:23) 

decafish

コメントありがとうございます。
お役に立てて嬉しいです。
この後もPi Picoを使い倒そうといろいろ試しているのですが、一身上の都合でまとめる時間がなくて、停滞しています。
なんとか平常運転に戻そうとしていますので、そうなればまたお立ち寄りください。

by decafish (2023-05-02 20:26) 

やまちゅう

次回作、楽しみにしております!
by やまちゅう (2023-05-04 13:24) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。