RTC-8564NBでうるう年の実験。
前に作った、タイマーなんですが、

やっぱ、50日に一回具合悪くなるので、リアルタイムクロック化して安定させるべく、
以前購入していた、秋月のRTC-8564NBの動作確認テストの記事です。
 
■回路
 

 
■テスト
 
ちゃんと明日になるかな?
ver0.1
  2021  4/17 23:59:55
  2021  4/17 23:59:56
  2021  4/17 23:59:57
  2021  4/17 23:59:58
  2021  4/17 23:59:59
  2021  4/18  0:00:00 ← ちゃんと明日になった。
  2021  4/18  0:00:01
  2021  4/18  0:00:02
  2021  4/18  0:00:03
  2021  4/18  0:00:04
  2021  4/18  0:00:05
■うるう年の確認
    国立天文台の定義によると
    「グレゴリオ暦法では、うるう年を次のように決めています。
    (1)西暦年号が4で割り切れる年をうるう年とする。
    (2)(1)の例外として、西暦年号が100で割り切れて    400で割り切れない年は平年とする。」
    さらに
    「例えば、西暦2004年、2008年、2012年……は(1)に当てはまりますので、
    うるう年になります。また、西暦2100年、2200年、2300年は(2)に当てはまりますので、
    平年となります。さらに、西暦2000年、2400年は、100でも割り切れますが400でも
    割り切れてしまいますので、(2)には当てはまらず、(1)のとおりにうるう年となります。」
    
つまり、直近だと
2020年はうるう年、2024年もうるう年
2000年は100でも400でも割り切れるのでうるう年
かなり未来の2100年は4で割り切れるけど100でも割り切れるのでうるう年じゃない
ってなりますけど。
さて、試してみましょう。
 
今年。2021年2月
ver0.1
  2021  2/28 23:59:55
  2021  2/28 23:59:56
  2021  2/28 23:59:57
  2021  2/28 23:59:58
  2021  2/28 23:59:59 ←うるう年でない。
  2021  3/ 1  0:00:00
  2021  3/ 1  0:00:01
  2021  3/ 1  0:00:02
2020年2月
ver0.1
  2020  2/28 23:59:55
  2020  2/28 23:59:56
  2020  2/28 23:59:57
  2020  2/28 23:59:58
  2020  2/28 23:59:59
  2020  2/29  0:00:00 ←うるう年。
  2020  2/29  0:00:01
  2020  2/29  0:00:02
  2020  2/29  0:00:03
  2020  2/29  0:00:04
2024年2月
ver0.1
  2024  2/28 23:59:55
  2024  2/28 23:59:56
  2024  2/28 23:59:57
  2024  2/28 23:59:58
  2024  2/28 23:59:59
  2024  2/29  0:00:00 ←うるう年。
  2024  2/29  0:00:01
  2024  2/29  0:00:02
  2024  2/29  0:00:03
  2024  2/29  0:00:04
2000年2月
ver0.1
  2000  2/28 23:59:55
  2000  2/28 23:59:56
  2000  2/28 23:59:57
  2000  2/28 23:59:58
  2000  2/28 23:59:59
  2000  2/29  0:00:00 ←うるう年。
  2000  2/29  0:00:01
  2000  2/29  0:00:02
  2000  2/29  0:00:03
  2000  2/29  0:00:04
  2000  2/29  0:00:05
2100年2月
ver0.1
  2000  2/28 23:59:55    ←年がばぐってるね。
  2000  2/28 23:59:56
  2000  2/28 23:59:57
  2000  2/28 23:59:58
  2000  2/28 23:59:59
  2000  2/29  0:00:00    ←うるう年になっているけど。不正解!!
  2000  2/29  0:00:01
  2000  2/29  0:00:02
  2000  2/29  0:00:03
  
これって、2099年の次は2000年に戻るっていうやつだね。
まさこれは2000年問題!!
懐かしい。
そうそう。1999年の年末は世間が浮かれている中
我々はデータセンターで年越ししたものです。
 
やっぱ、2100年問題とかなるのかね?
いちおう、試してみよう。2100年問題。
 
2099年年末
ver0.1
  2099 12/31 23:59:55
  2099 12/31 23:59:56
  2099 12/31 23:59:57
  2099 12/31 23:59:58
  2099 12/31 23:59:59
  2000  1/ 1  0:00:00 ←ちゃんと?2000年に戻っている。
  2000  1/ 1  0:00:01
  2000  1/ 1  0:00:02
  2000  1/ 1  0:00:03
 
このモジュール使う人は2100年問題に気をつけましょう。
原因は年を二桁だけで管理しているためです。
0~99までの範囲をふたつに分けて
 0~50までは2100を加算
 51~99までは2000を加算とすれば問題なくなるのでいちおう、
 2099年まではプログラムを更新して使えるはずですけど
 2100年は「うるう年じゃない」の扱い出来るようにしなきゃですね。
 
※いちおうこのモジュール、センチュリービットなるビットを立てて
 2000年以降を識別できるようですが、2100年以降は識別できるかな?
 ここから先は未来のエンジニアにお任せですね。
 
■プログラム
 
ここらへんhttp://www.straycats.net/wifky/wifky.cgi?p=Arduino+%E3%83%AA%E3%82%A2%E3%83%AB%E3%82%BF%E3%82%A4%E3%83%A0%E3%82%AF%E3%83%AD%E3%83%83%E3%82%AF%E3%80%80RTC-8564NB
を参考にさせてもらいましたが、
コンパイルエラーになるでの、色々変更しました。
ライブラリーもあっちこっち色々あるみたいであれなんで、
一本のプログラムとして合体しちゃいましたので、
ライブラリーの方言?は気にしなくて良くなるはずです。

#include <Arduino.h>
#include <Wire.h>

#define BCD2Decimal(x)  (((x>>4)*10)+(x&0xf))
#define Decimal2BCD(x)          (((x/10)<<4)+(x%10))

extern "C" {
  #include <stdlib.h>
  #include <string.h>
  #include <inttypes.h>
}

#include <inttypes.h>

class RTC8564
{
private:
  void init(void);
  uint8_t _seconds;
  uint8_t _minutes;
  uint8_t _hours;
  uint8_t _days;
  uint8_t _weekdays;
  uint8_t _months;
  uint8_t _years;
  bool  _century;
  
public:
  enum {
    BCD = 0,
    Decimal = 1,
  };
  RTC8564();
  void begin(void);
  void sync(uint8_t date_time[],uint8_t size = 7);
  bool available(void);
  bool isvalid(void);
  uint8_t seconds(uint8_t format = RTC8564::BCD) const;
  uint8_t minutes(uint8_t format = RTC8564::BCD) const;
  uint8_t hours(uint8_t format = RTC8564::BCD) const;
  uint8_t days(uint8_t format = RTC8564::BCD) const;
  uint8_t weekdays() const;
  uint8_t months(uint8_t format = RTC8564::BCD) const;
  uint8_t years(uint8_t format = RTC8564::BCD) const;
  bool century() const;
};

#define RTC8564_SLAVE_ADRS  (0xA2 >> 1)
#define BCD2Decimal(x)    (((x>>4)*10)+(x&0xf))

RTC8564::RTC8564()
  : _seconds(0), _minutes(0), _hours(0), _days(0), _weekdays(0), _months(0), _years(0), _century(0)
{
}

void RTC8564::init(void)
{
  delay(1000);
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x00);     // write reg addr 00
  Wire.write(0x20);     // 00 Control 1, STOP=1
  Wire.write(0x00);     // 01 Control 2
  Wire.write(0x00);     // 02 Seconds
  Wire.write(0x00);     // 03 Minutes
  Wire.write(0x09);     // 04 Hours
  Wire.write(0x01);     // 05 Days
  Wire.write(0x01);     // 06 Weekdays
  Wire.write(0x01);     // 07 Months
  Wire.write(0x01);     // 08 Years
  Wire.write(0x00);     // 09 Minutes Alarm
  Wire.write(0x00);     // 0A Hours Alarm
  Wire.write(0x00);     // 0B Days Alarm
  Wire.write(0x00);     // 0C Weekdays Alarm
  Wire.write(0x00);     // 0D CLKOUT
  Wire.write(0x00);     // 0E Timer control
  Wire.write(0x00);     // 0F Timer
  Wire.write(0x00);     // 00 Control 1, STOP=0
  Wire.endTransmission();
}

// Public Methods //////////////////////////////////////////////////////////////

void RTC8564::begin(void)
{
  Wire.begin();
  if(isvalid() == false)
    init();
}

void RTC8564::sync(uint8_t date_time[],uint8_t size)
{
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x00);     // write reg addr 00
  Wire.write(0x20);     // 00 Control 1, STOP=1
  Wire.endTransmission();
  
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x02);     // write reg addr 02
  Wire.write(date_time, size);
  Wire.endTransmission();
  
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x00);     // write reg addr 00
  Wire.write(0x00);     // 00 Control 1, STOP=0
  Wire.endTransmission();
}

bool RTC8564::available(void)
{
  uint8_t buff[7];
  
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x02);     // write reg addr 02
  Wire.endTransmission();
  
  Wire.requestFrom(RTC8564_SLAVE_ADRS, 7);
  
  for(int i=0; i<7; i++){
    if(Wire.available()){
      buff[i] = Wire.read();
    }
  }
  
  _seconds  = buff[0] & 0x7f;
  _minutes  = buff[1] & 0x7f;
  _hours    = buff[2] & 0x3f;
  _days   = buff[3] & 0x3f;
  _weekdays = buff[4] & 0x07;
  _months   = buff[5] & 0x1f;
  _years    = buff[6];
  _century  = (buff[5] & 0x80) ? 1 : 0;
  return (buff[0] & 0x80 ? false : true);
}

bool RTC8564::isvalid(void)
{
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x02);     // write reg addr 02
  Wire.endTransmission();
  Wire.requestFrom(RTC8564_SLAVE_ADRS, 1);
  if(Wire.available()){
    uint8_t buff = Wire.read();
    return (buff & 0x80 ? false : true);
  }
  return false;
}

uint8_t RTC8564::seconds(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_seconds);
  return _seconds;
}

uint8_t RTC8564::minutes(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_minutes);
  return _minutes;
}

uint8_t RTC8564::hours(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_hours);
  return _hours;
}

uint8_t RTC8564::days(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_days);
  return _days;
}

uint8_t RTC8564::weekdays() const {
  return _weekdays;
}

uint8_t RTC8564::months(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_months);
  return _months;
}

uint8_t RTC8564::years(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_years);
  return _years;
}

bool RTC8564::century() const {
  return _century;
}


// Preinstantiate Objects //////////////////////////////////////////////////////

RTC8564 Rtc = RTC8564();


int yearDecimal2BCD(int yyyy){
 
  int nDec = yyyy -1900;
  int nDecHigh = nDec / 100;
  int nDecLow  = nDec % 100;
  int bcdHigh = Decimal2BCD(nDecHigh);
  int bcdLow  = Decimal2BCD(nDecLow);
  int nBCD = bcdHigh * 256 + bcdLow;

  return nBCD;
}


void setup()
{
  Serial.begin(9600);
  Serial.println("ver0.1");

  Rtc.begin();
  
  //setDateTime(2021, 4, 17, 6, 23, 59, 55);
  //setDateTime(2021, 2, 28, 0, 23, 59, 55);
  //setDateTime(2020, 2, 28, 0, 23, 59, 55);
  //setDateTime(2024, 2, 28, 0, 23, 59, 55);
  //setDateTime(2000, 2, 28, 0, 23, 59, 55);
  //setDateTime(2100, 2, 28, 0, 23, 59, 55);
  setDateTime(2099, 12, 31, 0, 23, 59, 55);
}

void loop()
{
  char buf[256];
  
  Rtc.available();    //取得準備
  sprintf(buf, "  %04d %2d/%2d %2d:%02d:%02d",
      Rtc.years(RTC8564::Decimal)+2000,
      Rtc.months(RTC8564::Decimal),
      Rtc.days(RTC8564::Decimal),
      Rtc.hours(RTC8564::Decimal),
      Rtc.minutes(RTC8564::Decimal),
      Rtc.seconds(RTC8564::Decimal) );
  
  Serial.println(buf);
  
  delay(1000);
      

}

void setDateTime(int yyyy, int mm, int dd, int you, int hour, int min, int sec)
{
 
 
  byte date_and_time[7];
  date_and_time[0] = Decimal2BCD(sec); // 59秒
  date_and_time[1] = Decimal2BCD(min); // 59分
  date_and_time[2] = Decimal2BCD(hour); // 24時
  date_and_time[3] = Decimal2BCD(dd); // 31日
  date_and_time[4] = you; // 曜日 日=0,月=1,火=2,水=3,木=4,金=5,土=6,
  date_and_time[5] = Decimal2BCD(mm); // 12月
//  date_and_time[6] = 0x116;  //Decimal2BCD(yyyy-2000); // 年
  date_and_time[6] = yearDecimal2BCD( yyyy);  //年

  Rtc.sync(date_and_time);

}