2018年5月23日水曜日

ESP8266にDTRとRTSで自動書き込みをする

ESP8266を搭載したESP-WROOM-02が日本の組み込み市場に出てきてから約3年になりますが、あまりこれに関する記事を見なかったので(少しはあるようです)。
やっぱり組み込み系の記事を見ていると圧倒的にソフト屋さんのブログが多く、ハード屋さんが全然いないんだなって。

ESP8266のリセット回路

ESP8266のリセット回路はこのようになっています。IO0ピンをLにした状態で立ち上げる(リセットする)とブートローダーが起動し、UARTからプログラムが書きこめるようになります。すなわち、IO0と~RESETのスイッチを両方押して、~RESETを離してからIO0を離して書き込みボタンを押すという操作が必要になります。
ですが、書き込みをするたびにスイッチ2つを操作しないといけないのはいささか不便です。可能ならばArduino IDE上で書き込みボタンを押したらそのまま勝手に書き込まれてほしいものです。

そんな誰もが考えることは、ちゃんとSDKの提供者たちも考えてくれていて、その解決策が提供されています。
ズバリ、DTR端子とRTS端子を使います。

シリアル通信のフロー制御

DTR端子とRTS端子を使うと言いましたが、TXDやRXDとは違ってあまり聞きなれない端子です。これはいったい何なのでしょう。

UARTなどのシリアル通信では、送信側が送信したいタイミングでデータの送信をできてしまいます。ですので、受信側が受信する準備ができていない場合、勝手にデータを送信されても取りこぼしてしまいます。そのようなことがあっては不都合な場合、相手方に「受け入れ可能だよ」と言うことを伝える制御を行います。その制御のことをフロー制御と言います。
信号線名 名称 説明
TXD Transmit Data 送信データ
RXD Receive Data 受信データ
TXDを受ける。
DTR Data Terminal Rady 端末準備完了
機器の電源が入りポートが開かれると1になる。
DSR Data Set Ready データセットレディ
DTRを受ける。
RTS Request To Send 送信要求
自分の受信バッファーに十分な空きがあり、データの受け入れが可能にになると1となる。
CTS Clear To Send 送信可能
RTSを受ける。
SG Signal Ground 信号線の接地ライン

フロー制御ありのシリアル通信は、通常このように接続します。DTRで相手にこちらの端末が生きていることを伝え、RTSでデータ受け入れ可能であることを伝えます。
このことから、RTSが1になるときは必ずDTRも1になっているということがわかります。こちらの端末が通信できる状態じゃないのにデータ受け入れ可能になるわけはないですから。
FT232などのUSB-シリアル変換ICにもこのフロー制御用のピンは出ており、ソフトウェアから任意に出力を変更可能です。ただし、FT232ではDTRとRTSの論理が逆転しているので注意が必要です。
FT232R Datasheet - FTDI Chip より引用)

DTRとRTSを使ったリセット回路

さて、これを踏まえてDTRとRTSを使ったリセット回路を作る必要がありますが、大前提として「通常使用時(ESP8266の通常動作時)にシリアル通信をしてもリセット指令が出ない」というのが最低限の要件として挙げられます。
フロー制御を使っていないと言いつつも別用途でそれらの線を結線してしまっているわけですから、例えばPCで汎用ターミナルソフトを使ってESP8266とシリアル通信したときに、そのソフトが気を利かせてフロー制御を行ったがゆえに偶然リセットがかかっては困ります。
そこで先ほどの赤字を思い出してください。RTSが1になるときは必ずDTRも1になっているので(RTS,DTR)=(1,0)は通常のフロー制御ではありえないということになります。
なので、この状態をリセットに割り当てればいいわけです。
その条件をもとにトランジスタ1個で作った回路がこちらになります。FT232に合わせてDTRとRTSの論理を逆転させています。
(~RTS,~DTR)=(0,1)のときのみ~RESETが0になり、他の時は1になります。
そのほか、~RTS=IO0になりますので、~DTR=0にした状態で~RTSを操作することでIO0を変更することもできます。
これで無事にリセットシーケンスが送れるようになりました。めでたしめでたし。

ただ、1つ問題があります。
IO0を出力に使えません。また同じ系統の問題として、手動用のスイッチと共存させることができません。そうすると、RTSとIO0の間に1段トランジスタを挟みたくなりますよね。それならばせっかくなので対称な回路にしてしまいましょう。
これでよく見かける回路になりました。完成です。

真理値表を書くとこんな感じになります。
~RTS ~DTR ~RESET IO0
L L H H
L H L H
H L H L
H H H H
(~RTS,~DTR)=(L,H)のときのみ~RESETがLになり、他のパターンでは~RESET=1となります。そのほか、リセットにかかわらずIO0を制御する方法も確保できており、出力はプルアップとなっているためスイッチとの共存も可能です。
トランジスタ2石で完璧なリセット回路が出来上がりました。

自動書き込みを行う

さて、回路が出来上がったら実際に書き込みます。
Arduino IDEのツールメニューからReset Methodを"nodemcu" (NodeMCU)に指定してあげれば書き込みの直前でDTRとRTSを操作してくれます。
実際に書き込みの時のRESET端子とIO0端子を観察した画像がこちらです。青が~RESET、黄色がIO0です。最初にリセットをかけてから、その入れ替わりでIO0をLにしています。たいていRESET後はクロックが安定するまで(数~十数ms程度)マイコンは立ち上がりませんので、 RESETを終了させるのと同時にIO0をLにしても十分間に合うわけですね。


書き込みが始まるとIO0の黄色線がガタガタ震え始めています。
これは実際にマイコンとデータのやり取りを始めてRTSが変化しているためですね。フロー制御が行われているだけですが、目論見通り~RESETは安定してHを保てています。

これで幸せなESPライフが送れるようになりました。

1 件のコメント: