見出し画像

ESP32-C3-WROOM-02-N4のブートモードを調べてみた。


ESP32-C3-WROOM-02-N4とは

秋月電商で売っているESP32のマイコンです。2024/07現在で310円とお手頃で、Bluetooth BLE と Wifi が使えて、USBシリアル接続とJTAGが内臓されているそうです。私は、ここ最近になってESP32-WROOM-32E を買って遊んでしたのですが、ESP32-C3やESP32-S3のことを知りませんでした。USBシリアル接続のモジュールが要らんの??と驚いて興味を持った次第です。

ブートモードについて

今回は、これのブートモードについて調べてみました。ブートモードとは、マニュアルによると、

  • SPI Boot mode

  • SPI Download boot mode

  • Joint Download boot mode

の3種類があるようです。

SPI Boot mode は電源が入るとすぐに書き込まれているプログラムが動作するモードです。おそらく SPI 接続されているフラッシュなどの領域からプログラムを読み込み起動させるという意味だと思います。

SPI Download boot mode は、実験でもうまく動作できなかったので、正直よくわかりません。おそらく内臓の USB シリアルからプログラムのデータをSPI接続のフラッシュに書き出すのと同時に動作させる?みたいな説明書きがあったような気がしますが、どうなんでしょうか。

Joint Download boot mode は、旧ESP32で一般的だった UART によるプログラムの書き出しモードに加え、内臓USBシリアル接続経由での書き出しをまとめたモードのようです。

先人の方がネットでブートモードについて、回路などの情報を出しており、参考にしましたが、如何せんモードの条件がはっきりしなかったのです。
ESP32-C3のマニュアルも確認しましたが、どことなくあいまいではっきりしません。

ブートモードについて解説のあるページ。

そして C3 のデータシート。

ブートモードを決定する方法

いろいろ情報を見ていくと、ブートモードを決定するGPIOがあることがわかります。GPIO2、8、9 が GPIO_STRAP_REG レジスタのGPIO_STRAPPING フィールドとして使われ、ブートモードを決定する、といった内容がデータシートに記載がありますが、実験をしたところ、正確ではありませんでした。

実験によって分かったことをまとめると、実際には、ブートモードを決定するタイミングは、EN(RST) が LOW → HIGH (ライジング)になったときで、その時の

  • GPIO2

  • GPIO3

  • GPIO8

  • GPIO9(BOOT)

の4つのGPIOのHIGH/LOW状態によって決定されていました。
これは、UART0 接続をしていると、EN を LOW → HIGH にすると起動メッセージのようなものが出力されます。ここに

rst:0x1 (POWERON),boot:0x4 (DOWNLOAD(USB/UART0/1))
略
waiting for download

といったメッセージが出ていることに気づき、4つのGPIOの関連をまとめることにしました。
boot mode は4ビットで構成されており、上位からGPIO9、GPIO8、GPIO3、GPIO2 の順番です。それぞれをGND接続、PULLUP に変化させ、boot mode の番号と出力を調べました。

出力を見てみると、マニュアルには載っていない奇妙なモードが存在していることが分かります。

  • USB_BOOT ←なんだこれ

  • SPI_BOOT (SPI Download Boot mode)

  • UART0_BOOT ←なんだこれ

  • DOWNLOAD(USB/UART0/1) (Joint Download Boot mode)

  • SPI_FAST_FLASH_BOOT (SPI Boot mode)

検証を進めて分かったのですが、USB_BOOT と UART0_BOOT は、UART0 による書き出し、USBシリアルによる書き出し、JTAG によるデバッグの3点で挙動を確認しましたが、何かしら問題がありました。もしかして、このあたりがドキュメントで正確に書かれていない理由かもしれないと勘ぐってしまいました。結果は後程。

検証の項目と検証環境について

検証の回路は、下記のとおりです。かなり雑で申し訳ないです。

検証の回路

UART0とUSBシリアルは、どちらも接続し、PlatformIO のポート指定により書き出しの接続を切り替えて行いました。UART0にはコンソールメッセージも出てきますので、それをメモしておきます。

環境は、Windows11 23H2、PlatformIO を使いました。テストコードは、Arudino プラットフォームの簡単なコードを用います。UART0に接続しているUSBシリアル接続モジュールは、ESP32用の自動起動付きのものを利用しています。ESP32 loader というものでしょうか。

なぜ回路を公開したかというと、検証の結果がどうもリブートのタイミングの違いによる動作不良も考えられるので、例えば EN(RST) に接続しているコンデンサの容量などによっては、結果が異なるかもと考えたためです。

また、EN(RST) と GPIO9(BOOT) については、押しボタンで操作します。GPIO8、GPIO3、GPIO2 は、NCにはせず、GNDかPULLUPのどちらになるように設定しておきました。

検証の項目は、以下の通りです。

  • UART0 でのプログラム書き出しができるかどうか

  • USB(内臓USBシリアル) でプログラム書き出しができるかどうか

  • USB(内臓USBシリアル) で JTAG によるデバッグができるかどうか

この結果により、今後 ESP32-C3 を使った基板設計で GPIO の条件を定めていきます。

UART0でのプログラム書き出し

