2021年3月21日日曜日

えっ、まだESP32にRTC外付けしてるんですか!?

煽り気味のタイトルで始めてみましたが、今回はESP32の内蔵RTCの話をしようと思います。
ESP32にはRTCが内蔵されています。

RTC timer: Allows keeping the system time during any resets and sleep modes, only the power-up reset leads to resetting the RTC timer. The frequency deviation depends on an RTC Clock Source and affects accuracy only in sleep modes, in which case the time will be measured at 6.6667 us resolution.

公式のWikiにも書いてあるとおり、スリープ中も使えるクロックです。
deepsleepの復帰をタイマーで設定するAPIなんかはこのクロックを使っています。
ただ、このクロックは、標準では内蔵RCオシレーターを使うようになっており、
実測したところ30分でも数秒以上の誤差が出ることもありました。
何時何分のデータかタイムスタンプが重要なアプリケーションや、
正確な起動時間が求められるタイマーとしてはこのままでは使うことができません。

そこで、DS3231のようなI2C RTCを接続し、スリープの復帰をGPIO割り込みにして、
RTCのINTピンを使って起こすなんてことをよくしていると思います。
しかし、実はESP32内蔵のRTC、よくRTCに使われる32.786kHzのクロックを
外部から供給して、外付けRTC並の高精度な時刻保持ができるのです。
上記リンクを少し下へスクロールすると、 RTC Clock Sourceという項目があります。
そこには
・Internal 150kHz RC oscillator (default)
・External 32kHz crystal
・External 32kHz oscillator at 32K_XN pin
・Internal 8.5MHz oscillator, divided by 256 (~33kHz)
という利用できるクロックソースのリストがあります。
それぞれ、
標準で使用されるRCオシレーター、
外付け32k水晶
XNピンへのクロック入力
内蔵8.5MHzオシレーターの分周
となっていますが、標準のRCはご存知のようにかなり精度が悪いです。
また、8.5MHzから分周するのも、周波数安定度は高いものの、
周波数が近似値なので均一にかなりずれます。絶対時間での誤差ではRCのほうがマシでした。
そこで、32kHzの水晶を外部に接続する方法を試してみました。


ひどい話ですね。これまで流通していたESP-WROOM-32では
うまくRTC用クロックソースの水晶を発振できませんでした。
そんな矢先、秋月でもチップバージョン3のWROOM-32Eが発売されていたので
本当にそちらでは動くのか試してみました。

水晶のドライブに必要な回路はデータシートの11-12ページに書いてあります。


GPIO32, 33に並列に抵抗を入れて、あとは水晶と負荷容量をつなぐだけです。
並列の抵抗は5M~10MΩとされていたので、手持ちのある5.1Mを使いました。


なお、ハードウェア的に接続するだけで自動的に水晶が使われるわけではなく、
ビルドオプションでクロックソースを別途設定する必要があります。


idf.py menuconfigで
Component config->ESP32-specific
RTC clock sourceでEnterを押して
External 32kHz crystalを選択
Number of attempts to repeat 32k XTAL calibrationでEnterを押して、
適当に数値を増やす
(これをしないと、発振の開始が間に合わずRCに切り替わってしまいます。)
なお、この数値を莫大な数値にすると、
発振できない場合にブートローダー内でWDT Resetが掛かりブートループになります。

うまく発振できないと、ブートローダーでこのようにエラーが出て、RCに切り替わります。


プローブを当てて、発振できているのにこの表示になる場合は、
Number of attempts to repeat 32k XTAL calibrationの数値を増やすといいと思います。


うまく発振できると、App cpu upの周りでエラーが表示されなくなります。
このように、ESP-WROOM-32Eでは外付けの水晶を使ったRTCクロックソースを
使用することができました。

この状態で実際に仕事で使ってみていますが、半日時計合わせをしなくても
1秒以内の誤差になっているため、
外付けRTCなしでも絶対時刻が重要なアプリケーションで使用できています。

ちなみに、秋月で売っているESP-WROOM-32Eは、技適マークが刻印されておらず、
シールが添付されていますが、3月のロットからは技適マークが入るようになっています。

Changes of the marking on ESP32-WROVER-E and ESP32-WROVER-IE to add new certification identification

実際にDigiKeyで3月に購入したものについては、技適マークが入っていました。

4 件のコメント:

  1. はい。 私もI2CでRTCモジュールを接続しているんです。どうやったら、電源が落とされてもESP32のRTCを動作させ続け、時刻を刻み続けることができるのかわからなくて悩んでいます。 多分電源ラインにバッテリーを接続して、電源が落ちたら即座にDeep Sleepへ移行させるのかなと思っているのですが、これは正しいでしょうか?回答が頂けると幸いです。

    返信削除
  2. 電源を落としてもなお時刻を保持させるには外部のRTCを使うしかないと思います。
    Deepsleepに落としている間は保持されます。
    一度電源を落としてからDeepsleepに入れるというのがどういう状況かわかりませんが、一度3.3Vの供給を止めるか、ENを落としてしまうと時刻もリセットされます。

    返信削除
    返信
    1. 早速のコメントありがとうございます!! そうですか時刻がリセットされてしまうのですか。であれば、ボタン電池付きの外部RTCを使うしかないですね。
      電源を落としてからDeepSleepというのは、電源が落とされたことをセンスして、数ms以内にDeepsleepへ移行して時刻を刻み続けることができるのかなと考えていました。DeepSleep時はスーパーキャパシタか何かを使って、ESP32へ電源を供給し続けるなどの回路を形成するなどの工夫をします。 でもリセットされるとカウントもクリアされるのであればそもそもだめですね。 ありがとうございました。 素直に外付けRTCを使います。

      削除
  3. WiFiが使える環境なら電源投入時にNTP参照するとか、まあタイマー起動はできませんが、、、

    返信削除