先にオチを書いておくとUSB 1.1世代のセルフパワーのデバイスが、Type-Cで地味にトラブることがあるね。という話です。
USB Type-C to USB2.0 Micro-Bケーブルで、ちょっと古いデバイスとPCを繋ぐケースはよくありますよね。最近使っているRaspberry Pi PicoもMicro USBのデバイスなのですが、使っている間に
- Raspberry Pi Pico W(無線仕様)
- Pico Wをバッテリー駆動する(VSYSに給電)
- Type-C to USB 2.0 micro USBケーブル、またはType-C to Standard-AアダプタでホストPCと接続
という条件で、ケーブルを
- デバイス
- ホストPC
の順に接続するとUSBで電力が供給されず、結果USBデバイスとして機能できないという現象を踏みました。接続順を逆にすると問題は起きません。またType-CではなくType-Aでホストに接続しても問題は起きません。さらに標準のPicoの場合も問題は起きません。無線仕様のPico Wだけです。
USBレガシーケーブルアセンブリ
USB Type-Cの仕様では、Type-C以前の古いコネクターとの配線を Legacy Cable Assemblies1として定義しています。
それぞれのピンをどう配線するか、Type-Cにだけにあるピンをどう処理するか明確に記述されています。Type-Cケーブルでよく話題に上がる CC
(Configuration Channel)ピンは、Micro-Bケーブル相手の場合はType-Cケーブル内で5.1kΩ±20%の抵抗を介してGNDに接続されます。
今回の現象
3本の単三電池でVSYS
に給電したPico Wを、ときどきUSBでホストPCに接続。そのときに少し挙動を変えるようなプログラムを書いていました。回路はPico Wデータシート2を参考に、ショットキーダイオードを挟むだけのシンプルなもの。
データシートではVSYSにバッテリー給電しながらUSB接続して給電を受けても問題がない回路構成になっている旨説明されています。実際にUSBケーブルを着脱してもPico Wは動き続けます。ただしホストPCとのUSB通信はときどき確立されません。ん?ときどき?
世界中でめちゃくちゃ数が出ているIoT製品がこんな変な動きするわけないじゃんよ?とか思いつつもRaspberry Pi Pico forum3で相談したところ、USBから給電されているはずのVBUS
の電圧を実際に測ってみようぜーということになり、接続条件を変えながら測ってみました。
Device | USB未接続 | デバイス->ホストの順で接続 | ホスト->デバイスの順で接続 |
---|---|---|---|
Pico | 0.42 V | 5.17 V | 5.17 V |
Pico W | 1.36 V | 0.97〜1.09 V (計測値は不安定) | 5.16 V |
結果、ケーブルの接続順によってはUSB通信どころかUSB電源(VBUS
)が供給されていないことがわかりました。さらにUSB未接続時のPico WのVBUSにはどこからか1.36 Vの電圧が。犯人はヤツ。
USB PD vSafe0V
Type-Cの仕様書ではVBUSの"0V"をUSB Power Deliberyで定義される vSafe0V4としています。vSafe0Vの範囲は0〜0.8V。上記のUSB未接続時のPico Wの値 1.36VはvSafe0Vを余裕で超えています。これらの事実から、vSafe0Vを超えたVBUSを持つデバイスをType-C接続した際に、ホストのUSBコントローラーが異常と判断してVBUSに電力供給をしない操作をした可能性が推察されます。
ホスト->デバイス の順で接続して問題が起こらないのは、Type-Cコネクタ内でGNDに接続されたCCの状態によって「俺はSource (ホスト)だ(確信)」と決心がつくから。 デバイス->ホスト 順の場合はまだ決心がつかない。たぶん。詳しくはUSB Type-C Specを読んでください。僕は諦めました。
ちなみにvSafe0Vの閾値はUSBコントローラである程度いじれるようですが、たぶん非推奨。調査の過程で、無線仕様ではない標準PicoのVBUSにつながっている GPIO24
5 をpull-upしても特に影響なかったことが解せなくて、こちらの電圧を測ってみたところ 0.96 V でした。超えてるじゃん!けど、これくらいは許容されるらしい。
問題の回避策
なんだかえらく遠回りした気持ちです。バッテリー駆動するPico WがUSB接続したことを検出する、という本来の目的を達成するためには、USB接続されたら確実に通信を確立してもらう必要があり、これは前出の事実と推察から次の3パターンで対応できます。
A. ソフトウェア対応
Pico WのVBUSは、無線モジュールCYW43439のWL_GPIO 2
に接続されています。このGPIOはデフォルトでtri-stateなのですが、これを明示的にLow出力に設定すると、この問題は回避できます。ただし、GPIOでVBUSの状態を見ることはできなくなります。
cyw43_arch_init();
cyw43_arch_gpio_put(2, 0);
B. ハードウェア対応
VBUSへの漏れが問題なので、Pico WのVBUSに繋がる40ピンをPull-downする回路を追加すると、この問題は回避できます。抵抗値はUSBのサスペンドを配慮するなら10kΩ程度じゃないかということ。
手持ちの抵抗がなかったので1kΩ程度で試したところ、問題が起きないことを確認できました。
C. 根本のファームウェア対応
無線モジュールCYW43439のGPIOの設定をソフトウェア的に変更すれば良いじゃない?そう思うのですが、このチップの仕様は公開されていません。マジかよ。pico-sdkが当該チップとSPIで通信している箇所も、あいにくGPIOのpull-up/down抵抗を設定する処理は含まれていませんでした。マジかよ。
というわけでエンドユーザーにとってこの選択肢は無し。Raspberry Pi PicoのGithub Issueにレポート6して、対応を依頼しました。たぶんPico Wのデータシートでこの問題が言及されるオチではないかなと考えています。
結局、vSafe0Vを超える電圧が、何から漏れた電流由来なのかは確定できていません。無線モジュールCYW43439のGPIOか、オンボードのショットキーダイオードMBR120VLSFT1Gのいずれかだと思われます。
こんな感じで僕の2024年の大型連休は潰れました。結果、よく知らなかったことを勉強できて面白かったのですが、なんだかえらく遠回りした気分です。後日、Raspberry Piのエンジニアから謝意と、この問題を調査する旨コメントをもらいましたのでチャラとしましょう。
問題や調査の詳細はGithubリポジトリにまとめてあります。