●Arduinoでリアルタイムクロックを使う

DS3231というRTC(リアルタイムクロック)モジュールを使って時計らしきものを作成します。
EasyWordMall DC 3.3-5.5V DS3231 AT24C32 高精度リアルタイムクロックモジュール [並行輸入品]
DS1307であっても同じ構成と同じプログラムで動作します。


液晶の表示部分は、
Arduinoで1602 I2C液晶モジュールを使う
で作成した表示部分を流用します。


▼ハードウエアの接続

RTCのVccはArduinoの5Vに、GNDはGNDに接続します。
RTCのSDAはArduinoのA4
RTCのSDLはArduinoのA5に接続しました。

VccとGNDとA4、A5ピンは液晶と共有になります。
I2CリアルタイムクロックとI2C液晶モジュールはI2Cというシリアル通信で同じ線を使って通信するためにこのようになります。


▼ソフトウエアの作成

ライブラリの準備を行います。
RTClibという名前のライブラリですが、小文字のlibであることに注意です。
大文字の物は別のライブラリになります。
Arduinoの開発環境でツール→ライブラリの管理でライブラリマネージャを開き、検索をフィルタ…の所にRTClibと入力して探します。



インストールすることにより、RTClib.hが使えるようになります。


▼RTCの時間の設定

RTC.adjust(DateTime(年,月,日,時間,分,秒))

この文で時間を設定できます。
このプログラムを最初に走らせてRTCに時間を設定しました。

#include <RTClib.h>

void setup () {
    RTC_DS1307 RTC;
    RTC.begin();
    RTC.adjust(DateTime(2019, 11, 9, 18, 30, 0));
}
void loop () {
}

Arduinoに書き込んだ後に、目的の時間の2秒前にリセットボタンを押すと設定がうまく行きます。


▼RTCの時間の表示

プログラム中ではDS1307となっていますが、DS3231でも動作します。
このページの先頭の画像が動作状態です。

#include <RTClib.h>
#include <LiquidCrystal_I2C.h>

//アドレス0x27 16文字2行の液晶
LiquidCrystal_I2C lcd(0x27, 16, 2);
RTC_DS1307 RTC;

void setup () {
    RTC.begin();
    lcd.init();
    lcd.backlight();
}

void loop () {
    lcd.setCursor(0, 0);

    if (RTC.isrunning()) {
        DateTime now = RTC.now();
        lcd.print(now.year(), DEC);
        lcd.print('/');
        lcd.print(now.month(), DEC);
        lcd.print('/');
        lcd.print(now.day(), DEC);
        lcd.print("                ");
        
        lcd.setCursor(0, 1);
    
        lcd.print(now.hour(), DEC);
        lcd.print(':');
        lcd.print(now.minute(), DEC);
        lcd.print(':');
        lcd.print(now.second(), DEC);
        lcd.print("                ");
    }else{
        lcd.print("RTC not find    ");
    }
    delay(100);
}


■時刻設定のできる時計を作成する

時間の表示が出来ても設定が出来ないと悲しいので時間が設定できるデジタル時計風にしてみました。
時間を設定するスイッチは、デジタルピン2番とGND、3番とGNDに取り付けて2個のスイッチで時間設定するように作成しました。。

#include <stdio.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>

//アドレス0x27 16文字2行の液晶
LiquidCrystal_I2C lcd(0x27, 16, 2);
RTC_DS1307 RTC;

void setup () {
    pinMode(2, INPUT_PULLUP);
    pinMode(3, INPUT_PULLUP);

    RTC.begin();
    lcd.init();
    lcd.backlight();
}

//時刻設定の設定箇所の変数
int mode;

int year;
int month; 
int day;
int hour;
int minute;
int second;

void loop () {
    static int old_second;
    if (RTC.isrunning()) {
        //時刻を取得
        getNow();
        
        if(old_second!=second){
          //LCDに時間を表示
          lcdshow();
          old_second=second;
        }

        //デジタルピン2番のスイッチが押された場合には時刻設定に入る
        if(chatteringCut_pullupPin(2)){
            mode++;
            if(6<mode) mode=0;
        }

        //デジタルピン3番のスイッチが押された場合には時間をカウントアップして設定する
        if(mode && chatteringCut_pullupPin(3)){
            getNow();
            switch (mode){
                case 1:
                  year++;
                  if(year>2050) year=2000;
                  break;
                case 2:
                  month++;
                  if(month>12) month=1;
                  break;
                case 3:
                  day++;
                  if(day>31) day=1;
                  break;
                case 4:
                  hour++;
                  if(hour>23) hour=0;
                  break;
                case 5:
                  minute++;
                  if(minute>59) minute=0;
                  break;
                case 6:
                  if(second>30 && minute<59) minute++;
                  second=0;
                  break;
                default:
                  break;
            }
            RTC.adjust(DateTime(year,month,day,hour,minute,second));
            lcdshow();
        }
    }else{
        lcd.setCursor(0, 0);
        lcd.print("RTC not find    ");
        lcd.setCursor(0, 1);
        lcd.print("ERROR           ");
    }
}

//現在の時刻を取得する
void getNow(){
    DateTime now = RTC.now();
    year = now.year();
    month = now.month(); 
    day = now.day();
    hour = now.hour();
    minute = now.minute();
    second = now.second();
}

//時刻をLCDに表示する
void lcdshow(){
    //時刻設定用の300msカーソル点滅を作成
    static bool brink;
    static unsigned long old_millis;

    if((millis()-old_millis)>300){
        brink=!brink;
        old_millis=millis();
    }

    //画面に時刻を表示
    char str[17];
    
    lcd.setCursor(0, 0);

    sprintf(str,"%4d/",year);
    sprintf(str+5,"%2d/",month);
    sprintf(str+8,"%2d      ",day);
    
    //時刻設定のカーソル点滅
    if(1==mode && brink) sprintf(str,"    ");
    if(2==mode && brink) sprintf(str+5,"  ");
    if(3==mode && brink) sprintf(str+8,"  ");
    lcd.print(str);
    
    lcd.setCursor(0, 1);

    sprintf(str,"%002d:",hour);
    sprintf(str+3,"%002d:",minute);
    sprintf(str+6,"%002d        ",second);

    //時刻設定のカーソル点滅
    if(4==mode && brink) sprintf(str,"  ");
    if(5==mode && brink) sprintf(str+3,"  ");
    if(6==mode && brink) sprintf(str+6,"  ");
    lcd.print(str);

}

//ボタンを押した時のチャタリング対策
unsigned int chatteringCut_pullupPin(int pinNo){
    unsigned int i=0;
    while(!digitalRead(pinNo)){
        i++;
        //if(i>200) break;
        delay(1);
    }
    //1111111111000000 でアンドを取り下位ビットをマスクする
    //つまりループが63を超えるまで0しか返さない
    return i&(~(unsigned int)0x3F);
}


この150行近くあるプログラムを実行すると安いデジタル時計風に時間設定ができるようになります。
ただ、割り込みを使ってないのでボタン操作がもっさりしています。
ちなみに写真のDS3231は電池の代わりにスーパーキャパシタを取り付けてあります。


▲トップページ > マイコンなど