ArduinoでRTCモジュールの情報をLCDを使って更新する

組み込みエンジニア

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

Arduino環境においてWireライブラリを使用してRTCモジュールに時刻の書き込みと読み込みができます。LCDとボタン(DI)を使って任意のデータをRTCモジュールに書き込みを行ってLCDに現在時刻を表示するようにします。過去記事のRTCを使用した例の応用編になります。

ArduinoでRTCを使用してファイル管理とAPIの実装する

ボタンを押して変数を変更しながらRTCモジュールに時刻を書き込みますが、モードを切り替えて書き込むようにしています。この方法は他のモジュールにデータをセットする場合にも使えるテクニックです。

Arduino UNOを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

RTCモジュールの情報をLCDを使って更新する

RTCモジュールはRX8900を搭載したAE-RX8900(秋月電子)使用しています。温度補償発振器(DTCXO)を内蔵しており高精度で最大で月差13秒で時刻を管理できます。時刻データはI2C通信(Wireライブラリ)を使用することで取得することができます。

LCDは「LiquidCrystal.h」ライブラリを使用することで文字を表示することができます。使い方については下記を参考にしてください。

Arduinoの標準ライブラリでLCDに文字を表示する方法

全体の構成

全体の構成図(Arduino+RTCモジュール+LCD)
全体の構成図(Arduino+RTCモジュール+LCD)

ArduinoとAE-RX8900(RTC)とLCDを組み合わせた配線例を示しています。I2CのSCLとSDAはプルアップする必要がありますがAE-RX8900モジュール内でプルアップ抵抗を有効にすると10kΩでプルアップすることができます。

SW1はRTCの時刻を書き換えるモードに遷移するボタンとして使用します。SW2は時刻設定するときのカウントをプラスする際に使用します。SW3はカウントをマイナスする際に使用します。

スタートすると時刻表示モード(通常モード)でスタートしSW1を長押し(2秒)すると時刻設定モードに遷移するようにします。時刻設定モードでは年→月→日→曜日→時→分→秒の順で設定できるようにします。

LCDで表示する項目

LCDに表示する項目
LCDに表示する項目

LCDに表示するモードについて説明します。

  1. 現在時刻表示と温度
  2. 設定(年、月、日、曜日)
  3. 設定(時、分、秒)

1については1段目に2020/10/04 SUNのように年・月・日・曜日を表示します。2段目には20:30:00 22.63のように時・分・秒・温度を表示します。

2については設定モードに遷移したときに表示します。1段目にSET DATEを表示します。2段目に2020/のみを最初に表示し年の設定後モードを進めると2020/10/のように月まで表示します。月の設定後モードを進めると2020/10/04となり日の設定になります。モードを進めると曜日の設定になります。

3については2を設定後モードを進めると1段目にSET TIMEを表示します。2段目に20:と時を表示し設定後モードを進めると20:33:のように分まで表示します。分の設定後モードを進めると20:33:00となり秒の設定になります。

モードを進めると2に戻って年からの設定に戻りますが、モード遷移ボタンを長押し(2秒)すると時刻表示モード(1)に戻ります。

モード遷移による管理と時刻の設定

時刻表示モードでは1秒毎に時刻を更新しますが、言い換えると1秒ことにRTCの時刻データを読み込んでいると言えます。RTCの時刻設定は書き込みたい任意の時刻の値を準備しておきRTCにデータを送信することで行えます。

モード遷移を実装する

モード切替と各設定項目の遷移図
モード切替と各設定項目の遷移図

SW1とSW2、SW3の関係を示しています。SW1は長押しすると表示モードと設定モードの切り替えを行います。長押ししない場合は設定モードにおいて設定項目の選択に使用します。SW2は各設定項目においてカウントを+1する際に使用し、SW3はカウントを-1するときに使用します。

