RTC(リアルタイムクロック)と接続して読み書きを行って見ます

〔Arduinoの動かせ方入門に戻る〕


 前回の記事でArduinoとPICを接続してI2C通信を行いました、I2Cの概要等はそちらを参考にして下さい。 この頁ではArduinoとRTC(RTC-8564NB)をI2Cで接続してRTC機能を利用して見たいと思います。
このRTC-8564NB利用の記事はすでに沢山の他のHPサイト様が書かれていますが、私なりに書いて見たいと思います。 尚、RTC-8564NBは秋月電子のこちらから購入しています。
 Arduinoにももちろん時刻カウント機能は有りますが精度が良くなくずれずれです、 なので、外部に時刻カウント専用ICのRTCを取り付けて利用したりします。
RTC-8564NBのマニュアルを見ると月差1分の誤差となっている...時計での利用は今いちか?...
(こちらのモジュールが、月差13秒相当と誤差が少ないのでお勧めかも、入手にて動作記事を書きます)

もっとぉ高性能なRTCが欲しいと言う人はSPI接続ですがこれ何か如何でしょう?月差約3秒程らしい。
私も含めて、こんなSOパッケージ(1.27mmピッチ)なんか半田付け出来るかぁ〜って人は
スイッチサイエンスのこちら何かは同でしょう、またI2C接続が良いって人はこちらをどうぞ。 *3)

 RTC-8564NBの機能として周波数出力(32.768kHz, 1024Hz, 32Hz, 1Hz)と 定周期タイマー割込み機能(244.14us〜255min)にアラーム割込み機能(何日何時何分何曜日の指定が出来る)が有ります。
周波数出力はCLKOUT端子から出力され、定周期タイマー・アラーム割込み機能はINT端子から出力されます。 尚、通常はINT端子はHIGHで設定時間になればLOW(GNDに接続される)です。 *1)

 この頁のRTCライブラリ(skRTClib)を利用して初期化すれば、周波数出力は1Hz(1秒に1回ON/OFFの繰返し)で設定されCLKOUT端子から出力されています、 なので後はArduinoの外部割り込みピンにいれてやれば1秒に1回割込みが発生する様になります。
 また、このRTCライブラリの初期状態では定周期タイマー・アラーム割込み機能は禁止となっています、利用したい時に 定周期タイマー・アラーム割込み機能設定関数を使えば利用出来ます。

[配線図]

配線図 配線の写真
(Fritzing用のRTC-8564NB部品ダウンロードはこちらからお願いします。)

ArduinoのI2C/TWI端子はSCL(アナログ5番ピン)・SDA(アナログ4番ピン)となっています。

抵抗について
I2Cのプルアップ抵抗用として1KΩを2個取り付けていますが、 RTC-8564NBモジュールは、上図のJP1とJP2の場所を半田付けすればモジュールに取り付けてある 2.2KΩでプルアップされるので外付け1KΩはいらなくなります。

LEDについて
今回、定周期タイマー・アラーム割込み機能確認用としてRTCのINT端子に接続しています。
LEDは通常は消えています、定周期タイマー・アラーム割込み機能の設定時刻になると点灯します。
(通常INT端子はHIGHに接続され、設定時刻になるとLOW/GNDに接続されるので+5V電源から電流が流れてきてLEDが点灯します) *1)
また、RTC-8564NBモジュールは、上図のJP3の場所を半田付けすればモジュールに取り付けてある
LEDで確認できるので外付けLEDと抵抗はいらなくなります。

LEDを外付けする場合はLEDには極性が有ります、
足の長いアノード側をVDD(5V)の方に、足の短いカソード側をINT端子側の方に接続します。
また、LEDには流せる電圧と電流が決まっています、必ず電流制限抵抗を付けましょう。

電流制限抵抗
 LEDの順方向電流(IF)と順方向電圧(VF)がデータシート等に書いてあると思います、
 例えばIFが10mAで、VFが2.5Vで、電源電圧が5Vとすると、
 (電源電圧−順方向電圧)÷ 順方向電流 = 電流制限抵抗値
 よって、(5V - 2.5V) ÷ 0.010A = 250Ω(250Ωは無いので240Ωか270Ωを使います)
 10mAは0.010AというふうにAに変換して計算します。

 だいたい120Ω〜680Ωのあたりだと思います。
 LEDは5mAくらいで使った方が目に優しいでしょう、で470Ω?
 また、抵抗はLEDのアノード側とカソード側のどちら側に接続してもOKです。

