【ESP32】自動ブートローダー(書き込み)の仕組みと真理表を理解する

EN-capacitor
この記事は約13分で読めます。

現在、方言を話すおしゃべり猫型ロボット「ミーア」を開発中。

ミーア
おしゃべり猫型ロボット「ミーア」は、100以上の種類の豊かな表情で、悲しみや喜びを共有します。様々な性格(皮肉・おせっかい・ロマンチスト・天然)や方言(大阪弁・博多弁・鹿児島弁・広島弁)も話してくれます。ミーアとの暮らしで、毎日の生活をもっ...

ミーアのカスタム基板(ESP32ベース)を作成するために、ESP32開発ボードの回路図をまずは理解しないといけないと思い、見始めたところ、下記の回路図と真理表に遭遇し「なんじゃこりゃ」となった。

下記回路がESP32の自動ブートローダー機能に相当しているということで、調べ始めたが、久しぶりの回路図勉強でトランジスタやプルアップ抵抗など基本的なところまで遡る必要があったため、理解するのに6,7時間もかかってしまった。

esp32-auto-bootloader

今回は、ESP32の自動アップロード機能の仕組みに関して、理解した部分を記載したいと思います。もし間違いがありましたら、コメント欄でご指摘いただけますと幸いです。

UARTのフロー制御

まず、UARTはUniversal Asynchronous Receiver/Transmitter(汎用非同期式送受信機)の略。

デジタル信号の送信方法にはパラレル通信とシリアル通信の2つがあり、シリアル通信の中に同期式と非同期式がある。UARTは、このうちの非同期式シリアル通信に該当。

UARTで2つの機器間でデータのやり取りを行う場合、機器がデータを受け取る準備ができていない時に相手からデータを送られると受け取りそこなってしまう。そうならないように、送信側の機器に「データ送信はちょっと待ってね」と通知して送信を一時中断したり、速度を低下させたりして、データの流れを調整することをフロー制御という。そして、フロー制御の中にもいくつか方式があるが、今回関係するのはハードウェアフロー制御。

  • 送信側: 送信側デバイスがデータを送信したいとき、RTSラインをHighにする
  • 受信側: RTSラインがHighになると、受信側デバイスは送信側デバイスからデータを受信する準備ができているか確認。準備ができていれば、CTS(Clear to Send)ラインをHighにして送信側に送信許可を通知

今回は、PC(送信側)からUSBを介してESP32(受信側)にプログラムを書き込むことを想定しているので、その場合は

  1. PC(送信側)は、ESP32に書き込むプログラムのデータを用意し、通信の準備ができたらDTRをHighにして「通信準備完了」をESP32に通知。
  2. ESP32(受信側)は、DTRを検出すると、送信側が通信を受け入れる準備ができていることを知る。
  3. 通信確立後、PCはプログラムのデータを送信したいときは、RTSをHighにして「送信準備完了」を通知。
  4. ESP32はRTSを検出すると、バッファが空いていればCTSをHighにして「受信準備完了」をPCに通知
  5. PCは、CTSがHighを検出すると、プログラムのデータの送信を開始。
  6. ESP32のバッファが一杯になると、CTSをLowにして「受信一時停止」を通知。
  7. PCは、CTSのLowを検出すると、データの送信を一時停止。
  8. ESP32のバッファが空くと、再びCTSをHighにして「受信再開」を通知。
  9. PCはデータの送信を再開し、全データが送信されるとDTRをLowにして「通信終了」を通知。

まず、DTRで通信確立を受信端末に伝えて、そのあとで、RTSで「データを送っても良いですか」と受信端末に伝えるという流れですね。まず通信を確立しないと、そもそもデータを送れないし、かといって通信が確立できても受信側にデータを受け入れる容量がないと送っても溢れてしまうので、今からデータを送ってもいいですかと聞くという二段構えです。

ESP32のENピンとIO0ピン

ENピン:再起動用ボタン。Enableの略

  • High:通常の動作モード。フラッシュメモリからプログラムが読み込まれ、実行が開始される。ENピンがHighになると、ESP32はリセットから回復し、通常の動作を開始する。
  • Low:ESP32はリセット状態になる。すべての動作が停止し、プロセッサの内部状態が初期化される。ENピンがLowになると、ESP32はハードリセットが発生し、すべての動作が停止する。
  • プログラムの再実行、挙動が不安定になった時に押す。
  • プルアップ抵抗は内蔵されていない。

IO0(Input/Output 0)ピン

  • High:通常実行モードで、フラッシュメモリに保存されたプログラムが実行される。
  • Low:ブートローダーモード(フラッシュモードともいう)で、新しいプログラムをフラッシュメモリに書き込むための特別なモードに入る。ESP32はプログラムの書き込みを受け入れる準備ができている状態になる。
  • プルアップ抵抗が内蔵されているので、通常でHighになっている。

というわけで、プログラムを新しくESP32に書き込みたい時は、下記の順番にスイッチボタンを押すことで可能になる。

ESP32-DevKitC V4
手動アップロードの際のスイッチ操作
  • STEP1
    [Boot]スイッチ(IO0に繋がってるスイッチ)を押す(IO0:Low, EN:High)

    次回のリセット時にブートローダーモードで起動する準備が整う

  • STEP2
    [Boot]スイッチを押した状態で[EN]スイッチを押す(IO0:Low, EN:Low)
  • STEP3
    [Boot]スイッチは押した状態で[EN]スイッチを離す(IO0:Low, EN:High)

    ブートローダーモードに入り、プログラミング書き込みを始める

  • STEP4
    [Boot]スイッチを戻す(IO0:High, EN:High)

    プログラムの書き込みが完了し、書き込まれたプログラムを実行する

上は一例であって、正確にはI0O=EN=Low状態が維持された状態で、ENのみHighに切り替われば、その瞬間をブートローダーモードと検知してプログラミングの書き込みを始められるので、上のステップの1,2に関しては、[Boot]が先でもいいし[EN]が先でも良いという認識。I0O=EN=Low状態を保てれば良い。

ESP32手動ブートロード

自動ブートローダーと真理表

さて、いよいよ、こちらの回路図と真理表を見ていきたいと思う。

esp32-auto-bootloader

まず、この真理表の0,1はそれぞれ下記を表している。

・1=High=正の電圧(+5V or +3.3V)
・0=Low=0V

この図と真理表は、ホスト側のPCのプログラムで、USB-UART変換チップのDTR端子、RTS端子の電圧をそれぞれ調整して、(DTR, RTS)=(1,1)(0,0)(1,0)(0,1)の状態を作り出し、さらに2つのトランジスタ(Q1, Q2)をうまく使うことで、ENとIO0の電圧をうまく調整できますよということを示している。

トランジスタの役割
ここで使われているトランジスタはNPNトランジスタ。
トランジスタの役割については、下記記事をご参照ください。

トランジスタの仕組み
トランジスタにはPNP型とNPN型があります。 この構造を持つトランジスタを バイポーラトランジスタ と呼びます。 これに対し、電界効果トランジスタ(FET)を ユニポーラトランジスタ と呼びます。 ここでは、トランジスタの仕組みについて説

DTR=1, RTS=0の時

NPNトランジスタなので、ベース(DTR)からエミッタ(RTS)に電流が流れ、コレクタ(EN)からエミッタ(RTS)にも電流が流れる。

エミッタはRTSであり、0Vなので、コレクタからエミッタに電流が流れるとコレクタも0Vになるので、EN=0Vで、真理表では0になる。

一方で、IO0ピンは、DTRの電圧>RTSの電圧なので、Q2のベース(RTS)からエミッタ(RTS)へは電流は流れないので、もちろんコレクタ(IO0)からエミッタ(RTS)へも電流は流れない。なので、0Vになるかと思いきや、IO0ピンはENピンと異なりESP32内部にプルアップ抵抗があるので、3.3Vになり、真理表では1になる。

プルアップ抵抗に関しては、下記記事参照
https://voltechno.com/blog/pullup-pulldown/

DTR=1, RTS=0の状態では、Q2のトランジスタは、コレクターエミッタ間に電流を流さないので、スイッチが開いた状態とみなすことができるため、IO0ピンは下記の状態になっており、ESP32の内部抵抗が非常に大きいので、分圧の原理より3.3Vに引っ張り上げられる。

DTR=1, RTS=0状態をPCのプログラムで作り出すことで、EN=0, IO0=1の状態(=プログラム書き込み始める状態)を作り出せることが分かった。

DTR=0, RTS=1の時

先ほどとは逆の状態なので、ベース(RTS)からエミッタ(DTR)に電流が流れ、コレクタ(I0O)からエミッタ(DTR)にも電流が流れる。

エミッタはDTRであり、0Vなので、コレクタからエミッタに電流が流れるとコレクタも0Vになるので、IO0=0Vで、真理表では0になる。

一方、ENは、Q1トランジスタには電流が流れない=スイッチが開いた状態なので、0Vになるかと思いきや、ENに至るまでの配線にプルアップ抵抗(10kΩ)を取り付けて3.3Vに繋いでいるので、EN=3.3Vとなり真理表では1になる。

IO0ピンは、ESP32内部にプルアップ抵抗があるので、外部にプルアップ抵抗を取り付ける必要がないが、ENピンはプルアップ抵抗がないので、上記真理表を達成しようと思うと外側にプルアップ抵抗を取り付ける必要がある。

ENピンとGNDの間にコンデンサをさらに設置?

これで、真理表を達成できることはわかったが、やりたいことは、手動でENピンやBOOTピン(IO0ピン)を押さなくても、PCからプログラム書き込みを指示する(PlatformIOでUploadボタンを押す)と自動でプログラム書き込みできるようになること。

必要十分条件は、(EN, IO0)=(0,0)の状態から(EN, IO0)=(1,0)の状態を作り出すこと。この瞬間がプログラム書き込みとして認識されるので。

ESP32手動ブートロード

これを踏まえて、先ほどの真理表を見てみると、(EN, IO0)=(1,0)はDTR, RTSの制御でできるが、(EN, IO0)=(0,0)は達成できないことがわかる。

auto bootloader truth table

で、これをどうやって解決しているのかというと、Espressifの公式ドキュメントに

Boot Mode Selection - ESP32 - — esptool.py latest documentation

If this circuitry is implemented (all Espressif boards have it), adding a capacitor between the EN pin and GND (in the 1uF-10uF range) is necessary for the reset circuitry to work reliably. This is shown in the ESP32 Module section of the schematic.

Espressif

と記載されている。下記のようにコンデンサをつけて下さいねということ。

EN-capacitor

Chat-GPTに聞いたところ、下記の回答がきた

キャパシタ: 100nFのキャパシタは、リセットのタイミングを制御する役割があります。このキャパシタは、ENピンがLowになる時間を一時的に延長し、ESP32が確実にリセットされるようにします。

以下は、この動作の詳細です。

  • リセット時: DTR=1, RTS=0のとき、トランジスタがオンになり、ENピンはLowに引かれます。このとき、キャパシタは放電されます。
  • リセット後: DTR=0, RTS=1に切り替えると、トランジスタがオフになり、ENピンはプルアップ抵抗によってHighに引かれます。しかし、キャパシタが一時的に放電されるため、ENピンがHighになるまでの時間がわずかに遅延します。
  • プログラミングモード: この遅延により、IO0がLowになるタイミングと同期して、ESP32がプログラミングモードに入ることが確実になります。

この設計は、リセットとプログラミングモードへの切り替えのタイミングを調整し、プログラミングプロセスを確実かつ信頼性の高いものにするために使用されます。キャパシタの値は、リセットのタイミングを調整するために選ばれます。この値が大きすぎると、リセットが遅くなりすぎる可能性があります。小さすぎると、リセットが十分に行われない可能性があります。

図示するとこういうことかなと。

コンデンサがない状態で、DTRとRTSを切り替える場合、IO0ピンが1→0になると同時に、ENピンが0→1になってしまうために、プログラミングモードにIO0ピンが入れない。

なので、コンデンサをENピンとGNDの間に接続することで、ENピンの電圧が0Vから3.3Vに上がったとしても

  1. トランジスタがOFFになる: DTRがLow、RTSがHighになると、トランジスタがOFFになります。このとき、コレクタからエミッタへの電流は流れなくなります。
  2. ENピンの電圧立ち上がり: トランジスタがOFFになると、ENピンはプルアップ抵抗によってHighに引かれるようになります。しかし、コンデンサが充電状態になるため、その電圧の立ち上がりはゆっくりになります。
  3. コンデンサの充電: コンデンサはENピンと並列に接続されているため、ENピンの電圧が0のまま抑えられる時間が生じます。この時間は、コンデンサの容量とプルアップ抵抗の値に依存します。

この挙動により、ENピンの電圧がHighになるまでの時間がわずかに遅延を生じさせることで、IO0=EN=0の状態を作り出し、その後、ENのみ1になることで、プログラミング書き込みができるようになる。と理解しました。

推奨されるコンデンサの容量は?

コンデンサの容量が小さすぎると、EN=0に抑えられる時間が短くなるため、EN=IO0=0状態を作れなくなるかもしれないため、1μF以上ということなのだろう。一方でコンデンサの容量が大きすぎる場合は、IO0=EN=0の状態が長くなりすぎて、いつまで経ってもプログラムの書き込みを始められない状態に陥る(ここはこれ以上深入りしなかったが、プログラムを書き込み開始できませんみたいにTimeoutの表示が出てしまうとかになるのか?)。

同じEspressifの公式には

Some third party ESP32 development boards use an automatic reset circuit for EN & GPIO0 pins, but don’t add a capacitor on the EN pin. This results in unreliable automatic reset, especially on Windows. Adding a 1uF (or higher) value capacitor between EN pin and GND may make automatic reset more reliable.

Espressif

と、自作カスタムボードなど作成する際には、1μF以上のCapacitorを入れてねと記載があり、上限については記載がなかった。ただ、ESP32_DevKitC_V4の回路図では、GNDに接続しているCapacitorは0.1μFと、公式の記載の下限値の1/10で、ここは謎。

下記記事にも

https://pcbartists.com/design/embedded/esp32-ch340-programmer-schematic/

EN pin needs capacitor of at least 100nF and a pull-up of at least 10K for auto-program circuit to work!

ESP32 CH340 PROGRAMMER SCHEMATIC

と記載があり、もしかしたら0.1μFで良いのかもしれない。

というわけで、久しぶりの回路図だったので、トランジスタやプルアップ抵抗の仕組みなど基礎からやり直す羽目になり、だいぶ時間がかかってしまった。でも、ようやくESP32 自動ブートローダーの仕組みを理解できたので、自作基板の回路図を進めていけたらと思います。

ではでは♪

コメント

タイトルとURLをコピーしました