/*** モード切替処理  ***/
void ModeCng(){

    if( difilt.di1 == 0 ){ //SW1が押されたか
        if( timbtn1 == TIME_OFF ){
            timbtn1 = MODE_CNG; //2分の時限をセット
        }
    }else{
        timbtn1 = TIME_OFF;
        btn1hold = false;
    }

    if( timbtn1 == TIME_UP ){
        timbtn1 = TIME_OFF;
        if( mainmode == MD_DATE ){
            mainmode = MD_SET; //設定モードに遷移
            //LCD初期化処理など
            btn1hold = true; //SW1が押されたままかを判断するために使用
        }else if(mainmode == MD_SET){
            mainmode = MD_DATE; //表示モードに戻す
            RX8900.setDateTime(&setdata); //RTCに設定データを書き込み
        }
    }
}

SW1を長押しするとタイマをスタートし2秒経過したときモードを切り替える処理を行っています。

設定モードに遷移した後サブモードでSW1が押されたと誤判定しないためにSW1がOFFになってからサブモードの処理を行うためにbtn1holdに状態を保持して状態を判定できるようにしています。

表示モードと設定モードの切り替えと項目切り替えのボタンを区別している場合は特に意識する必要はありません。

RTCモジュールに時刻を設定する(サブモードの遷移)

RTCモジュールに時刻を設定するために変数を準備する必要があります。変数を任意の値にするためにSW2とSW3を押してカウントを更新していきます。設定項目をサブモードで管理しSW1が押されるまで同一のサブモードで変数の更新ができるようにします。

enum SET_MODE{
    SET_YEAR = 0,
    SET_MONTH,
    SET_DAY,
    SET_WEEK,
    SET_HOUR,
    SET_MINUTE,
    SET_SECOND,
    SET_MAX  
};

SET_MODE submode; //サブモードを管理する変数

設定する項目を番号順に並べてSET_MODEを定義し、submodeの変数をSET_MODEの型で宣言しています。SET_YEARで年の設定が完了しSW1を押すとSET_MONTHにサブモードを進めて月の設定を行います。SET_SECONDが終わるまで繰り返していきます。

/*** サブモード(時刻設定)処理  ***/
void SetSubMode(){
    char strbuf[2];

    if( btn1hold == false ){ //SW1のONが解除されたか
        switch(submode){
        case SET_YEAR:
            sprintf(strbuf,"%02d",SetDate(submode)); //文字を変換2桁の10進数
            //年のLCD表示
            if( difilt.di1 == 0 ){
                //SW1を押された時下の処理
                btn1hold = true;
                submode = SET_MONTH; //次のモードに遷移する
            }
            break;
        case SET_MONTH:
            sprintf(strbuf,"%02d",SetDate(submode)); //文字を変換2桁の10進数
            //月のLCD表示
            if( difilt.di1 == 0 ){
                btn1hold = true;
                submode = SET_DAY;
            }  
            break;
    /*--------省略------------*/ 
        case SET_SECOND:
            sprintf(strbuf,"%02d",SetDate(submode)); //文字を変換2桁の10進数
            //秒のLCD表示
            if( difilt.di1 == 0 ){
                btn1hold = true;
                submode = SET_YEAR; //先頭の年に戻る
                lcd.clear(); //表示を切り替えるためクリアする
            }   
            break;
        }
    }
}

各サブモードにおいて変数を変更しながらLCD表示します。SW1を押してモードを進めた時SW1が解除されたのを確認して次のサブモードの処理を行うためにbtn1holdでボタンを押された状態を保持しています。

SW1のONが解除されたのを確認してから内部のサブモードによる処理を行うようにすることで誤判定によってサブモードが進みすぎるのを防ぐことができます。

表示モードの切り替えとサブモードの切り替えを別のSWで管理している場合は特に意識する必要はありません。

時刻設定のカウントを管理する

SW2とSW3を押してカウント値を更新しながら年~秒の値を設定するため各サブモードに対応して値の上限と下限を決定するようにします。

 /***年の更新の例***/
   switch(date){
   case SET_YEAR:
        if( difilt.di2 == 0){//カウント+
            if( btn2hold ){
                btn2hold = false; //クリアするのでSW2が押されている間は処理しなくなる
                if( ++setdata.year > 99 ){
                    setdata.year = 0;
                }     
            }
        }else{
            btn2hold = true;
        }
     
        if( difilt.di3 == 0){//カウント-
            if( btn3hold ){
                btn3hold = false; //クリアするのでSW3が押されている間は処理しなくなる
                if( setdata.year == 0 ){
                    setdata.year = 99;
                }else{
                    --setdata.year;
                }    
            }
        }else{
            btn3hold = true;
        }
        /*うるう年の判定*/
        ret = setdata.year;
        break;
    }

