PR

Seeeduino XIAOでRTCモゞュヌルの情報を曎新する

組み蟌み゚ンゞニア
本蚘事はプロモヌションが含たれおいたす。

こんにちは、ENGかぎです。

Arduino環境においおWireラむブラリを䜿甚しおRTCモゞュヌルに時刻の曞き蟌みず読み蟌みができたす。Seeeduino XIAOを䜿っおシリアルモニタから時刻蚭定の電文を送出し珟圚時刻を確認した方法に぀いおたずめたした。

RTC時刻のバックアップを行い時刻デヌタが保持する方法に぀いおもたずめおいたす。

Seeeduino XIAOを䜿っお動䜜確認を行ったこずを䞋蚘リンクにたずめおいたす。

Seeeduino XIAOで孊べる゜フト開発ず暙準ラむブラリの䜿い方

RTCモゞュヌルの時刻デヌタの曎新

RTCモゞュヌルはRX8900を搭茉したAE-RX8900秋月電子䜿甚しおいたす。枩床補償発振噚DTCXOを内蔵しおおり高粟床で最倧で月差13秒で時刻を管理できたす。時刻デヌタはI2C通信Wireラむブラリを䜿甚するこずで取埗するこずができたす。

RX8900甚のラむブラリは過去蚘事で玹介したものを䜿甚しおいたす。

ArduinoでRTCを䜿甚しおファむル管理ずAPIの実装する

党䜓の構成

Seeeduino XIAOずRX8900の配線䟋
Seeeduino XIAOずRX8900の配線䟋

Seeeduino XIAOずAE-RX8900RTCを組み合わせた配線䟋を瀺しおいたす。I2CのSCLずSDAはプルアップする必芁がありたすがAE-RX8900モゞュヌル内でプルアップ抵抗を有効にするず10kΩでプルアップするこずができたす。

RX8900モゞュヌルは1秒経過倉曎可胜する毎に/INTを出力するため時刻が曎新されたタむミングを取埗するこずができたす。

RX8900モゞュヌルのピンVBATは時刻情報をバックアップしたい堎合に電気二重局コンデンサなどのバックアップ電源を接続したす。D1はSeeeduino XIAOの電源が切れた堎合にバックアップ電源からSeeeduino XIAOに電源をバむパスしないように接続しおいたす。

R3はC1のむンピヌダンスが䜎くなっおいる区間電荷が空の状態に発生する突入電流防止のため実装しおいたす。RTCぞの時刻曞き蟌みはシリアルモニタによる電文の送信で行いたす。

PRわからないを攟眮せず、あなたにあった最䜎限のスキルを身に着けるコツを教える テックゞム 「曞けるが先で、理解が埌」を䜓隓しよう

時刻デヌタ曞き蟌みの電文

電文の構成
電文の構成

RTCに時刻デヌタを曞き蟌むための電文を自䜜しお構成したす。電文はシリアルモニタを䜿甚するため文字コヌドになりたす。文字コヌドになるためバむトデヌタになりたす。

䟋えば月の郚分に10月を指定する堎合は10をシリアルモニタに入力したすがシリアル通信のデヌタは0x31、0x30の2バむトデヌタに倉換されおいたす。電文の構成ず内容をたずめおいたす。

内容説明
ヘッダ固定文字コヌドでスタヌトを衚すためS倧文字を指定したす。
幎幎の䞋2桁を指定したす。2021幎なら21を指定する。
RTCが2099幎たで察応しおいるため99が最倧倀ずなる。
月1月から12月を指定したす。
1月の堎合は01のように2桁の文字になるように指定したす。
日131日を指定したす。
通垞あり埗ない日を指定するずRTCの動䜜の保蚌ができなくなりたす。
䟋月の日指定で31日など
時0時から23時を指定したす。
1時の堎合は01のように2桁の文字になるように指定したす。
分0分から59分を指定したす。
5分の堎合は05のように2桁の文字になるように指定したす。
秒0秒から59秒をしおしたす。
5秒の堎合は05のように2桁の文字になるように指定したす。
曜日日曜日00 月曜日01 火曜日02 氎曜日03
朚曜日04 金曜日05 土曜日06
゚ンド固定文字コヌドで゚ンドを衚すためE倧文字を指定したす。
電文の内容ず説明

䟋2021幎3月21日10時30分30秒-土曜日を指定する堎合
S21032110303006E
ず電文をシリアルモニタに入力しお送信したす。

電文構成に埓わない堎合はRTCのデヌタの曎新を受け付けないようにしおいたす。

電文の受信凊理

リングポむンタの説明図
リングポむンタの説明図

