リアルタイムクロック(RTC-8564NB)の使い方 [Arduino]
今回のリアルタイムクロック(RTC)の「RTC-8564NB」はArduinoなどのマイコンボードにあるSDA/SCLピンをお互いに接続して「I2C通信」を行い利用します。
このRTCは「時計・カレンダー」以外に「アラーム機能」(日時指定の割り込み)、「タイマー機能」(カウントダウンによる割り込み)、「クロック出力」(一定周期による割り込み)があります。
実行例
[アラーム]
アラーム時刻(12時21分)になると、LEDが点灯します。
[シリアルモニター]
現在時刻とアラーム時刻を表示します。
使用部品・材料
総額で約800円です。(Arduino本体の値段を除く)
部品/材料 | 値段 | 備考 |
---|---|---|
ブレッドボード | ¥270 | 秋月電子の通販コード(P-00315) |
リアルタイムクロック(RTC) モジュール | ¥500 | 秋月電子の通販コード(I-00233) |
5mm赤色LED OSDR5113A | ¥20 | 秋月電子の通販コード(I-11655) |
タクトスイッチ | ¥10 | 秋月電子の通販コード(P-03647) |
カーボン抵抗器 | 数円 | 10kΩ(1個)、1kΩ(2個)、220Ω(1個)を使用。 |
配線図
[RTC-8564NB] ※公式より引用。
[SDA/SCLについて]
Arduino UNOの場合はSDA/SCLはデジタルピン側のAREFの横にあります。「SDA=A4 SCL=A5」でも代用可能です。
[外部割り込みについて]
Arduino UNOの場合はデジタルの2ピン(int.0)または3ピン(int.1)を使用します。それ以外のピンでは動作しないのでご注意ください。
スケッチ(プログラム)
シリアルモニターに現在時刻とアラーム時刻を表示します。
アラーム時刻になるとLEDが点灯。LEDを消灯するにはボタンを押します。
#include <Wire.h> // 外部割り込みの状態 volatile int state = LOW; // RTCのレジスタテーブル(16byte) int RegTbl[16]; // デバイスアドレス(スレーブ) // ※Arduinoの仕様では8bitのアドレスを右に1bitシフトした「7bit」を使用する // ※[8bit]Write : 0xA2 = 10100010 Read : 0xA3 = 10100011 byte DEVICE_ADDRESS= 0x51; // 2進化10進数(BCD)を10進数に変換 byte BCDtoDec(byte value){ return ((value >> 4) * 10) + (value & 0x0F) ; } // 外部割り込みが発生した時に動作します。 void myInterrupt() { state = !state; } void setup() { Serial.begin(9600); // タイマー割り込みの解除用 pinMode(8,INPUT); // 2ピンの状態がLOWからHIGHに変化した場合に外部割り込みを発生させる attachInterrupt(digitalPinToInterrupt(2), myInterrupt, RISING); // マスタとしてI2Cバスに接続する Wire.begin(); // I2Cスレーブに対して送信処理を開始する Wire.beginTransmission(DEVICE_ADDRESS); // レジスタのアドレスを指定する(0-15の内、先頭から) Wire.write(0x00); // --------------------------------- // コントロールレジスタ // --------------------------------- // [00]Control1 Wire.write(0x00); // [01]Control2 // 0x02:アラーム割り込み時に3ピン[INT]をLOWにする Wire.write(0x02); // --------------------------------- // 時計・カレンダーレジスタ // --------------------------------- // アドレスの[02-05]及び[08-11]は2進化10進数(BCD)で指定します。 // [02]Seconds(15秒) Wire.write(0x15); // [03]Minutes(20分) Wire.write(0x20); // [04]Hours(12時) Wire.write(0x12); // [05]Days(25日) Wire.write(0x25); // [06]Weekdays(月) // 0:日 1:月 2:火 3:水 4:木 5:金 6:土 Wire.write(0x01); // [07]Month/Century(21世紀の12月) // ・Month(01-12) BCD形式 // ・Century(先頭bit 0:20世紀[19xx年代],1(0x80):21世紀[20xx年代]) Wire.write(0x12 | 0x80); // [08]Years(2017年) Wire.write(0x17); // --------------------------------- // アラームレジスタ // --------------------------------- // 先頭bitに1がある場合(0x80)はアラーム対象外となる // 例)次のコードは「25日(月)12時21分」にアラームが設定されていますが、 // 「25日」と「(月)」には0x80がありますので、実際のアラームは「12時21分」で毎日となります。 // [09]Minutes alarm(21分) Wire.write(0x21); // [0A]Hours Alarm(12時) Wire.write(0x12); // [0B]Days Alarm(25日) Wire.write(0x25 | 0x80); // [0C]Weekdays Alarm(月) // 0:日 1:月 2:火 3:水 4:木 5:金 6:土 Wire.write(0x01 | 0x80); // --------------------------------- // クロック出力レジスタ // --------------------------------- // [0D]CLKOUT Frequency // ・クロック出力機能を有効にする。 // ※有効:0x80 無効 :0x00 // ・クロック周波数は1Hz(1秒間に1回)とする // ※0x00:32768Hz, 0x01:1024Hz, 0x02:32Hz, 0x03:1Hz // // 次の設定だと2ピン[CLKOUT]から1秒に1回、クロック出力される Wire.write(0x80 | 0x03); // I2Cスレーブへの送信完了 Wire.endTransmission(); Serial.println("RTCの初期化完了。"); } void loop(){ int i; // 外部割込みが発生した場合 if(state == HIGH){ // レジスタのアドレスを先頭にする Wire.beginTransmission(DEVICE_ADDRESS); Wire.write(0x00); Wire.endTransmission(); // I2Cスレーブに16byteのレジスタデータを要求する Wire.requestFrom(DEVICE_ADDRESS,16); // 16byteのデータを取得する for (i=0; i<16; i++){ while (Wire.available() == 0 ){} RegTbl[i] = Wire.read(); } // 現在日時 Serial.print("現在:"); Serial.print(String(BCDtoDec(RegTbl[8])+ 2000) + "年"); Serial.print(String(BCDtoDec(RegTbl[7] & 0x1F)) + "月"); Serial.print(String(BCDtoDec(RegTbl[5] & 0x3F)) + "日"); switch(RegTbl[6] & 0x07){ case 0 : Serial.print("(日)");break; case 1 : Serial.print("(月)");break; case 2 : Serial.print("(火)");break; case 3 : Serial.print("(水)");break; case 4 : Serial.print("(木)");break; case 5 : Serial.print("(金)");break; case 6 : Serial.print("(土)");break; } Serial.print(" "); Serial.print(String(BCDtoDec(RegTbl[4] & 0x3F)) + "時"); Serial.print(String(BCDtoDec(RegTbl[3] & 0x7F)) + "分"); Serial.print(String(BCDtoDec(RegTbl[2] & 0x7F)) + "秒"); // アラーム日時 Serial.print(" アラーム:"); if((RegTbl[11] & 0x80) == 0){ Serial.print(String(BCDtoDec(RegTbl[11] & 0x3F)) + "日"); } if((RegTbl[12] & 0x80) == 0){ switch(RegTbl[12] & 0x07){ case 0 : Serial.print("(日) ");break; case 1 : Serial.print("(月) ");break; case 2 : Serial.print("(火) ");break; case 3 : Serial.print("(水) ");break; case 4 : Serial.print("(木) ");break; case 5 : Serial.print("(金) ");break; case 6 : Serial.print("(土) ");break; } } if((RegTbl[10] & 0x80) == 0){ Serial.print(String(BCDtoDec(RegTbl[10] & 0x3F)) + "時"); } if((RegTbl[9] & 0x80) == 0){ Serial.print(String(BCDtoDec(RegTbl[9] & 0x7F)) + "分"); } Serial.println(""); state = !state; } // ボタン押下時にアラーム割り込みイベントをクリアする(LEDを消灯する) if(digitalRead(8) == HIGH){ // レジスタのアドレスをControl2にする Wire.beginTransmission(DEVICE_ADDRESS); Wire.write(0x01); // アラーム割り込みが発生した際に // Control2の4bit目のAFフラグが「1」になるので「0」にする // それにより、3ピン[INT]がHIGHになりLEDが消灯します。 Wire.write(0x02); Wire.endTransmission(); } }
このスケッチはこちらのサイトのコードを参考にしています。特に初心者向けにコメントをフンダンに付けました。
システムの流れ
1 | setup()で時刻などの情報をRTCに書き込みます。 |
---|---|
2 | RTCの機能を使用して1Hz毎(1秒に1回)にクロック出力(CLKOUT)で外部割込みを発生させます。この割り込みでシリアルモニターに現在時刻を表示させます。 スケッチの流れとしては、割り込みが発生すると、myInterrupt()が実行されて「state」のフラグを逆にします。次にloop()で「state」が「HIGH」の場合にRTCから時刻を取得してシリアルモニターへ出力しています。 |
3 | 上記の流れとは別に、RTCのアラーム機能で12時21分になったら、LEDを点灯するようにしています。このLEDを消灯にするにはブレッドボードに設置したボタンを押下します。 |
最後に
今回は「時計・カレンダー」「アラーム機能」「クロック出力」をご紹介しましたが「タイマー機能」には触れていません。
というのは「アラーム機能」と「タイマー機能」は2ピン[INT]の扱いで被っているんです。AF/TFフラグの状態を確認すればわかるみたいですが、ややこしくなるので却下しました。
詳しく知りたい方は、公式のデータシートをご参照ください。
参考リンク
リアルタイムクロック
リアルタイムクロック(RTC)との接続
RTC(リアルタイムクロック)と接続して読み書きを行って見ます
掲示板
ArduinoやRaspberry Piなどの電子工作の掲示板を作成しました。質問やわからない事は電子工作 (Arduino・ラズパイ等)でユーザー同士で情報を共有して下さい。