SW2を押すとカウントアップするようにしています。マイコンは高速で処理しているため少しボタンを押しただけでも複数回押された判断をするためカウントが進んでしまいます。この対策のため一度ボタンを押すとONが解除されるまでカウントアップしないようにする必要があります。

SW2がOFFしたときbtn2holdをセットしておきONしたときにbtn2holdをクリアすることで一度だけカウントアップする処理を実施することができます。

SW3を押すとカウントダウンするようにしています。SW3もSW2と同様にbtn3holdを使ってSW3がONしたときに一度だけカウントダウンするようにしています。

年を設定するときにうるう年の計算をする必要があります。うるう年の定義は以下の通りです。

  1. 4で割り切れる
  2. 上記において100で割り切れる場合は平年とする
  3. 400で割り切れる場合はうるう年とする
ycnt = 2000 + setdata.year;
if( (ycnt % 4) == 0 ){ //4で割り切れる
    if( (ycnt % 400)== 0){ //400で割り切れる
        uruflg = true;
    }else if( ( ycnt % 100 )== 0 ){ //100で割り切れる(平年)
        uruflg = false;
    }else{
        uruflg = true;
    }
}else{
    uruflg = false;
}

4で割り切れるのを判定するために%(余りを求める)で計算を行い余りが0であれば割り切れています。4で割り切れると基本的にうるう年になりますが、100で割り切れる場合は平日とする条件や400で割り切れるときはうるう年とする条件があるので4で割り切れることを判定した後にこれら2つの条件によってうるう年の判断をしています。

 /***日の更新の例(日付の上限の考え方)**/
const uint8_t daymax[4]={28,29,30,31};
uint8_t dcnt; //日の上限値を設定する
        
switch(setdata.month){
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
    dcnt = daymax[3]; //31を上限とする
    break;
case 4:
case 6:
case 9:
case 11:
   dcnt = daymax[2]; //30を上限とする
   break;
case 2:
   if(uruflg ){ //うるう年
       dcnt = daymax[1]; //29を上限とする
   }else{ //平年
       dcnt = daymax[0]; //28を上限とする
   }
   break;
}

日付の上限に関する考え方の一例を示しています。2月は28日または29日があるためうるう年の判定を行って日の上限を決めます。1,3,5,7,8,10,12月は31日あり4,6,9,11月は30日なので変数dcntにこれらを格納してカウント値の上限として処理するようにします。

/***WEEKの設定例***/
//SW2による処理(一部省略)
if( ++wcnt > 6 ){ 
    wcnt = 0;
}
//SW3による処理(一部省略)
if( --wcnt < 0 ){
    wcnt = 6;
}   

setdata.week = ( 1 << wcnt); //曜日の値はシフト値で決まるため曜日が進む毎に左にシフト

WEEK(曜日)の設定のみ値の設定の仕方が異なります。SUN→MON→・・・→SATと進むにつれてビットをシフトさせて表現するようになっています。SW2、SW3でシフトする回数を更新し、値を確定する際にシフト演算して曜日を設定しています。

動作確認

ArduinoとRX8900の結果をLCDに表示する動作確認
ArduinoとRX8900の結果をLCDに表示する動作確認

電源を入れると初期画面が2秒表示されて時刻表示モードになります。電源を入れた場合はコールドスタートになるためRTCに初期値が書き込まれ、その値から時刻をカウントします。

SW1を2秒間長押しするとモード2に遷移し任意の日付を設定します。曜日の設定が完了すると次にモード3に遷移し任意の時刻を設定します。

設定モードから抜けると設定した各種データをRTCに書き込んでモード1に遷移し時刻を表示します。

時刻データをLCDで確認しながら設定することができるようになったことで温度計を搭載した時計を作ることができました。時刻の設定画面やモードの遷移のさせ方は様々な方法がありますが、ボタンを使った方法で今回の例は方法は有効だと思います。

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。チェックはしておりますがコメントなど細かな部分で間違っている可能性があります。参考としてお使いいただければと思います。