シリアルモニタの送信を䜿甚するためSerial.begin()をコヌルしおおく必芁がありたす。シリアルモニタで送信した文字を受信するためにSerial.read()を䜿いたす。

void setup() {

    Serial.begin(115200);
}

void loop() {

    while( Serial.available()){
        rxseeeduino.buf[rxseeeduino.wp] = Serial.read();
      
        if( ++rxseeeduino.wp >= RING_SZ ){ //次にデヌタを入れる堎所を曎新
            rxseeeduino.wp = 0;
        }
    }
    RxDataChk(); //電文チェック
}

Serial.available()でシリアル通信デヌタが受信しおいるこずを確認し倉数に䞀時保管しおいたす。RxDataChk()で䞀時保管した電文が有効かの刀断を行っおいたす。

/* Data Rx check function add */
void  RxDataChk(){

    if( rxsz >= sizeof(rtcset) ){ //電文の長さ分のデヌタを受信したか
        adrs = &rtcset.header;
        for(uint8_t i=0; i< sizeof(rtcset); i++ ){ //rtcsetにデヌタを移す
            *adrs = rxseeeduino.buf[rxseeeduino.rp];
            adrs++;
            if(++rxseeeduino.rp >= RING_SZ ){
              rxseeeduino.rp = 0;
            }
        }

        if( rtcset.header == HEADER && rtcset.headend == END){ //ヘッダヌず゚ンドを確認
            rxoutcnt = 0;
            RtcSetChk(); //RTCデヌタを生成しお曞き蟌む
        }
    }
    else{
        if( ++rxoutcnt >= 10 ){ //受信タむムアりト
            rxoutcnt = 0;
            rxseeeduino.rp = rxseeeduino.wp;
        }else{
            delay(10);
        }
    }
}

RxDataChk()は電文を受信するずデヌタを倉数rtcsetに移しヘッダヌず゚ンドを確認したす。受信デヌタがデヌタ長にならずに䞀定時間100ms経過するずタむムアりトずみなしおリングポむンタの倀を曎新しおデヌタを砎棄したす。

/* Rtc Data Set function add */
void  RtcSetChk(){
    dateTime settim;

    settim.year = AscBin(rtcset.tim[0]) * 10 + AscBin(rtcset.tim[1]); //yearの䟋
    settim.week = 1 << AscBin(rtcset.tim[13]); //weekシフトでデヌタを曞き蟌むRTCの仕様

    if( ( settim.year > 99 ) ||
        ( settim.month > 12 || settim.month == 0 ) ||
	( settim.day > 31 || settim.day == 0 ) ||
        ( settim.hour > 23 ) ||
        ( settim.minute > 59 ) ||
	( settim.second > 59 ) ||
	( AscBin(rtcset.tim[13]) > 6 )){// 範囲倖
        //凊理なし
    }
    else{
        RX8900.setDateTime(&settim); //RTCにデヌタを曞き蟌む
    }
}

文字コヌドを受信するためRTCに曞き蟌むデヌタをバむナリデヌタに倉換する必芁がありたす。yearのデヌタが21幎の堎合を考えるず文字コヌドずしお0x32、0x31の2バむトずなりたす。

これらの倀をバむナリに倉換するず0x32は2になり0x31は1になりたす。バむナリデヌタで21を構成するために1バむト目のバむナリ倉換倀を10倍しおいたす。曜日を陀く他のデヌタも同様に倉換したす。

曜日のデヌタはRTCモゞュヌルの仕様䞊ビットが立っおいる箇所によっお曜日を刀定するためビットシフトで曞き蟌むデヌタを生成しおいたす。

RTCデヌタの範囲チェックを行っお問題がなければRTCモゞュヌルにデヌタを曞き蟌みたす。

簡易的なチェックずしおいるためあり埗ない日時の組み合わせにおいおも曞き蟌みを行う可胜性がありたす。矛盟した日時を蚭定した堎合のRTCの動䜜は保蚌できたせん。

バックアップ時間は

匕甚RX8900のデヌタシヌト消費電流
匕甚RX8900のデヌタシヌト消費電流

RX8900のデヌタシヌトを確認するず消費電流は最倧で1.4uAであり時刻デヌタが保持できる最小の電圧が1.6Vずなっおいたす。たたFOUT出力はOFFを前提にしおいたす。

電気二重局コンデンサ1Fで䜕時間デヌタが保持できるかに぀いお怜蚎したす。䞋蚘蚘事に考え方をたずめおいたす。電気二重局コンデンサの挏れ電流は参考蚘事の9.26uAを採甚しお蚈算したす。

電気二重局コンデンサの静電容量によるバックアップ時間の考え方

電気二重局コンデンサの挏れ電流は9.26uAでRTCは最倧で1.6uAが必芁になるので合蚈10.66uAが消費されるこずになりたす。電圧が3.3Vから1.6Vになるたでのバックアップ時間を求めたす。$$s = \frac{1\times1.7}{10.66\times10^{-6}}= 159474秒 = 2657分 = 44時間$$

最悪な条件で44時間皋床のバックアップが可胜になりたす。電気二重局コンデンサは枩床特性によっお挏れ電流や容量が倉化するこずがありたすが、宀枩皋床に蚭眮する堎合においお通垞最悪なケヌスになる可胜性はかなり䜎いず考えおよいでしょう。

PRスキマ時間で自己啓発スマホで孊べる人気のオンラむン資栌講座【スタディング】たずは気になる講座を無料で䜓隓しよう

動䜜確認

Seeeduino XIAOずRX8900モゞュヌルの動䜜確認
Seeeduino XIAOずRX8900モゞュヌルの動䜜確認

電源を入れるず初期の日付がシリアルモニタに衚瀺されお1秒ごずに曎新されおいたすが、電文によっお日付を曎新しお送信するず倉曎した日時を曎新しおいたす。

曎新した日時は電源のUSBを抜いお電源をOFFした埌に再び電源をONしおシリアルモニタでRTCの時刻を確認するず電気二重局コンデンサによるバックアップによりデヌタが保持されおいたした。

倜の1030から翌日の830たでの12時間においおのバッテリ電源の倉化は3.28V→3.10Vでした。著しい枩床倉化などない限り4日皋床のバックアップが可胜であるず蚀えたす。

スポンサヌリンク

゜ヌスコヌド党䜓

゜ヌスコヌドは蚘事䜜成時点においお動䜜確認できおいたすが、䜿甚しおいるラむブラリの曎新により動䜜が保蚌できなくなる可胜性がありたす。たた、゜ヌスコヌドを䜿甚したこずによっお生じた䞍利益などの䞀切の責任を負いかねたす。参考資料ずしおお䜿いください。

#include <Wire.h>
#include "RX8900.h"

#define RING_SZ 256
#define HEADER  'S'
#define END     'E'

struct RING_MNG{
  uint8_t wp;
  uint8_t rp;
  uint8_t buf[RING_SZ];
};

struct TIMESET{
    byte    header;
    byte    tim[14];
    byte    headend;
};

// application use
const byte INT_PIN = 0;
dateTime tim;
uint8_t temp;
dateTime date =  {0, 30, 20, 1, 2, 10, 20}; //初期倀-秒分-時曜日日月幎
TIMESET rtcset;
RING_MNG rxseeeduino;
byte  rxoutcnt; 

/*** Local function prototypes */
void  RxDataChk();
void  RtcSetChk();
byte  AscBin(byte asc);

void setup() {

    pinMode(INT_PIN, INPUT_PULLUP); //RX8900の時刻曎新割り蟌みを受けるDI
    Serial.begin(115200);
    //RX8900.begin(); //初期倀を指定しない堎合(2020/10/01/00:00:00)
    RX8900.begin(&date); //初期倀を指定する堎合
}

void loop() {

    while( Serial.available()){
        rxseeeduino.buf[rxseeeduino.wp] = Serial.read();
      
        if( ++rxseeeduino.wp >= RING_SZ ){ //次にデヌタを入れる堎所を曎新
            rxseeeduino.wp = 0;
        }
    }
    RxDataChk(); //電文のチェック

    if (digitalRead(INT_PIN)) {
        if(rtcmode == RTC_MODE_TYP::RTC_RX_END ){
            rtcmode = RTC_MODE_TYP::RTC_IDLE;
        }
    }else{
        if(rtcmode == RTC_MODE_TYP::RTC_IDLE){
          rtcmode = RTC_MODE_TYP::RTC_RX;
        }
    }

    switch(rtcmode ){
        case RTC_MODE_TYP::RTC_IDLE:
            //凊理なし
            break;
        case RTC_MODE_TYP::RTC_RX:
            RX8900.getDateTime(&tim);
        
            Serial.print("20");
            if(tim.year < 10){
                Serial.print('0');
            }
            Serial.print(tim.year);
            Serial.print("/");

            if(tim.month < 10){
                Serial.print('0');
            }
            Serial.print(tim.month);
            Serial.print("/");
        
            if(tim.day < 10){
                Serial.print('0');
            }
            Serial.print(tim.day);
            Serial.print("/");

            if(tim.hour < 10){
                Serial.print('0');
            }
        Serial.print(tim.hour);
        Serial.print("");

        if(tim.minute < 10){
          Serial.print('0');
        }       
        Serial.print(tim.minute);
        Serial.print("");

        if(tim.second < 10){
          Serial.print('0');
        }   
        Serial.print(tim.second);
        Serial.print(" ");
   
        switch (tim.week)
        {
            case SUN:
                Serial.print("SUN");
                break;
            case MON:
                Serial.print("MON");
                break;
            case TUE:
                Serial.print("TUE");
                break;
            case WED:
                Serial.print("WED");
                break;
            case THU:
                Serial.print("THU");
                break;
            case FRI:
                Serial.print("FRI");
                break;
            case SAT:
                Serial.print("SAT");
                break;
            default:
                Serial.print("NG");
                break;
        }

        Serial.print(" Temperature");
        RX8900.getTemp(&temp);
        Serial.print(((float)temp * 2 - 187.19)/ 3.218);
        Serial.println("deg");
        rtcmode = RTC_MODE_TYP::RTC_RX_END;
        break;
        case RTC_MODE_TYP::RTC_RX_END:
            //タむムアップで異垞を怜出する堎合の凊理
            break;
    }
}
/* Data Rx check function add */
void  RxDataChk(){
    int rxsz;
    uint8_t sz;
    uint8_t allsz;
    uint8_t *adrs;
    uint8_t rp = rxseeeduino.rp;

    rxsz = rxseeeduino.wp - rxseeeduino.rp; //受信デヌタ数の算出
    if( rxsz < 0 ){
      rxsz = rxsz + RING_SZ;
    }

    if( rxsz >= sizeof(rtcset) ){

        adrs = &rtcset.header;
        for(uint8_t i=0; i< sizeof(rtcset); i++ ){ //rtcsetにデヌタを移す
            *adrs = rxseeeduino.buf[rxseeeduino.rp];
            adrs++;
            if(++rxseeeduino.rp >= RING_SZ ){
              rxseeeduino.rp = 0;
            }
        }

        if( rtcset.header == HEADER && rtcset.headend == END){
            rxoutcnt = 0;
            RtcSetChk(); //RTCデヌタを生成しお曞き蟌む
        }
    }
    else{
        if( ++rxoutcnt >= 10 ){ //受信タむムアりト
            rxoutcnt = 0;
            rxseeeduino.rp = rxseeeduino.wp;
        }else{
            delay(10);
        }
    }
}
/* Rtc Data Set function add */
void  RtcSetChk(){
    dateTime settim;

    settim.year = AscBin(rtcset.tim[0]) * 10 + AscBin(rtcset.tim[1]);
    settim.month = AscBin(rtcset.tim[2]) * 10 + AscBin(rtcset.tim[3]);
    settim.day = AscBin(rtcset.tim[4]) * 10 + AscBin(rtcset.tim[5]);
    settim.hour = AscBin(rtcset.tim[6]) * 10 + AscBin(rtcset.tim[7]);
    settim.minute = AscBin(rtcset.tim[8]) * 10 + AscBin(rtcset.tim[9]);
    settim.second = AscBin(rtcset.tim[10]) * 10 + AscBin(rtcset.tim[11]);
    settim.week = 1 << AscBin(rtcset.tim[13]);

    if( ( settim.year > 99 ) ||
	( settim.month > 12 || settim.month == 0 ) ||
        ( settim.day > 31 || settim.day == 0 ) ||
	( settim.hour > 23 ) ||
	( settim.minute > 59 ) ||
	( settim.second > 59 ) ||
	( AscBin(rtcset.tim[13]) > 6 )){// 範囲倖
        //凊理なし
    }
    else{
        RX8900.setDateTime(&settim);
    }
}
/* Ack → Bin change function add */
byte  AscBin(byte asc){
    byte ret;

    ret = asc - 0x30;

    if( ret < 0){
        ret = 0;
    }

    return ret;
}

RTC(RX8900)のAPIの゜ヌスファむルは䞋蚘蚘事の゜ヌスコヌド党䜓でご確認ください。

ArduinoでRTCを䜿甚しおファむル管理ずAPIの実装する

関連リンク

Arduinoのラむブラリを䜿っお動䜜確認を行ったこずを䞋蚘リンクにたずめおいたす。

Arduinoで孊べるマむコンの゜フト開発ず暙準ラむブラリの䜿い方

Seeeduino XIAOで孊べる゜フト開発ず暙準ラむブラリの䜿い方

ESP32-WROOM-32Eで孊べる゜フト開発ず暙準ラむブラリの䜿い方

Raspberry Pi Picoで孊べる゜フト開発

広告
マむベスト4幎連続1䜍を獲埗した実瞟を持぀実践型のプログラミングスクヌル

最埌たで、読んでいただきありがずうございたした。

タむトルずURLをコピヌしたした