早速検証結果です。書き出しをする前にモードを確認するため、配線後に必ずリブートを行っています。連続した書き出し、リブートをしないで、配線を変えるなどの操作は想定していません。(モードの決定はあくまで EN(RST) が LOW → HIGH になったときだけなので

特質すべき点は、0x2 はマニュアルではInvalid Combinationとなっているにも関わらず、UART0の書き出しが動作しています。

また、GPIO9(BOOT) は、押しボタンで再起動時(EN(RST) の押しボタンをオンオフ、つまりEN(RST)がGNDからPULLUPに変化する時)にGNDにしていますが、UART0 で書き出す際は、すでにGPIO9のボタンを離していて PULLUP 状態に戻っています。

加えて、UART0 に接続されている USB シリアル接続モジュールにより、物理的に EN(RST) と GPIO9(BOOT) がGND に落とされるため、SPI Boot mode になることはなく、下位ビットに対応するモードに切り替わるため N/A (該当なし)としました。

おそらくですが、SPI Boot mode では、すでにプログラムが動作していますので、UART0 はプログラムの出力となっているので、書き込みのコマンドは受け付けられないでしょう。

結論は、0x2 および 0x4~0x7(GPIO8がPULLUP)の場合は、UART0 による書き出しができるということになります。

USBでのプログラムの書き出し

こちらも検証結果です。USB(内臓USBシリアル接続) では、リブート時に USB が切断されるため、検証が大変でした。こちらも書き出し前には必ずリブートをおこなし、同時に接続している UART0 から起動メッセージを随時確認する方法で行っています。

よくわかないのは、UART0 に出てくる書き出し中や後にでてくる再起動のメッセージで、rst:0x15 というものです。どのような理由でリセットをしたかを表したものかもしれません。内臓USBシリアルでリセットする場合は、ENを GND に落とすのではなく、内部でそういうことにしているためなのでしょうか。

そう考えると、UART0(外部でUSBシリアル)の場合は、物理的に EN / GPIO9 をGNDに落としますが、内部の場合は物理的に EN / GPIO9 を GND に落としているわけではないということになり、これが挙動の違いを生んでいるように思います。

つまり、UART0では、物理的なリブートによりモードが切り替わるので挙動がはっきりするわけですが、USBでは物理的なENが操作されない限りモードが切り替わることがありません。USB で停止をしてしまうと操作不能によく陥るのはこういうことが原因だと思いました。

USBの書き出しを想定した開発ボートを作成する場合には、EN(RST) / GPIO9(BOOT) ボタンをつけておいた方が無難でしょう。

結論です。こちらも 0x0 および0x4~0x7(GPIO8がPULLUP)の場合は、USB による書き出しができるということになります。

JTAGによるデバッグ

JTAG は、いままで使ったことがありませんでした。昼夜逆転さんのページを熟読して、jtagのセッティングをしました。

素のままでは、USBドライバが JTAG に対応していないぽいので、USBのドライバを充てることにしましたが、どうしても認識しません。

試行錯誤を繰り返した結果、デバイスマネージャの「ユニバーサルシリアルバスデバイス」→「USB JTAG/serial debug unit」のドライバの更新で「USB JTAG debug unit」と「WinUsb Device」の2つが出てきますが、「USB JTAG debug unit」を選択したところ、JTAG が動作するようになりました。

また、デバッグが動作したとしても、ビルドオプションで
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MODE=1
を指定すると、JTAG が途中でエラーを起こして動作が停止してしまうので、こちらの指定は外して検証しています。

では検証結果です。

JTAGの動作については、0xc~0xfのみということなので、SPI boot mode でないと動作しないこと、かつ、GPIO8がPULLUPであることが条件となります。
JTAG デバッグの時は、いわゆる DOWNLOAD mode では正常動作しないようです。

検証結果のまとめ

  • 0x2 および 0x4~0x7(GPIO8がPULLUP)の場合は、UART0 による書き出し可能。

  • 0x0 および0x4~0x7(GPIO8がPULLUP)の場合は、USB による書き出し可能。

  • 0xc~0xf(GPIO8がPULLUP)の場合は、JTAG が可能。

マニュアルにも書いてあるし、検証結果を見るとはっきりしましたが、GPIO8を PULLUP すれば、GPIO2、GPIO3 の状態はかかわらず結果が全く同じです。つまり GPIO2 と GPIO3 は考える必要がなくなります。

そして、GPIO8 を PULLUP にしておけば、UART0、USB、JTAG 全てに対応可能になります。
今後の ESP32-C3 の回路は、全部 GPIO8 を PULLUP にしておけばOKで、GPIO2とGPIO3は自由に使ってよし!ということですね。

でも注意としては、

  • USB の書き出しで操作不能になった場合の対処には、EN(RST) / GPIO9(BOOT) ボタンがあると便利であること

  • JTAG を利用するには、GPIO9(BOOT)を明示的に PULLUP しておく必要があること

の2点です。

最後に

この検証をするきっかけは、ESP32-C3のDIP基板を作ろうとしたためでした。どうせ作るなら、USB も UART0 でも接続できるインターフェイスをつけたいなと思い、そうなるとブートモードを理解しておかなければと調査したからです。
最初は、USBとUART0を指定する条件があるのだと思っていたのですが、実際は全然違いました。

この結果が何かのお役に立てれば幸いです。
最後まで読んでいただいてありがとうございました。


この記事が気に入ったらサポートをしてみませんか?