Arduinoのスケッチ:

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

#define TIME_UP 0
#define TIME_OFF -1
#define BASE_CNT 10 //10msがベースタイマとなる
#define MODE_CNG 200 //2sでモード切替

#define PIN_DI1 2
#define PIN_DI2 3
#define PIN_DI3 4
#define DI_FILT_MAX 4
#define FILT_MIN 1

#define LCD_RS 8
#define LCD_EN 9
#define LCD_D4 10
#define LCD_D5 11
#define LCD_D6 12
#define LCD_D7 13
#define COL_SZ 16

enum MODE_BTN{
    MD_DATE = 0,
    MD_SET,
    MD_MAX
};

enum SET_MODE{
    SET_YEAR = 0,
    SET_MONTH,
    SET_DAY,
    SET_WEEK,
    SET_HOUR,
    SET_MINUTE,
    SET_SECOND,
    SET_MAX  
};

enum DI_NO{
    DI1 = 0,
    DI2,
    DI3,
    DI_MAX
};

struct DIFILT_TYP{
    uint8_t wp;
    uint8_t buf[DI_MAX][DI_FILT_MAX];
    uint8_t di1;
    uint8_t di2;
    uint8_t di3;
};

const char disptbl[2][COL_SZ]={
    "SET DATE        ",
    "SET TIME        "
};

const uint8_t daymax[4]={28,29,30,31};

// application use
LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4,LCD_D5, LCD_D6, LCD_D7);
const byte INT_PIN = 7;
int16_t cnt10ms;
int16_t timdifilt = TIME_OFF;
int16_t timbtn1 = TIME_OFF;
DIFILT_TYP difilt;
dateTime tim;
uint8_t temp;
dateTime initdate =  {0, 30, 20, SUN, 4, 10, 20}; //初期値-秒-分-時-曜日-日-月-年
dateTime nowdata;
dateTime setdata;
MODE_BTN mainmode;
SET_MODE submode;
int16_t wcnt; //week計算用
bool uruflg;
bool btn1hold;
bool btn2hold;
bool btn3hold;

/*** Local function prototypes */
void TimerCnt();
void mainTimer();
void DiFilter();
void RtcMain();
void SetSubMode();
uint16_t SetDate( SET_MODE date);

void setup() {

    pinMode(INT_PIN, INPUT_PULLUP); //RX8900の時刻更新割り込みを受けるDI
    pinMode( PIN_DI1, INPUT_PULLUP );
    pinMode( PIN_DI2, INPUT_PULLUP );
    pinMode( PIN_DI3, INPUT_PULLUP );

    MsTimer2::set(1,TimerCnt); //1msごとに関数へ遷移
    MsTimer2::start();
    timdifilt = FILT_MIN;
    
    Serial.begin(9600);
    //RX8900.begin(); //初期値を指定しない場合(2020/10/04/00:00:00)
    RX8900.begin(&initdate); //初期値を指定する場合

    for( uint8_t i=0; i < 10; i++ ){
        mainTimer();
        DiFilter();
        delay(10);
    }
    
    lcd.begin(16,2); //16×2を表示領域
    lcd.print("RTC-TEST");
    lcd.setCursor(0, 1); //2段目の左端にカーソル
    lcd.print("Ver1.00         ");
  
    delay(2000); //初期表示を2秒間行う
    nowdata = initdate;
}

