RTCを使うと電源を切っても都度の時刻合わせが不要となります。バックアップ電源により時間を刻み続けてくれるためです。動作したときの時刻を記憶しておきたい場合や、定刻で動作をさせたい時に使います。
RTCの動作
RTCは電源オフしてもバックアップ電源により時間を刻み続けます。M5Stack CORE2にはRTC回路が実装されています。回路はBM8563というRTC ICとRTC用電源、クリスタルにより構成されています。公式サイトによると2023年以降の出荷品はボタン電池が廃止されメイン電源のリチウム電池と共用されているようです。
M5StackのマイコンであるESP32自体にもRTCの機能はありますが、こちらはバックアップ電源を使わずにDeep Sleep Modeという極低消費電力で動作するモードで使われるようです。
BM8563はMCUであるESP32とI2Cで通信しています。PORT.Aに出ている外部I2Cではなく内部I2Cになっています。BM8563に年、月、日、時、秒をセットすればその後は電源オフしても自ら時間を刻み続け、現在時刻を読み出すことができるようになります。M5Stack公式サイトより回路は下記の通りです。RTC_VDDは電源管理IC AXP129から来ています。
引用元:M5Stack公式サイト https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/644/CORE2_V1.0_SCH.pdf
RTC IC BM8563の仕様
M5Stackの公式サイトにはBM8563のデータシートのリンクが貼ってありますが中国語版です。8563というRTCにはこの中国製のものやセイコーエプソンのRTC-8563、NXPのPCF8563があります。動作仕様を知りたいだけなので互換品であるPCF8563のデータシートを参考にしました。
8563の機能としては時計の他にアラームとタイマがあります。アラームは設定した時間にマイコンを起動、タイマは一定時間で時刻をキャリブレーションする用途などが考えられます。
レジスタ構成は以下のようになっています。年、月、曜日、日、時、秒が02hから08hまでのレジスタアドレスに格納されています。曜日は0〜6の値で表現し0が日曜日です。それ以外は全てBCD(Binaly Coded Decimal)表現になっています。
各レジスタの値はI2C通信のプロトコルに従って読み書きが可能です。上位7bitのI2Cスレーブアドレスは51h、下位1bitのR/WビットはReadは1、Writeは0です。
レジスタデータのWriteは、スレーブアドレスに引き続き、Writeしたいレジスタアドレスを設定します。その次に書き込む値を送ります。その後は設定したレジスタアドレスから自動でインクリメントされるため、順番に各レジスタに書き込む値を送ります。STOP conditionを送信するまでレジスタアドレスは自動インクリメントされます。
Readはレジスタアドレス設定が無く、00hから順番にSTOP conditionを送信するまでアドレスが自動インクリメントされて読み出されるものと思います。
レジスタアクセス中は誤作動防止のため時刻がストップする仕様になっているため、データシートには1秒以内にアクセスを完了させるよう記載されています。つまり秒から年まで1回のシーケンスでまとめて読み出すよう推奨されています。2秒停止していると電源遮断された時のような通信エラーと同様に認識しウォッチドッグが作動しリセットされタイマ再始動し誤差の拡大を防止する仕様になっています。
引用元:https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf
RTC動作確認プログラム
RTCは電源OFFしても暦時刻を失わないようにするためのものです。電源OFFしても連続的に時間を刻み続けているか確認するプログラムを作成しました。
方法は以下の通りです。システムの現在時刻を読み出して電源OFFし、数分後に電源ONして再度システムの現在時刻を読み込みます。再ONした時に前回電源OFFした時刻+OFFしていた時間が加算されていれば電源OFF中にも時間を刻み続けていたことが分かります。
プログラムは下記の手順で実行されます。
- 時間経過が分かりやすいように起動時のみ1回だけRTCにダミー時刻(2021/01/01 00:00:00)を書き込んでおく。
- 500ms毎にRTCの現在時刻とシステムの現在時刻をLCD表示する
ArduinoIDEのサンプルスケッチ「Rtc.ino」を参考にしています。
#include <M5Unified.h>
void setup() {
auto cfg = M5.config();
M5.begin(cfg);
M5.Display.setTextSize(2);
M5.Display.print("--- my_RTC_write ---\n\n");
//ダミーの時刻を書き込み
// YYYY MM DD hh mm ss
M5.Rtc.setDateTime( { { 2021, 1, 1 }, { 0, 0, 0 } } );
}
void loop() {
M5.Display.setTextSize(1);
M5.delay(500);
static constexpr const char* const wd[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"};
//RTCの現在時刻(UTC)を表示
auto dt = M5.Rtc.getDateTime();
M5.Display.setCursor(0,30);
M5.Display.printf("RTC UTC :%04d/%02d/%02d (%s) %02d:%02d:%02d\r\n"
, dt.date.year
, dt.date.month
, dt.date.date
, wd[dt.date.weekDay]
, dt.time.hours
, dt.time.minutes
, dt.time.seconds
);
//ESP32システム時刻(UTC)を表示
auto t = time(nullptr); //ESP32システム時刻(エポック秒)を取得
{
auto tm = gmtime(&t); //暦時刻に変換
M5.Display.setCursor(0,50);
M5.Display.printf("ESP32 UTC :%04d/%02d/%02d (%s) %02d:%02d:%02d\r\n",
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
wd[tm->tm_wday],
tm->tm_hour, tm->tm_min, tm->tm_sec);
}
}実行結果
電源ONの1回目が写真1、その後電源OFFし数分間待って2回目の電源ONが写真2です。電源OFF中も時間を刻み続けていることが分かります。
RTCの現在時刻(RTC UTC)は、「RTCに書き込んだ時刻(2021/01/01 00:00:00)を起点とした現在時刻」を示しています。システムの現在時刻(ESP32 UTC)は「前回RTCに書き込んだ時刻を起点とした現在時刻」を示しています。
写真2のシステム現在時刻5分39秒というのは写真1のRTCの現在時刻1分56秒に電源OFFしていた数分と写真2のRTC現在時刻0分58秒つまり電源OFFしていた時間を加算した値となっています。つまりシステムの現在時刻はRTCにより電源OFFしている時間も反映されており、電源OFFしても時間を刻み続けていることが分かります。
RTCに時刻を書き込んでもシステム時刻が即座に上書きされるわけではなく、次の電源ON時にRTCが刻んでいた時刻を読みに行ってシステム時刻に設定されています。


まとめ
M5Stack CORE2にはBM8563を使用したRTC回路が内蔵されています。RTCにいったん現在時刻を書き込んでおけば、電源OFFしても時刻を刻み続け、システム時刻に反映することが可能になります。今回はRTCの動作確認用に適当な時刻を書き込みましたが、NTPサーバーより現在時刻を取得してRTCに書き込めば電源オフしても時刻を再設定する必要なくカレンダー時計の機能が実現できると思います。