《ダウンロードスケッチについて》

サンプルスケッチは"2012/01/10 火曜 15:30:00"から時間設定を行っています、
まずはこの設定で実験を行い動作を確認した後に色々設定を変更して見て下さい。


↓ここからArduino用サンプルスケッチファイルをダウンロードして下さい。
skRTClib.lzh(2012.01.15)
skRTClib.lzh(Ver 1.01:2012.01.24) RTC.rTime()関数のバグ修正  *1)

ArduinoIDE 1.0.1でArduinoのI2Cライブラリ名やインクルード名が変更になっています、
その対応でスケッチを変更しました、↓をダウンロードして下さい。
skRTClib.lzh(Ver 1.10:2012.09.13)  IDE1.0.1対応  *2)
ですので、IDEは1.0.1以降を利用ください。

解凍すると下の様に展開されます。
[skRTClib]─┬─[examples]──┬─[Repeat]--- Repeat.pde
       ├ skRTClib.cpp └─[Inter]---- Inter.pde
       ├ skRTClib.h
       └ keywords.txt
ArduinoIDE0022がインストールされているフォルダー、インストール先を変更していないなら
[C:\Program Files\arduino-0022\libraries]です。
この場所に上記解凍ファイルを[skRTClib]ディレクトリ丸ごと移動します。
また、こちらの[ライブラリ登録]を参考すればより理解が深まるかもぉ?いや、こんがらがるかもぉ。

Repeat.pde ・・・・・・RTCサンプル1の本体スケッチ(自己delayでの1秒繰り返し&アラーム機能)
Inter.pde ・・・・・・・・RTCサンプル2の本体スケッチ(外部割込みでの1秒繰返し&定周期タイマー機能)
skRTClib.cpp ・・・・・RTC(I2C/TWI)関数ライブラリソース
skRTClib.h ・・・・・・・ヘッダファイル
keywords.txt ・・・・・キーワードファイル

Repeat.pde

このスケッチは delay(1000) 1秒毎で自己ループしてそのタイミングでRTCから時刻を読み取って表示を行っています。 また、アラーム機能も起動から1分後に出力する様に設定されています。

Repeat.pdeを開くには、
IDEを起動して、メニューバーの「File」→「Examples」→「skRTClib」→[Repeat]
をクリック操作すればファイルが開かれます。
次に、IDEツールバーの「Upload」ボタンをクリックしてコンパイルとarduinoボードに書込みを行います。

COM画面1  IDEのシリアルモニターCOM画面を表示させましょう。
 こんな感じ(左図)に1秒毎に表示されると成功です。
 "Failed initialization of the RTC ans=?"と表示されたら失敗です。
 配線などをよ〜くぅ確かめてみましょう。

 起動から1分後(15:31)にアラーム機能が起動しLEDが点灯する、
 さらに1分後(15:32)にアラーム機能を停止するのでLEDが
 消灯します、表示は継続したままです。


Inter.pde

このスケッチはRTCからの1秒毎割り込み信号のタイミングでRTCから時刻を読み取って表示を行っています。 また、定周期タイマー機能も起動後、10秒間隔で出力する様に設定されています。

尚、このスケッチを実行する前に配線が1本必要になります、上の配線図には記載されていません、 それはRTCの2番ピン(CLKOUT)とArduinoのデジタル2番ピンをジャンパー線で接続して下さい、
RTCからのクロック信号(1Hz)でArduinoに割り込みをかける為です。

Inter.pdeを開くには、
IDEを起動して、メニューバーの「File」→「Examples」→「skRTClib」→[Inter]
をクリック操作すればファイルが開かれます。
次に、IDEツールバーの「Upload」ボタンをクリックしてコンパイルとarduinoボードに書込みを行います。

COM画面2  IDEのシリアルモニターCOM画面を表示させましょう。
 上記の"Repeat.pde"の様に表示されます。

 また、定周期タイマー機能によりLEDが10秒間隔で点滅します、
 それから2分後(15:32)に定周期タイマー機能を停止しているので
 LEDが消灯します、表示は継続したままです。

 左の赤線を見ましょう、[15:30:20]が表示されていません、
 これは実は上記の"Repeat.pde"で表示させた場合の画像です。
 Repeat.pdeは自分のdelay()ループ1秒のタイミングでRTCを読んで
 いましたがどうしてもずれが生じます、なのでInter.pdeの様にRTC
 から外部の割り込み信号をもらいそのタイミングで読みにいけばずれません。


skRTClib.h

RTC(リアルタイムクロック)関数のインクルードファイルです。
RTC関数ライブラリを利用する場合は、メニューバーの「Sketch」→「ImportLibray...」→「skRTClib」を
クリック操作すれば、"#include <skRTClib.h>"がスケッチに追加されます。
まぁ、手動でキー入力しても良いんですけどね。

skRTClib.cpp

まず利用する場合は下記2行を記述します。
#include <Wire.h>
#include <skRTClib.h>

RTC(リアルタイムクロック)用関数の使い方を説明します。

ans = RTC.begin(Inter,Year,Mon,mDay,wDay,Hour,Min,Sec)
 RTCを利用する為の初期化を行う処理です。
  Inter :RTCからのクロック出力による外部割込み処理を行うのかを指定します
      0:割込みは無し 2:2番ピンで割込みする 3:3番ピンで割込みする
      クロック出力(CLKOUT)は1Hzで初期化しているのでRTCは常に出力状態です。
      その信号をArduinoの2か3番ピンで受けて割込み処理を行う場合に使用します。
      下記記載の"CLKOUT割込みの利用方法"を参照下さい。
  Year :日付の年(0-99)を指定します(西暦の下2ケタ 2000年-2099年)
    Mon  :日付の月(1-12)を指定します
    mDay:日付の日(1-31)を指定します(在りえない日を指定したら動作が不定らしい)
    wDay:曜日の指定をします 0:日 1:月 2:火 3:水 4:木 5:金 6:土
  Hour :時刻の時(0-23)を指定します
    Min   :時刻の分(0-59)を指定します
    Sec  :時刻の秒(0-59)を指定します
  ans   :戻り値    0=正常終了、それ以外はI2C通信エラーです
             1=送ろうとしたデータが送信バッファのサイズを超えた
             2=スレーブ・アドレスを送信し、NACKを受信した
             3=データ・バイトを送信し、NACKを受信した
             4=その他のエラー
             5=データ受信エラー
 例)外部割込みなしで"2012/01/10 火曜 15:30:00"で初期化するなら
     ans = RTC.begin(0,12,1,10,2,15,30,0) ;
     if (ans == 0) {
          Serial.println("Successful initialization of the RTC") ;// 初期化成功
     } else {
          Serial.print("Failed initialization of the RTC ans=") ; // 初期化失敗
          Serial.println(ans) ;
          while(1) ;                                              // 処理中断
     }
 定周期タイマー・アラーム割込み機能は禁止の無しで初期化されます。
 クロック出力(CLKOUT)は1Hz(1秒間に1回ON/OFF)で初期化しています。
   1Hz以外に変更したければRTCレジスタアドレス0Dhの初期化を書き換えて下さい。
   1Hz=0x83 32Hz=0x82 1024Hz=0x81 32.768KHz=0x80 の4パターンです。
 電池などでRTCがバックアップ状態時はすでに初期起動時に初期化は行われています、その
   状態でこの初期化処理が再度実行(Arduino再起動等)された場合は、初期化処理は行いません。
 バッテリバックアップの方法実験についてはこちらを参考にして下さい。

ans = RTC.sTime(Year,Mon,mDay,wDay,Hour,Min,Sec)
 RTCに日付と時刻を書き込む処理です。
 時刻がずれた等の時に再度時刻を設定する場合に利用しましょう。
  Year :日付の年(0-99)を指定します(西暦の下2ケタ 2000年-2099年)
    Mon  :日付の月(1-12)を指定します
    mDay:日付の日(1-31)を指定します(在りえない日を指定したら動作が不定らしい)
    wDay:曜日の指定をします 0:日 1:月 2:火 3:水 4:木 5:金 6:土
  Hour :時刻の時(0-23)を指定します
    Min   :時刻の分(0-59)を指定します
    Sec  :時刻の秒(0-59)を指定します
  ans   :戻り値    0=正常終了、それ以外はI2C通信エラーです(エラー内容はRTC.begin()を参照)

ans = RTC.rTime(*tm)
 RTCから現在の日付と時刻を読み取る処理です。
  *tm  :読み取ったデータを保存する配列変数を指定します(配列は7バイト必要です)
      配列にはBCDデータで秒・分・時・日・曜日・月・年の順で保存されます
  ans   :戻り値    0=正常終了、それ以外はI2C通信エラーです(エラー内容はRTC.begin()を参照)
 例)
     byte tm[7] ; 

     RTC.rTime(tm) ;
     for (int i=0 ; i < 7 ; i++) {
          Serial.print(tm[i],HEX) ;
          Serial.print(' ') ;
     }
RTC.cTime(*tm,*c)
 RTCの日付と時刻を文字列に変換する処理です。
 RTC.rTimeはBCD値で返しますが、その値を文字列に変換します。
  *tm  :RTC.rTimeから読み込んだ配列変数を指定します
  *c    :文字列に変換したデータ(24バイト)を格納する配列変数を指定します
      変換データのフォーマットは、"yyyy/mm/dd www hh:mm:ss" (2010/01/15 TUE 15:30:00)
      で返します
 例)
     byte tm[7] ; 
     char buf[24] ;

     RTC.rTime(tm) ;
     RTC.cTime(tm,(byte *)buf) ;
     Serial.println(buf) ;
ans = RTC.SetTimer(sclk,count)
 定周期タイマーの設定をする処理です。(タイマーの開始)
  sclk   :ソースクロックの指定です 0:244.14us 1:15.625ms 2:1sec 3:1min
  count:カウントダウン値の指定(1-255)
      ソースクロックのタイミングでcountの値がカウントダウンし、0でINT端子がONします。
      すなわち、sclk=2 count=10 なら10秒周期でINTがONします。
      また、INT端子がONしている時間は 244us(sclk=0時)か15.625ms(sclk=1-3時)でOFFします。
  ans   :戻り値    0=正常終了、それ以外はI2C通信エラーです(エラー内容はRTC.begin()を参照)
 例)10秒周期設定の場合
     RTC.SetTimer(2,10) ; // これを実行した後定周期タイマーの開始です
 定周期タイマー機能の動作として、設定の定周期でONとOFFを繰り返すモードと、
   ONが1回限りで終了するモードの2種類がありますが、このライブラリでは繰り返すモードのみ
   対応です。

ans = RTC.StopTimer()
 定周期タイマー機能を無効にする処理です。(タイマーの終了)
 この処理を発行しない限り、定周期タイマー出力は永遠に繰り返しています。
  ans   :戻り値    0=正常終了、それ以外はI2C通信エラーです(エラー内容はRTC.begin()を参照)

ans = RTC.SetAlarm(Hour,Min,mDay,wDay)
 アラームの日時を設定する処理です。(アラームの開始)
 設定した時刻になるとINT端子がONします、RTC.offAlarm()関数を発行しない限りONしたままです。
  Hour :時刻の時(0-23)を指定します、0xff指定でHour設定は無効となります
    Min   :時刻の分(0-59)を指定します、0xff指定でMin設定は無効となります
    mDay:日付の日(1-31)を指定します、0xff指定でmDay設定は無効となります
    wDay:曜日の指定をします 0:日 1:月 2:火 3:水 4:木 5:金 6:土 0xff指定でwDay設定無効
  ans   :戻り値    0=正常終了、それ以外はI2C通信エラーです(エラー内容はRTC.begin()を参照)
 例)
     RTC.SetAlarm(30,0xff,0xff,0xff) ;  // 30分の1時間毎に起動
     RTC.SetAlarm(0,12,0xff,0xff) ;     // 毎日12時に起動
     RTC.SetAlarm(0,12,1,0xff) ;        // 毎月の1日の12時に起動
     RTC.SetAlarm(0,12,0xff,1) ;        // 毎週月曜日の12時に起動
ans = RTC.StopAlarm()
 アラーム機能を無効にする処理です。(アラーム終了)
 アラーム設定は無効になり、INT端子の出力はOFFします。
  ans   :戻り値    0=正常終了、それ以外はI2C通信エラーです(エラー内容はRTC.begin()を参照)

ans = RTC.offAlarm()
 アラームの発生を解除する処理です。
 INT端子の出力のみOFFします、設定は有効なので次の時刻になるとINT端子の出力はONします。
  ans   :戻り値    0=正常終了、それ以外はI2C通信エラーです(エラー内容はRTC.begin()を参照)

※定周期タイマー機能とアラーム機能は同じINT端子を兼用しているので、両方設定していると
 出力がかさなります、どちらでONしたかを知るにはRTCレジスタアドレス01h(Control2)の
 AF/TFフラグを読み出さないと判断付きません、このライブラリでは行っていません。


CLKOUT割込みの利用方法

本ライブラリでクロック出力(CLKOUT)をArduinoに接続して外部割込みとして利用する方法を説明しておきます。

RTCの2番ピン(CLKOUT)とArduinoのデジタル2番ピンを接続した場合
 ans = RTC.begin(2,12,1,10,2,15,30,0) ; // 赤数字の場所を2にします
RTCの2番ピン(CLKOUT)とArduinoのデジタル3番ピンを接続した場合
 ans = RTC.begin(3,12,1,10,2,15,30,0) ; // 赤数字の場所を3にします
利用しない場合は赤数字は0です、尚、Arduino Duemilanoveは外部割込み2と3番ピンのみです。

後は、初期化設定(RTC.begin)で1秒周期(1Hz)でCLKOUTから出力されているので、
ライブラリ(skRTClib)が割込みがかかると RTC.InterFlag を1にします。
だからぁ、本体のスケッチ内で RTC.InterFlag=1 をチェックして下さい、
チェック終了したら必ずぅ RTC.InterFlag=0 を実行して下さい。
 例)この例が"Inter.pde"と同じ処理です
     void loop()
     {
          byte tm[7] ; 
          char buf[24] ;

          if (RTC.InterFlag == 1) {          // 割込みは発生したか?
               RTC.rTime(tm) ;               // RTCから現在の日付と時刻を読込む
               RTC.cTime(tm,(byte *)buf) ;   // 日付と時刻を文字列に変換する
               Serial.println(buf) ;         // シリアルモニターに表示
               RTC.InterFlag = 0 ;           // 割込みフラグをクリアする
          }
     }
loop()関数内で1秒以上かかるような他の仕事をさせる場合はこの方法はあまりよろしくないですがぁ、
実は"skRTClib.cpp"のライブラリ内に、attachInterrupt(0,InterRTC,RISING);を宣言しています。
なので下記の様に割込み処理関数を記述したのですが、
     void InterRTC()
     {
          byte tm[7] ; 
          char buf[24] ;

          RTC.rTime(tm) ;               // RTCから現在の日付と時刻を読込む
          RTC.cTime(tm,(byte *)buf) ;   // 日付と時刻を文字列に変換する
          Serial.println(buf) ;         // シリアルモニターに表示
     }
どうもInterRTC()関数内でWireライブラリを呼び出すと上手く動作しません、 Wireライブラリが割り込み処理で何か行っている感じですが良く分かりません、 やり方が有るかもですがぁ、断念してぇ、RTC.InterFlag 方式にしたしだいです。
まあ、時計の様に1秒毎に表示したり等の事をしなければ、それ程シビアに考えなくてもぉって感じみたいなぁ。

ちなみにぃ割り込みを利用しない"Repeat.pde"の場合の例は下記です。
     void loop()
     {
          byte tm[7] ; 
          char buf[24] ;

          RTC.rTime(tm) ;               // RTCから現在の日付と時刻を読込む
          RTC.cTime(tm,(byte *)buf) ;   // 日付と時刻を文字列に変換する
          Serial.println(buf) ;         // シリアルモニターに表示
          delay(1000) ;                 // 1秒後に繰り返す
     }

尚、
外部割込みは2番と3番ピンのみ利用可能と書きました、 なのでこのライブラリでもし2番ピンを利用した場合は、3番ピンのみしか外部割り込みは利用できなくなります。
他から3番ピンを外部割り込みで利用する場合は、
attachInterrupt(1,function,RISING) ;
として、割り込みが発生した時に関数functionが呼び出されます。
functionは関数名で自由に名前は変更可能です、詳しくはArduino日本語リファレンスを参照ください。


・ EEPROM(24LC256)と接続した記事はこちらを参照下さい。
・ RTC-8564NBとPIC(16F1827)を接続した記事はこちらを参照下さい。
・ PICで"32.768KHzの水晶振動子を使ってRTCを行ってみます"の記事はこちらを参照下さい。



追記(*3) 2014/12/27
追記(*2) 2012/09/13
追記(*1) 2012/01/24


【きむ茶工房ガレージハウス】
Copyright (C) 2006-2014 Shigehiro Kimura All Rights Reserved.