void loop() {

    mainTimer();
    DiFilter();
    RtcMain();
    ModeCng();
}
/*** RTCメイン処理  ***/
void RtcMain(){

    if( mainmode == MD_DATE ){
        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);
            nowdata = tim;
            lcd.setCursor(0, 0); //2段目の左端にカーソル
            lcd.print("20");
            if(tim.year < 10){
                lcd.print("0");
            }
            lcd.print(tim.year);
            lcd.print("/");
    
            if(tim.month < 10){
              lcd.print("0");
            }
            lcd.print(tim.month);
            lcd.print("/");
            
            if(tim.day < 10){
              lcd.print("0");
            }
            lcd.print(tim.day);
            
            switch(tim.week){
            case SUN:
                lcd.print(" SUN");
                break;
            case MON:
                lcd.print(" MON");
                break;
            case TUE:
                lcd.print(" TUE");
                break;
            case WED:
                lcd.print(" WED");
                break;
            case THU:
                lcd.print(" THU");
                break;
            case FRI:
                lcd.print(" FRI");
                break;
            case SAT:
                lcd.print(" SAT");
                break;
            }
            lcd.setCursor(0, 1); //2段目の左端にカーソル
            if(tim.hour < 10){
                lcd.print("0");
            }
            lcd.print(tim.hour);
            lcd.print(":");
    
            if(tim.minute < 10){
                lcd.print("0");
            }       
            lcd.print(tim.minute);
            lcd.print(":");
    
            if(tim.second < 10){
                lcd.print("0");
            } 
            lcd.print(tim.second);
            lcd.print("  "); 
            RX8900.getTemp(&temp);
            lcd.print(((float)temp * 2 - 187.19)/ 3.218);
            rtcmode = RTC_MODE_TYP::RTC_RX_END;
            break;
        case RTC_MODE_TYP::RTC_RX_END:
            //タイムアップで異常を検出する場合の処理
            break;
        }
    }else if( mainmode == MD_SET){
        SetSubMode();
    }
}
/*** モード切替処理  ***/
void ModeCng(){

    if( difilt.di1 == 0 ){
        if( timbtn1 == TIME_OFF ){
            timbtn1 = MODE_CNG;
        }
    }else{
        timbtn1 = TIME_OFF;
        btn1hold = false; 
    }

    if( timbtn1 == TIME_UP ){ //2秒経過したか
        timbtn1 = TIME_OFF;
        if( mainmode == MD_DATE ){
            mainmode = MD_SET;
            submode = SET_YEAR;
            setdata = nowdata;
            lcd.clear();
            lcd.setCursor(0, 0); //1段目の左端にカーソル
            lcd.print(disptbl[0]); //1段目の文字列
            btn1hold = true;
        }else if(mainmode == MD_SET){
            mainmode = MD_DATE;
            RX8900.setDateTime(&setdata);
        }
    }
}
/*** サブモード(時刻設定)処理  ***/
void SetSubMode(){
    char strbuf[2];

    if( btn1hold == false ){
        switch(submode){
        case SET_YEAR:
            lcd.setCursor(0, 0); //1段目の左端にカーソル
            lcd.print(disptbl[0]); //1段目の文字列
            lcd.setCursor(0, 1); //2段目の左端にカーソル
            lcd.print("20");
            sprintf(strbuf,"%02d",SetDate(submode)); //文字を変換2桁の10進数
            lcd.print(strbuf);
            lcd.print("/");
            if( difilt.di1 == 0 ){
                btn1hold = true;
                submode = SET_MONTH;
            }
            break;
        case SET_MONTH:
            lcd.setCursor(5, 1); 
            sprintf(strbuf,"%02d",SetDate(submode)); //文字を変換2桁の10進数
            lcd.print(strbuf);
            lcd.print("/");
            if( difilt.di1 == 0 ){
                btn1hold = true;
                submode = SET_DAY;
            }  
            break;
        case SET_DAY:
            lcd.setCursor(8, 1); 
            sprintf(strbuf,"%02d",SetDate(submode)); //文字を変換2桁の10進数
            lcd.print(strbuf);
            lcd.print(" ");
            if( difilt.di1 == 0 ){
                btn1hold = true;
                submode = SET_WEEK;
            }    
            break;
        case SET_WEEK:
            lcd.setCursor(11, 1); 
       
            switch(SetDate(submode)){
            case SUN:
                lcd.print(" SUN");
                break;
            case MON:
                lcd.print(" MON");
                break;
            case TUE:
                lcd.print(" TUE");
                break;
            case WED:
                lcd.print(" WED");
                break;
            case THU:
                lcd.print(" THU");
                break;
            case FRI:
                lcd.print(" FRI");
                break;
            case SAT:
                lcd.print(" SAT");
                break;
            }
        
            if( difilt.di1 == 0 ){
                btn1hold = true;
                submode = SET_HOUR;
                lcd.clear();
            }  
            break;
        case SET_HOUR:
            lcd.setCursor(0, 0); //1段目の左端にカーソル
            lcd.print(disptbl[1]); //1段目の文字列
            lcd.setCursor(0, 1); //2段目の左端にカーソル
            sprintf(strbuf,"%02d",SetDate(submode)); //文字を変換2桁の10進数
            lcd.print(strbuf);
            lcd.print(":");
            if( difilt.di1 == 0 ){
                btn1hold = true;
                submode = SET_MINUTE;
            }  
            break;
        case SET_MINUTE:
            lcd.setCursor(3, 1); 
            sprintf(strbuf,"%02d",SetDate(submode)); //文字を変換2桁の10進数
            lcd.print(strbuf);
            lcd.print(":");
            if( difilt.di1 == 0 ){
                btn1hold = true;
                submode = SET_SECOND;
            }   
            break;
        case SET_SECOND:
            lcd.setCursor(6, 1); 
            sprintf(strbuf,"%02d",SetDate(submode)); //文字を変換2桁の10進数
            lcd.print(strbuf);
            if( difilt.di1 == 0 ){
                btn1hold = true;
                submode = SET_YEAR;
                lcd.clear();
            }   
            break;
        }
    }
}
/*** 時刻設定カウント処理  ***/
uint16_t SetDate(SET_MODE date){
    uint16_t ret = 0;
    uint16_t ycnt;
    uint8_t dcnt;

    switch(date){
    case SET_YEAR:
        if( difilt.di2 == 0){//カウント+
            if( btn2hold ){
                btn2hold = false;
                if( ++setdata.year > 99 ){
                    setdata.year = 0;
                }     
            }
        }else{
            btn2hold = true;
        }
     
        if( difilt.di3 == 0){//カウント-
            if( btn3hold ){
                btn3hold = false;
                if( setdata.year == 0 ){
                    setdata.year = 99;
                }else{
                    --setdata.year;
                }    
            }
        }else{
            btn3hold = true;
        }
        ycnt = 2000 + setdata.year;
        if( (ycnt % 4) == 0 ){ //4で割り切れる
            if( (ycnt % 400)== 0){ //400で割り切れる
                uruflg = true;
            }else if( ( ycnt % 100 )== 0 ){ //100で割り切れる(平年)
                uruflg = false;
            }else{
                uruflg = true;
            }
        }else{
            uruflg = false;
        }
        ret = setdata.year;
        break;
    case SET_MONTH:
        if( difilt.di2 == 0){//カウント+
            if( btn2hold ){
                btn2hold = false;
                if( ++setdata.month > 12 ){
                    setdata.month = 1;
                }   
            }
        }else{
            btn2hold = true;
        }
     
        if( difilt.di3 == 0){//カウント-
            if( btn3hold ){
                btn3hold = false;
                if( --setdata.month == 0 ){
                    setdata.month = 12;
                }    
            }
        }else{
            btn3hold = true;
        }
        ret = setdata.month;
        break;
    case SET_DAY:
        switch(setdata.month){
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            dcnt = daymax[3];
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            dcnt = daymax[2];
            break;
        case 2:
            if(uruflg ){
                dcnt = daymax[1];
            }else{
                dcnt = daymax[0];
            }
            break;
        }

        if( difilt.di2 == 0){//カウント+
            if( btn2hold ){
                btn2hold = false;
                if( ++setdata.day > dcnt ){
                    setdata.day = 1;
                }  
            }
        }else{
            btn2hold = true;
        }
     
        if( difilt.di3 == 0){//カウント-
            if( btn3hold ){
                btn3hold = false;
                if( --setdata.day == 0 ){
                    setdata.day = dcnt;
                }   
            }
        }else{
            btn3hold = true;
        }
        ret = setdata.day;
        break;
    case SET_WEEK:
        if( difilt.di2 == 0){//カウント+
            if( btn2hold ){
                btn2hold = false;
                if( ++wcnt > 6 ){
                    wcnt = 0;
                }  
            }
        }else{
            btn2hold = true;
        }
     
        if( difilt.di3 == 0){//カウント-
            if( btn3hold ){
                btn3hold = false;
                if( --wcnt < 0 ){
                    wcnt = 6;
                }   
            }
        }else{
            btn3hold = true;
        }
     
        setdata.week = ( 1 << wcnt);
        ret = setdata.week;
        break;
    case SET_HOUR:
        if( difilt.di2 == 0){//カウント+
            if( btn2hold ){
                btn2hold = false;
                if( ++setdata.hour > 23 ){
                    setdata.hour = 0;
                }  
            }
        }else{
            btn2hold = true;
        }
     
        if( difilt.di3 == 0){//カウント-
            if( btn3hold ){
                btn3hold = false;
                if( setdata.hour == 0 ){
                    setdata.hour = 23;
                }else{
                    --setdata.hour;
                }  
            }
        }else{
            btn3hold = true;
        }
        ret = setdata.hour;
        break;
    case SET_MINUTE:
        if( difilt.di2 == 0){//カウント+
            if( btn2hold ){
                btn2hold = false;
                if( ++setdata.minute > 59 ){
                    setdata.minute = 0;
                }  
            }
        }else{
            btn2hold = true;
        }
     
        if( difilt.di3 == 0){//カウント-
            if( btn3hold ){
                btn3hold = false;
                if( setdata.minute == 0 ){
                    setdata.minute = 59;
                }else{
                    --setdata.minute;
                } 
            }
        }else{
            btn3hold = true;
        }  
        ret = setdata.minute;
        break;
    case SET_SECOND:
        if( difilt.di2 == 0){//カウント+
            if( btn2hold ){
                btn2hold = false;
                if( ++setdata.second > 59 ){
                    setdata.second = 0;
                } 
            }
        }else{
            btn2hold = true;
        }
     
        if( difilt.di3 == 0){//カウント-
            if( btn3hold ){
                btn3hold = false;
                if( setdata.second == 0 ){
                    setdata.second = 59;
                }else{
                    --setdata.second;
                }
            }
        }else{
            btn3hold = true;
        }
        ret = setdata.second;
        break;
    }
    return ret;
}
/* callback function add */
void TimerCnt(){
  ++cnt10ms;
}
/* Timer Management function add */
void mainTimer(){

    if( cnt10ms >= BASE_CNT ){ //10msごとにここに遷移する
        cnt10ms -=BASE_CNT;
    
        if( timdifilt > TIME_UP ){
            timdifilt--;
        }

        if( timbtn1 > TIME_UP ){
            timbtn1--;
        }
    }
}
/* DiFilter function add */
void DiFilter(){

    if( timdifilt == TIME_UP ){
        difilt.buf[DI1][difilt.wp] = digitalRead(PIN_DI1);
        difilt.buf[DI2][difilt.wp] = digitalRead(PIN_DI2);
        difilt.buf[DI3][difilt.wp] = digitalRead(PIN_DI3);
    
        if( difilt.buf[DI1][0] == difilt.buf[DI1][1] &&
            difilt.buf[DI1][1] == difilt.buf[DI1][2] &&
            difilt.buf[DI1][2] == difilt.buf[DI1][3] ){ //4回一致を確認
            difilt.di1 = difilt.buf[DI1][0];
        }
    
        if( difilt.buf[DI2][0] == difilt.buf[DI2][1] &&
            difilt.buf[DI2][1] == difilt.buf[DI2][2] &&
            difilt.buf[DI2][2] == difilt.buf[DI2][3] ){ //4回一致を確認
            difilt.di2 = difilt.buf[DI2][0];
        }
        
        if( difilt.buf[DI3][0] == difilt.buf[DI3][1] &&
            difilt.buf[DI3][1] == difilt.buf[DI3][2] &&
            difilt.buf[DI3][2] == difilt.buf[DI3][3] ){ //4回一致を確認
            difilt.di3 = difilt.buf[DI3][0];
        }

        if( ++difilt.wp >= DI_FILT_MAX ){
            difilt.wp = 0;
        }

        timdifilt = FILT_MIN;
    }
}

RTC(RX8900)のAPIのソースファイルは下記記事のソースコード全体でご確認ください。

ArduinoでRTCを使用してファイル管理とAPIの実装する

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

Code Camp完全オンラインのプログラミング個人レッスン【無料体験】

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました