現在、方言を話すおしゃべり猫型ロボット「ミーア」を開発中。
ミーアのカスタム基板(ESP32ベース)を作成するために、ESP32開発ボードの回路図をまずは理解しないといけないと思い、見始めたところ、下記の回路図と真理表に遭遇し「なんじゃこりゃ」となった。
下記回路がESP32の自動ブートローダー機能に相当しているということで、調べ始めたが、久しぶりの回路図勉強でトランジスタやプルアップ抵抗など基本的なところまで遡る必要があったため、理解するのに6,7時間もかかってしまった。
今回は、ESP32の自動アップロード機能の仕組みに関して、理解した部分を記載したいと思います。もし間違いがありましたら、コメント欄でご指摘いただけますと幸いです。
UARTのフロー制御
まず、UARTはUniversal Asynchronous Receiver/Transmitter(汎用非同期式送受信機)の略。
デジタル信号の送信方法にはパラレル通信とシリアル通信の2つがあり、シリアル通信の中に同期式と非同期式がある。UARTは、このうちの非同期式シリアル通信に該当。
UARTで2つの機器間でデータのやり取りを行う場合、機器がデータを受け取る準備ができていない時に相手からデータを送られると受け取りそこなってしまう。そうならないように、送信側の機器に「データ送信はちょっと待ってね」と通知して送信を一時中断したり、速度を低下させたりして、データの流れを調整することをフロー制御という。そして、フロー制御の中にもいくつか方式があるが、今回関係するのはハードウェアフロー制御。
- 送信側: 送信側デバイスがデータを送信したいとき、RTSラインをHighにする
- 受信側: RTSラインがHighになると、受信側デバイスは送信側デバイスからデータを受信する準備ができているか確認。準備ができていれば、CTS(Clear to Send)ラインをHighにして送信側に送信許可を通知
今回は、PC(送信側)からUSBを介してESP32(受信側)にプログラムを書き込むことを想定しているので、その場合は
- PC(送信側)は、ESP32に書き込むプログラムのデータを用意し、通信の準備ができたらDTRをHighにして「通信準備完了」をESP32に通知。
- ESP32(受信側)は、DTRを検出すると、送信側が通信を受け入れる準備ができていることを知る。
- 通信確立後、PCはプログラムのデータを送信したいときは、RTSをHighにして「送信準備完了」を通知。
- ESP32はRTSを検出すると、バッファが空いていればCTSをHighにして「受信準備完了」をPCに通知
- PCは、CTSがHighを検出すると、プログラムのデータの送信を開始。
- ESP32のバッファが一杯になると、CTSをLowにして「受信一時停止」を通知。
- PCは、CTSのLowを検出すると、データの送信を一時停止。
- ESP32のバッファが空くと、再びCTSをHighにして「受信再開」を通知。
- 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に書き込みたい時は、下記の順番にスイッチボタンを押すことで可能になる。
- 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状態を保てれば良い。
自動ブートローダーと真理表
さて、いよいよ、こちらの回路図と真理表を見ていきたいと思う。
まず、この真理表の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トランジスタ。
トランジスタの役割については、下記記事をご参照ください。
https://hegtel.com/transistor.html
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ボタンを押す)と自動でプログラム書き込みできるようになること。
コメント