RTC-8564NB(リアルタイムクロック)(for Arduino)

秋月でリアルタイムクロック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を使用します。

レジスタへの書き込みは、レジスタカウンタにレジスタ番号を指定した後、データを送信して行われます。

  1.  I2Cアドレスを指定。
  2.  書き込みたいレジスター番号を指定。
  3.  書き込みたいデータの送信。 
  4.  終了処理。 

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();
}

レジスタからの読み込みは、レジスタカウンタにレジスタ番号を指定した後、読み込みたいデータ数を送信した後データを読み込みます。

  1.  I2Cアドレスを指定。
  2.  読み込みたいレジスター番号を指定。
  3.  一度送信を終了。
  4.  読み込みたいレジスタの数を送信。
  5.  送信したレジスタの数だけレジスタを読み込む。 

書き込みと違うところは、レジスタ番号を送ってから一度送信を終了し、後に読み込みたいレジスタの数を指定する点です。

下記はレジスタ番号(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は単体の消費電力が非常に少なく、ボタン電池で簡単にバックアップ出来ます。本体の電源を落としても時間を保持したい時に役立ちます。

ここに今回のプロジェクトを保存します