秋月でリアルタイムクロックRTC-8564NBを買ったのでその使い方の説明。このデバイスかなり昔から秋月で売られています。秋月の商品説明にも、”I2Cインターフェース(2線式)でPICやH8などと通信可能です”と有ります。H8って1980年代の製品です。40年位前から売ってるのでしょうか。
商品説明
秋月のHPのA045_リアルタイムクロック.pdfに商品の概要が説明されています。
- インターフェース方式 I2C
- 電圧範囲 1.8Vから5.5V
- バックアップ時消費電流 275nA
- 出力制御付き32.768kH出力機能 CLKOUT端子出力
8ピンの時計機能を持ったモジュールです。回路及び各ピンは以下の様になっています。
回路図の下にある8ピンがヘッダーピンと繋がっています。
- ピン1 CLKOE
- クロック制御用のピン。
- このピンがHIGHの時、内部レジスタを設定するとピン2からクロックが出力される。
- この回路では、1MΩでPullUp(HIGH)されている。
- ピン2 CLKOUT
- クロック出力用端子
- ピン3 INT
- 割り込み信号出力端子。通常ハイインピーダンスで、割り込み時はLOWになります。
- 回路上オープンですので、通常時HIGHにしたいならPullUpが必要です。
- ピン4 GND
- ピン5 SDA
- I2Cのデータ信号
- ピン6 SCL
- I2Cのクロック信号
- ピン7 N.C.
- ピン8 Vdd
- 電源に接続
- パッド JP1
- ここをショートするとSCLが2.2KΩでPullUpされる。
- パッド JP2
- ここをショートするとSDAが2.2KΩでPullUpされる。
- パッド JP3
- ここをショートするとINTがLOWの時にLEDが点灯する。
使い方
RTC8564のレジスターに値を設定して使います。下記がレジスタ一覧
注意点は
- アドレス00(Control 1)レジスタのTESTビットは必ず”0”にする。
- 電源投入時全てのレジスタの値は不定な為、必ず設定を行う。
- アドレス02〜05,08〜0BはBCD形式
- 上記の表で、”x”のビットは使用しない。マスクして使用。
です。ちなみにこの素子のI2Cアドレスは、”0x51”固定です。
ESP32との接続
先ずは回路図、
- ESP-WROOM-32 DIP化キットを使用。
- リセット(ENボタン)とブート(Bootボタン)。通信用のGND, TD, RDを追加
- GPIO21をRTC-8564のSDAにGPIO22をRTC-8564のSCLに接続。
- RTC-8564のCLKOUTにLED1を配線。CLKOUTのクロックに合わせてLEDが点灯。
- GPIO23にRTC-8564のINTを接続。割り込み信号に合わせてLED2を点灯。
次はスケッチ
開発環境は、Arduino IDEを使用します。
レジスタへの書き込みは、レジスタカウンタにレジスタ番号を指定した後、データを送信して行われます。
- I2Cアドレスを指定。
- 書き込みたいレジスター番号を指定。
- 書き込みたいデータの送信。
- 終了処理。
1つの書き込み操作が終了すると、レジスタカウンタがインクリメントされます。よって連続したレジスターの書き込みの場合は、最初のレジスタ番号を指定し、書きたいレジスタの数だけ上記 ”3”を行って書き込む事がが出来ます。(レジスタの指定は最初のみ)。
下記は、書き込みを行うレジスタ番号(uint8_t cmd)、データ(uint8_t rt_data)を引数として書き込み操作を行う関数です。
void Write_RTC_8564(uint8_t cmd, uint8_t rt_data)
{
Wire.beginTransmission(RTC_8564_ADDR);
Wire.write(cmd);
Wire.write(rt_data);
Wire.endTransmission();
}
レジスタからの読み込みは、レジスタカウンタにレジスタ番号を指定した後、読み込みたいデータ数を送信した後データを読み込みます。
- I2Cアドレスを指定。
- 読み込みたいレジスター番号を指定。
- 一度送信を終了。
- 読み込みたいレジスタの数を送信。
- 送信したレジスタの数だけレジスタを読み込む。
書き込みと違うところは、レジスタ番号を送ってから一度送信を終了し、後に読み込みたいレジスタの数を指定する点です。
下記はレジスタ番号(uint8_t cmd)を指定してデータを読み込む関数です。
uint8_t Read_RTC_8564(uint8_t cmd)
{
Wire.beginTransmission(RTC_8564_ADDR);
Wire.write(cmd);
Wire.endTransmission();
Wire.requestFrom(RTC_8564_ADDR,1);
return(Wire.read());
}
上記は1個のレジスタ読み込み専用です。連続したレジスタを読み込む場合、6行目で読み込む個数を指定してその回数Wire.read()を行えばレジスタの指定は1回で済みます。
動作確認用のスケッチ
動作確認用スケッチ “RTC_8564_test.ino”。RTC-8564操作用スケッチ”RTC_8564.cpp”。そのヘッダー”RTC_8564.h” を添付します。
RTC_8564.cpp
//
// RTC_8564_test.ino
//
#include "Arduino.h"
#include <Wire.h>
#include "RTC_8564.h"
int led,i_time;
void setup()
{
time_data t_data;
byte a;
int b;
Serial.begin(115200);
delay(500);
Wire.begin();
delay(500);
Serial.println("Init & Set Time");
t_data.second = 0x56;
t_data.minute = 0x59;
t_data.hour = 0x23;
t_data.day = 0x31;
t_data.weekday = 1;
t_data.month = 0x12;
t_data.year = 0x20;
init_RTC_8564(t_data);
delay(500);
Serial.println("END");
Serial.println("Check the Time");
a=t_data.second;
b=5;
while(b)
{
get_RTC_time(&t_data);
if(a != t_data.second)
{
Serial.print("Year:" + String(chg_BCD_num(t_data.year)+2000));
Serial.print(" Month:" + String(chg_BCD_num(t_data.month)));
Serial.print(" Weekday:" + String(chg_BCD_num(t_data.weekday)));
Serial.print(" Day:" + String(chg_BCD_num(t_data.day)));
Serial.print(" Hour" + String(chg_BCD_num(t_data.hour)));
Serial.print(" Minute:" + String(chg_BCD_num(t_data.minute)));
Serial.println(" Second:" + String(chg_BCD_num(t_data.second)));
a=t_data.second;
b --;
}
delay(10);
}
Serial.println("END");
Serial.println("Start Clock Out");
Write_RTC_8564(RTC_8564_CLKOUT_FRQ, 0x83);
a=5;
Serial.print("last " + String(a) + " HIGH");
while(a)
{
delay(500);
Serial.println(" LOW");
delay(500);
a --;
if(a) Serial.print("last " + String(a) + " HIGH");
}
Write_RTC_8564(RTC_8564_CLKOUT_FRQ, 0x00);
Serial.println("END");
Serial.println("Timer interrupt Test");
pinMode(23, INPUT_PULLUP);
pinMode(19, OUTPUT);
digitalWrite(19, HIGH);
attachInterrupt(23, LED_blink, FALLING);
Write_RTC_8564(RTC_8564_TIMER_CONT, 0x00);
Write_RTC_8564(RTC_8564_CONT2, 0x11);
Write_RTC_8564(RTC_8564_TIMER_CONT, 0x02);
Write_RTC_8564(RTC_8564_TIMER_NUM, 0x02);
Write_RTC_8564(RTC_8564_TIMER_CONT, 0x82);
led=1; i_time=b=3;
Serial.print("last " + String(i_time) + " HIGH");
while(i_time){
if(b != i_time)
{
Serial.print("\nlast " + String(i_time) + " HIGH");
b=i_time;
}
delay(500);
}
Write_RTC_8564(RTC_8564_CONT2, 0x00);
Write_RTC_8564(RTC_8564_TIMER_CONT, 0x00);
Serial.println("\nEND");
Serial.println("Alarm interrupt Test");
Write_RTC_8564(RTC_8564_CONT2, 0x02);
a=chg_BCD_num(Read_RTC_8564(RTC_8564_MINUTE));
a ++; if(a>59) a=0;
a=chg_num_BCD(a);
Write_RTC_8564(RTC_8564_MINUTE_ALARM,a);
led=1; i_time=b=3;
Serial.print("last " + String(i_time) + " HIGH");
digitalWrite(19, HIGH);
while(i_time)
{
if(b != i_time)
{
Serial.print("\nlast " + String(i_time) + " HIGH");
b=i_time;
}
a=Read_RTC_8564(RTC_8564_CONT2);
if(a & RTC_8564_AF_BIT)
{
a &= 0xf7;
Write_RTC_8564(RTC_8564_CONT2,a);
a=chg_BCD_num(Read_RTC_8564(RTC_8564_MINUTE_ALARM));
a ++; if(a>59) a=0;
a=chg_num_BCD(a);
Write_RTC_8564(RTC_8564_MINUTE_ALARM,a);
}
delay(500);
}
Serial.println("\nEND TEST");
}
void LED_blink()
{
if(led){
digitalWrite(19, LOW);
led=0;
Serial.print(" LOW");
}
else {
led=1;
digitalWrite(19, HIGH);
i_time --;
}
}
void loop()
{
}
これは動作確認用のスケッチです。
- 23行から34行 ”Init & Set Time”
- 初期設定と時間の設定。
- 時間は2020年12月31日 日曜日 23時59分56秒に設定しています。
- 36行から56行 ”Check the Time”
- 設定して時間から5秒間 RTC−8564から時間を読み込みシリアルモニタに表示します。
- 2020年12月31日 日曜日 23時59分56秒から2021年1月1日 月曜日 0時0分1秒まで表示されます。
- 58行から71行 ”Start Clock Out”
- CLKOUT端子から1秒周期のパルスが出力されます。
- LED1が周期に合わせてオンオフします。
- シリアルモニタに周期に合わせてHIGH LOWと表示されます。
- 73行から98行 ”Timer interrupt Test”
- RTC-8564のタイマーをセットしESP32に割り込みを行っています。
- 割り込みのインターバルは1秒です。
- LED2が割り込みに合わせてオンオフします。
- シリアルモニタに周期に合わせてHIGH LOWと表示されます。
- 100行から130行 ”Alarm interrupt Test”
- RTC-8564のタイマーをセットしESP32に割り込みを行っています。
- 割り込みのインターバルは1分です。
- LED2が割り込みに合わせてオンオフします。
- シリアルモニタに周期に合わせてHIGH LOWと表示されます。
スケッチを実行するとシリアルモニタに以下の用に表示されます。
Init & Set Time
END
Check the Time
Year:2020 Month:12 Weekday:1 Day:31 Hour23 Minute:59 Second:57
Year:2020 Month:12 Weekday:1 Day:31 Hour23 Minute:59 Second:58
Year:2020 Month:12 Weekday:1 Day:31 Hour23 Minute:59 Second:59
Year:2021 Month:1 Weekday:2 Day:1 Hour0 Minute:0 Second:0
Year:2021 Month:1 Weekday:2 Day:1 Hour0 Minute:0 Second:1
END
Start Clock Out
last 5 HIGH LOW
last 4 HIGH LOW
last 3 HIGH LOW
last 2 HIGH LOW
last 1 HIGH LOW
END
Timer interrupt Test
last 3 HIGH LOW
last 2 HIGH LOW
last 1 HIGH LOW
END
Alarm interrupt Test
last 3 HIGH LOW
last 2 HIGH LOW
last 1 HIGH LOW
END TEST
以下は”RTC_8564.cpp”と”RTC_8564.h” です。
//
// RTC_8564.cpp
//
#include <Wire.h>
#include "RTC_8564.h"
void init_RTC_8564(time_data t_data)
{
Wire.beginTransmission(RTC_8564_ADDR);
Wire.write(RTC_8564_CONT1);
Wire.write(RTC_8564_STOP);
Wire.write(0x00);
Wire.write(t_data.second);
Wire.write(t_data.minute);
Wire.write(t_data.hour);
Wire.write(t_data.day);
Wire.write(t_data.weekday);
Wire.write(t_data.month);
Wire.write(t_data.year);
Wire.write(0x80);
Wire.write(0x80);
Wire.write(0x80);
Wire.write(0x80);
Wire.write(0x00);
Wire.write(0x00);
Wire.endTransmission();
Write_RTC_8564(RTC_8564_CONT1,RTC_8564_START);
}
uint8_t chg_BCD_num(uint8_t t_data)
{
return(((t_data & 0xf0) >> 4) * 10 + (t_data & 0x0f));
}
uint8_t chg_num_BCD(uint8_t t_data)
{
return(((t_data / 10) << 4 ) + (t_data % 10));
}
void set_RTC_time(time_data t_data)
{
Write_RTC_8564(RTC_8564_CONT1,RTC_8564_STOP);
Wire.beginTransmission(RTC_8564_ADDR);
Wire.write(RTC_8564_SECOND);
Wire.write(t_data.second);
Wire.write(t_data.minute);
Wire.write(t_data.hour);
Wire.write(t_data.day);
Wire.write(t_data.weekday);
Wire.write(t_data.month);
Wire.write(t_data.year);
Wire.endTransmission();
Write_RTC_8564(RTC_8564_CONT1,RTC_8564_START);
}
void get_RTC_time(time_data* p_data)
{
Wire.beginTransmission(RTC_8564_ADDR);
Wire.write(RTC_8564_SECOND);
Wire.endTransmission();
Wire.requestFrom(RTC_8564_ADDR,7);
p_data->second = Wire.read() & 0x7f;
p_data->minute = Wire.read() & 0x7f;
p_data->hour = Wire.read() & 0x3f;
p_data->day = Wire.read() & 0x3f;
p_data->weekday = Wire.read() & 0x7;
p_data->month = Wire.read() & 0x1f;
p_data->year = Wire.read();
}
void Write_RTC_8564(uint8_t cmd, uint8_t rt_data)
{
Wire.beginTransmission(RTC_8564_ADDR);
Wire.write(cmd);
Wire.write(rt_data);
Wire.endTransmission();
}
uint8_t Read_RTC_8564(uint8_t cmd)
{
Wire.beginTransmission(RTC_8564_ADDR);
Wire.write(cmd);
Wire.endTransmission();
Wire.requestFrom(RTC_8564_ADDR,1);
return(Wire.read());
}
//
// RTC_8564.h
//
#include <Wire.h>
// I2C Address
#define RTC_8564_ADDR 0x51 // FIX
// Registers
#define RTC_8564_CONT1 0x00
#define RTC_8564_CONT2 0x01
#define RTC_8564_SECOND 0x02
#define RTC_8564_MINUTE 0x03
#define RTC_8564_HOUR 0x04
#define RTC_8564_DAY 0x05
#define RTC_8564_WEEKDAY 0x06
#define RTC_8564_MONTH_CENTURY 0x07
#define RTC_8564_YEAR 0x08
#define RTC_8564_MINUTE_ALARM 0x09
#define RTC_8564_HOUR_ALARM 0x0a
#define RTC_8564_DAY_ALARM 0x0b
#define RTC_8564_WEEKDAY_ALARM 0x0c
#define RTC_8564_CLKOUT_FRQ 0x0d
#define RTC_8564_TIMER_CONT 0x0e
#define RTC_8564_TIMER_NUM 0x0f
// Timer Start & Stop
#define RTC_8564_STOP 0x20
#define RTC_8564_START 0x00
// Interrupt Flag
#define RTC_8564_TF_BIT 0x04
#define RTC_8564_AF_BIT 0x08
// Timer Frequency
#define RTC_8564_TFrq_244us 0x00
#define RTC_8564_TFrq_15ms 0x01
#define RTC_8564_TFrq_1sec 0x02
#define RTC_8564_TFrq_1min 0x03
// Structure of Timer
typedef struct Time_data
{
uint8_t year;
uint8_t month;
uint8_t weekday;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
} time_data;
//------------------------------------------------
// Initial setting
// time_data t_data: Time data
//------------------------------------------------
void init_RTC_8564(time_data t_data);
//------------------------------------------------
// Convert BCD to Number
// uint8_t t_data: BCD data
//
// Return: Number (uint8_t)
//------------------------------------------------
uint8_t chg_BCD_num(uint8_t t_data);
//------------------------------------------------
// Convert Number to BCD
// uint8_t t_data: Number data
//
// Return: BCD (uint8_t)
//------------------------------------------------
uint8_t chg_num_BCD(uint8_t t_data);
//------------------------------------------------
// Set TIME
// time_data t_data: TIME data
//------------------------------------------------
void set_RTC_time(time_data t_data);
//------------------------------------------------
// Get TIME
// time_data* p_data: Pointer of TIME data
//------------------------------------------------
void get_RTC_time(time_data* p_data);
//------------------------------------------------
// Write RTC8564 Register
// uint8_t cmd: Register NO.
// uint8_t rt_data: Write Data
//------------------------------------------------
void Write_RTC_8564(uint8_t cmd, uint8_t rt_data);
//------------------------------------------------
// Read RTC8564 Register
// uint8_t cmd: Register NO.
//
// Return: Register Data
//------------------------------------------------
uint8_t Read_RTC_8564(uint8_t cmd);
最後に
最近RTC機能を持った素子は多いです。この素子の存在価値が薄れているように思えますが、このRTC-8564は単体の消費電力が非常に少なく、ボタン電池で簡単にバックアップ出来ます。本体の電源を落としても時間を保持したい時に役立ちます。