リアルタイムクロック
UNO

概要

RTC-8564NBというリアルタイムクロックを使った実験です。実際には、秋月電子通商で販売しているリアルタイムクロックモジュールを使った実験です。

Arduinoは内蔵カレンダーを持っていないので、日時や時刻が必要な場合に利用することができます。

目的

RTC-8564NBの全ての機能をArduinoから使えるようにします。

データシート

データシートからわかることをまとめます。

ピン配置

RTC-8564NBは、ピン間隔0.5mmのチップですが、秋月電子通商で販売しているリアルタイムクロックモジュールはこのチップを8ピンのDIPにしたもので、使いやすくなっています。

各ピンの意味は以下の通りです。

ピン番号 記号 意味
1 CLKOE 後述するFEビット、FD1ビット、FD0ビットと組み合わせて使用し、CLKOUT出力状態を制御します。
2 CLKOUT クロック出力端子。C-MOS出力です。
3 /INT アラームと定周期タイマ割り込みの割り込み信号を出力します。オープンドレイン端子です。
4 VSS アース端子。
5 SDA I2C通信用の信号線です。
6 SCL I2C通信用のシリアルクロック入力です。
7 N.C. 未使用。
8 VDD +5Vに接続します。

機能

まずは、データシートから、RTC-8564NBを使って何ができるかを調べます。大きく以下の4種類の機能があります。

項番 機能 概要
1 時計機能 西暦(二桁)・月・日・曜・時・分・秒の設定と読み出しが可能。
2 定周期タイマ割り込み機能 244.14μ秒から255分後に割り込みを発生させる。定周期で繰り返し割り込みを発せさせることも可能です。割り込み発生時に/INTにLOWを出力することも可能。
3 アラーム割り込み機能 月・日・曜・時・分に対する割り込みを発生させる。割り込み発生時に/INTにLOWを出力することも可能。
4 クロック出力機能 32.768kHz、1024Hz, 32Hz, 1HzのCMOS出力が可能。クロックは2番ピンから出力されます。

これらの機能は、チップに内蔵されているレジスタを設定することにより実現可能です。このチップはI2Cで制御するので、ArduinoではWireライブラリを用いて制御することができます。

各機能を実現するレジスタ

各機能を実現するためのレジスタの操作方法を調べます。レジスタを示す表の中で、0は実行時には0を設定して利用するビット、xは書き込み不可・読み出し値不定のビットです。

コントロールレジスタ

コントロールレジスタは、機能の起動・停止や、イベントの動作設定、イベント発生状況の把握を行うためのレジスタです。Control1とControl2の2種類があります。

アドレス 機能 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
0x00 Control 1 TEST 0 STOP 0 TEST 0 0 0
0x01 Control 2 0 x 0 TI/IP AF TF AIE TIE
TEST
チップメーカーのテスト用ビットです。利用時は0を設定します。
STOP
1のときは、時計などの機能が停止(カウント停止)します。
TI/IP(Interrupt Signal Output Mode Select. Interrupt/ Periodic)
1のときは、定周期タイマ割り込みイベント発生時に、その動作を繰り返します。0のときは、1回だけ動作します。
AF(Alarm Flag)
アラーム割り込みイベントが発生すると1になります。自動では0に戻りません(自分で設定する必要があります)。
TF(Timer Flag)
定周期タイマイベントが発生すると1になります。自動では0に戻りません(自分で設定する必要があります)。
AIE(Alarm Interrupt Enable)
1のときはアラーム割り込みイベント発生時に、/INT端子にLOWが出力されます。
TIE(Timer Interrupt Enable)
1のときは定周期割り込みイベント発生時に、/INT端子にLOWが出力されます。

時計・カレンダー機能を制御するためのレジスタ

年月日時分秒曜日を保持するためのレジスタ群です。

アドレス 機能 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
0x02 Seconds VL 40 20 10 8 4 2 1
0x03 Minutes x 40 20 10 8 4 2 1
0x04 Hours x x 20 10 8 4 2 1
0x05 Days x x 20 10 8 4 2 1
0x06 Weekdays x x x x x 4 2 1
0x07 Months/Century C x x 10 8 4 2 1
0x08 Years 80 40 20 10 8 4 2 1
VL(Voltage Low Flag)
1のときは電圧低下を検出した場合です。この場合はすべてのレジスタを初期設定する必要があります。
C(Century Bit)
Cビットが0のとき、年の桁が99から00にオーバーフローすると1になります。1のときは、年の桁がオーバーフローすると0に戻ります。

Seconds/Minutes/Hours/Days/Months/Yearsは、BCD形式で値を保持しています。上位4ビットと下位4ビットがそれぞれ10進数の10の位と1の位を表します。例えば、Yearsレジスタが"0001 0011"のときは、上位4ビットが1、下位4ビットが3なので、13となります。16進表記した値を10進のまま読むことと同等です。

Weekdaysは0から6まで値をとり、それぞれ日曜日から土曜日に対応します。

上記のレジスタ群は読み書き可能なレジスタです。読み込みは時刻の取得、書き込みは時刻の設定です。時刻を設定する際は、コントロールレジスタControl1のSTOPビットを1にして、時刻の更新を停止させます。設定後、STOPビットを0に戻して時刻の更新を再開します。

定周期タイマ割り込み機能を制御するためのレジスタ

定周期タイマ割り込み機能を制御するためのレジスタ群です。

アドレス 機能 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
0x01 Control 2 0 x 0 TI/IP AF TF AIE TIE
0x0e Timer Control TE x x x x x TD1 TD0
0x0f Timer 128 64 32 16 8 4 2 1
TI/IP(Interrupt Signal Output Mode Select. Interrupt/ Periodic)
1のときは、定周期タイマ割り込みイベント発生時に、その動作を繰り返します。0のときは、1回だけ動作します。
TF(Timer Flag)
定周期タイマイベントが発生すると1になります。自動では0に戻りません(自分で設定する必要があります)。
TIE(Timer Interrupt Enable)
1のときは定周期割り込みイベント発生時に、/INT端子にLOWが出力されます。
TE(Timer Enable)
1のときは定周期割り込み機能が動作します。0のときは停止します。
TD1、TD0
定周期割り込みのソースクロックを指定します。また、TI/IPが1のときの/INTへLOWを出力時間はTD1とTD0、Timerレジスタの設定値の組み合わせで決まります。

TD1とTD0の組み合わせによるソースクロックの値、TI/IPが1のときの/INTへのLOW出力時間は以下の通りです。

TD1 TD0 周波数 周期 /INT出力時間(Timer=1 ) /INT出力時間(Timer>1)
0 0 4096Hz 244.14μs 122μs 244μs
0 1 64Hz 15.625ms 7.813ms 15.625ms
1 0 1Hz 1秒(1秒桁更新時)(※) 15.625ms 15.625ms
1 1 1/60Hz 1分(1分桁更新時))(※) 15.625ms 15.625ms

(※)時計の秒もしくは分の更新と同期します。このため、最初の1周期が設定より短い時間で割り込みが発生します。

Timerは、設定するタイマ値(16進)です。例えば、TD1=1、TD0=0で、Timerが0x0aと設定すると、1Hz x 10=10秒で割り込みが発生します。さらにControl2のTI/IPが1であれば、10秒ごとに割り込みが発生します。

アラーム割り込み機能を制御するためのレジスタ

アラーム割り込み機能を制御するためのレジスタ群です。

アドレス 機能 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
0x01 Control 2 0 x 0 TI/IP AF TF AIE TIE
0x09 Minute Alarm AE 40 20 10 8 4 2 1
0x0a Hour Alarm AE x 20 10 8 4 2 1
0x0b Day Alarm AE x 20 10 8 4 2 1
0x0c Weekday Alarm AE x x x x 4 2 1
AF(Alarm Flag)
アラーム割り込みベントが発生すると1になります。自動では0に戻りません(自分で設定する必要があります)。
AIE(Alaram Interrupt Enable)
1のときはアラーム割り込みイベント発生時に、/INT端子にLOWが出力されます。
AE
AEが1のときは、そのレジスタは比較対象とはなりません。例えば、毎時1分にアラーム割り込みを発生させたいときは、Hour/Day/WeekdayのAEを1にし、MinuteのAEだけを0にします。ただし、全てのAEを1にすると、アラーム割り込みイベントは発生しません。

Minute Alarm/Hour Alarm/Day Alarmは、AEビットを除いて、BCD形式で値を保持しています。

Weekday Alarmは、AEビットを除いて、0から6まで値をとり、それぞれ日曜日から土曜日に対応します。

クロック出力機能を制御するためのレジスタ

クロック出力機能を制御するためのレジスタです。

アドレス 機能 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
0x0d CLKOUT Frequency FE x x x x x FD1 FD0
FE(Frequency output Enable)
1のときはクロック出力機能を有効になります。
FD1、FD0
2つのビットの組み合わせにより出力する周波数を決定します。

クロック周波数は以下の通りです。

FD1 FD0 周波数
0 0 32768Hz
0 1 1024Hz
1 0 32Hz
1 1 1Hz

クロック出力は、CLKOEピンがHIGHのときだけ有効となります。LOWのときはレジスタの設定に関係なく無効です。

Arduinoからの操作

レジスタの読み書き

Arduinoからチップのレジスタへの読み書きは、以下のように行います。このチップのスレーブアドレスは0x51です。

レジスタからの読み込み

レジスタから値を読み込む手順は以下の通りです。

  1. Wire.beginTransmission(スレーブアドレス)
  2. Wire.write(レジスタのアドレス)
  3. Wire.endTransmision()
  4. Wire.requestFrom(スレーブアドレス, 読み込むバイト数)
  5. Wire.read() (上記で指定したバイト数文繰り返し)

レジスタのアドレスを書き込んだ後、データを1バイトずつ読み込みます。2度目以降のWire.read()では、チップが自動的にアドレスをインクリメントするので、連続したレジスタから値を読み込むことができます。 レジスタへの書き込み

レジスタに値を書き込む手順は以下の通りです。

  1. Wire.beginTransmission(スレーブアドレス)
  2. Wire.write(レジスタのアドレス)
  3. Wire.write(データ、書き込むデータの個数)
  4. Wire.endTransmission()

レジスタのアドレスを書き込んだ後、続けてデータを1バイトずつ書き込みます。複数のデータを書き込む場合は、チップが自動的にアドレスをインクリメントするので、連続したレジスタに値を書き込むことができます。

ライブラリ

RTC8545をArduinoから利用するためのライブラリを作成しました。RTC8564というオブジェクトを作成します。

データ型

時刻とアラームを表すために以下の2つのデータ型を定義しています。POSIXのstruct tm型に似せていますが、月が0からではなく1から始まるところが違います。Arduinoで、月の名前を配列に入れて利用するケースがあまりなさそうなので、日本語としてわかりやすいようにしました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
struct dateTime {
  uint8_t second;             // 0-59
  uint8_t minute;             // 0-59
  uint8_t hour;               // 0-23
  uint8_t day;                // 1-31
  uint8_t month;              // 1-12
  uint8_t year;               // 00-199
  uint8_t weekday;            // 0(Sun)-6(Sat)
};
 
struct alarmTime {
  uint8_t minute;             // 0-59
  uint8_t hour;               // 0-23
  uint8_t day;                // 1-31
  uint8_t weekday;            // 0(Sun)-6(Sat)
};

API(メソッド)

以下のAPIを実装しました。

項番 API(シグネチャ) 説明 備考
1 void begin() オブジェクトを初期化します。カレンダーを13年1月1日(火)0時0分0秒に設定します。
2 void begin(struct dateTime *dt) オブジェクトを初期化します。dtに格納したカレンダーを設定します。 dtはポインタ渡しです。
3 void setDateTime(struct dateTime *dt) カレンダーを設定します。 dtはポインタ渡しです。
4 int getDateTime(struct dateTime *dt) カレンダーを取得します。VLフラグが1のときは-1を返すので、カレンダーを再設定する必要があります。 dtはポインタ渡しです。dtの領域は呼び出し側が確保する必要があります。
5 void setAlarm(uint8_t enableFlag, struct alarmTime *at, uint8_t interruptEnable) アラーム割り込みを設定します。enableFlagsを設定した要素がアラーム対象となります。interruptEnableをtrueにすると、アラーム割り込み発生時に/INTがLOWになります。 atはポインタ渡しです。
6 void getAlarm(uint8_t *enableFlags, struct alarmTime *at) アラーム情報を取得します。 atはポインタ渡しです。atの領域は呼び出し側が確保する必要があります。
7 int getAlarmFlag() アラームフラグを取得します。
8 void clearAlarmFlag() アラームフラグをクリアします。
9 void setTimer(uint8_t enableFlag, uint8_t repeatMode, uint8_t clockMode, uint8_t counter, uint8_t interruptEnable) タイマー割り込みを設定します。
10 int getTimerFlag() タイマーフラグを取得します。
11 void clearTimerFlag() タイマーフラグをクリアします。
12 void setClkoutFrequency(uint8_t enableFlag, uint8_t flag) CLKOUTに出力する周波数を設定します。

Arduinoとの接続

サンプルスケッチを動作させるには、以下の通りArduinoとRTC8564モジュールを接続します。

RTC8564のピン番号 Arduinoのピン番号 意味
1 - CLKOE
2 - CLKOUT
3 12 /INT
4 GND VSS
5 A4 SDA
6 A5 SCL
7 - N.C.
8 +5V VDD

スケッチ

今回の実験に使用したスケッチを以下に示します。GitHubのリポジトリは、こちら

sample.ino

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <Wire.h>
#include "RTC8564.h"
 
int interruptPin = 12;
 
void setup() {
  struct dateTime dt = {0, 0, 0, 1, 1, 13, 2};
  struct alarmTime at = {1, 0, 1, 2};
  char s[20];
  uint8_t flags;
 
  pinMode(interruptPin, INPUT_PULLUP);
  Serial.begin(9600);
  RTC8564.begin(&dt);
  RTC8564.setAlarm(RTC8564_AE_MINUTE, &at, 1);
  RTC8564.setTimer(true, false, RTC8564_CLK_1SEC, 5, true);
}
 
void loop() {
  struct dateTime dt;
  char s[20];
  uint8_t data;
   
  if (RTC8564.getDateTime(&dt) == 0) {
    sprintf(s, "%4d/%02d/%02d %02d:%02d:%02d ", 
      dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
    Serial.print(s);
  } else {
    RTC8564.begin();
    Serial.println("Detected voltage low");
  }
   
  if (digitalRead(interruptPin)) {
    Serial.print("HIGH ");
  } else {
    Serial.print("LOW  ");
  }
 
  if (RTC8564.getAlarmFlag()) {
    Serial.print("AF=ON,  ");
    RTC8564.clearAlarmFlag();
  } else {
    Serial.print("AF=OFF, ");
  }
   
  if (RTC8564.getTimerFlag()) {
    Serial.print("TF=ON,  ");
    RTC8564.clearTimerFlag();
  } else {
    Serial.print("TF=OFF, ");
  }
 
  Serial.println();
  delay(1000);
}

RTC8564.h

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#ifndef RTC8564_h
#define RTC8564_h
 
#include <Arduino.h>
#include <Wire.h>
 
// I2C Address
#define RTCS8564_I2C_ADDRESS      0x51
 
// Registers
#define RTC8564_CONTROL1           0x00
#define RTC8564_CONTROL2           0x01
#define RTC8564_SECONDS            0x02
#define RTC8564_MINUTES            0x03
#define RTC8564_HOURS              0x04
#define RTC8564_DAYS               0x05
#define RTC8564_WEEKDAYS           0x06
#define RTC8564_MONTH_CENTURY      0x07
#define RTC8564_YEARS              0x08
#define RTC8564_MINUTE_ALARM       0x09
#define RTC8564_HOUR_ALARM         0x0a
#define RTC8564_DAY_ALARM          0x0b
#define RTC8564_WEEKDAY_ALARM      0x0c
#define RTC8564_CLKOUT_FREQUENCY   0x0d
#define RTC8564_TIMER_CONTROL      0x0e
#define RTC8564_TIMER              0x0f
 
// Control1 register
#define RTC8564_STOP_BIT           0x20
 
// Control2 register
#define RTC8564_TIE_BIT            0x01
#define RTC8564_AIE_BIT            0x02
#define RTC8564_TF_BIT             0x04
#define RTC8564_AF_BIT             0x08
#define RTC8564_TITP_BIT           0x10
 
// Calendar registers
#define RTCS8564_CAL_VL            0x80
#define RTCS8564_CAL_CENTURY       0x80
 
// Alarm register
#define RTC8564_AE_NONE            0x00
#define RTC8564_AE_MINUTE          0x01
#define RTC8564_AE_HOUR            0x02
#define RTC8564_AE_DAY             0x04
#define RTC8564_AE_WEEKDAY         0x08
#define RTC8564_AE_ALL             (RTC8564_AE_MINUTE | RTC8564_AE_HOUR | RTC8564_AE_DAY | RTC8564_AE_WEEKDAY)
#define RTC8564_AE_BIT             0x80
 
// Timer control register
#define RTC8564_CLK_244US          0x00
#define RTC8564_CLK_15MS           0x01
#define RTC8564_CLK_1SEC           0x02
#define RTC8564_CLK_1MIN           0x03
#define RTC8564_TE_BIT             0x80
 
// Clkout frequency register
#define RTC8564_CLKOUT_32768HZ     0x00
#define RTC8564_CLKOUT_1024HZ      0x01
#define RTC8564_CLKOUT_32HZ        0x02
#define RTC8564_CLKOUT_1HZ         0x03
#define RTCS8564_FE_BIT            0x80
 
// date and time
struct dateTime {
  uint8_t second;             // 0-59
  uint8_t minute;             // 0-59
  uint8_t hour;               // 0-23
  uint8_t day;                // 1-31
  uint8_t month;              // 1-12
  uint8_t year;               // 00-199
  uint8_t weekday;            // 0(Sun)-6(Sat)
};
 
// alarm
struct alarmTime {
  uint8_t minute;             // 0-59
  uint8_t hour;               // 0-23
  uint8_t day;                // 1-31
  uint8_t weekday;            // 0(Sun)-6(Sat)
};
 
class RTC8564Class {
  public:
    // Initialization functions
    void begin();
    void begin(struct dateTime *dt);
     
    // Date and time handling functions
    void setDateTime(struct dateTime *dt);
    int getDateTime(struct dateTime *dt);
     
    // Alarm handring functions
    void setAlarm(uint8_t enableFlags, struct alarmTime *at, uint8_t interruptEnable);
    void getAlarm(uint8_t *enableFlags, struct alarmTime *at);
    int getAlarmFlag();
    void clearAlarmFlag();
     
    // Timer handring functions
    void setTimer(uint8_t enableFlag, uint8_t repeatMode, uint8_t clockMode, uint8_t counter, uint8_t interruptEnable);
    int getTimerFlag();
    void clearTimerFlag();
     
    // Clkout handling functions
    void setClkoutFrequency(uint8_t enableFlag, uint8_t flag);
 
  private:
    int slaveAddress;
 
    int decimalToBCD(int decimal);
    int BCDToDecimal(int bcd);
    int waitForData();
    void setRegisters(uint8_t address, int numData, uint8_t *data);
    void getRegisters(uint8_t address, int numData, uint8_t *data);
};
 
extern RTC8564Class RTC8564;
 
#endif

RTC8564.cpp

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include "RTC8564.h"
#include <Wire.h>
 
void RTC8564Class::begin() {
  struct dateTime dt = {0, 0, 0, 1, 1, 13, 2};  // 2013 January 1st, Tue 00:00:50
 
  begin(&dt);
};
 
void RTC8564Class::begin(struct dateTime *dt) {
  uint8_t control[2];
  struct alarmTime at = {0, 0, 0, 0};
 
  slaveAddress = RTCS8564_I2C_ADDRESS;
   
  delay(1000);     // Wait for 1 second to eusure the crystal to be stable.
  Wire.begin();
   
  control[0] = RTC8564_STOP_BIT, control[1] = 0x00;
  setRegisters(RTC8564_CONTROL1, 2, control);
     
  setDateTime(dt);                                      // Setup date and time.  setDateTime() sets STOP to 0
  setAlarm(RTC8564_AE_NONE, &at, false);       // Setup alarm interrupt.
  setClkoutFrequency(false, RTC8564_CLKOUT_32768HZ);    // Setup CLKOUT frequency.
  setTimer(false, false, RTC8564_CLK_244US, 0, false);                 // Setup timer interrupt.
}
 
void RTC8564Class::setDateTime(struct dateTime *dt) {
  uint8_t data[7];
   
  data[0] = RTC8564_STOP_BIT;
  setRegisters(RTC8564_CONTROL1, 1, data);
   
  data[0] = decimalToBCD(dt->second);
  data[1] = decimalToBCD(dt->minute);
  data[2] = decimalToBCD(dt->hour);
  data[3] = decimalToBCD(dt->day);
  data[4] = decimalToBCD(dt->weekday);
  data[5] = decimalToBCD(dt->month);
 
  if (dt->year > 100) {
    data[6] = decimalToBCD(dt->year - 100);
    data[5] |= RTCS8564_CAL_CENTURY;
  } else {
    data[6] = decimalToBCD(dt->year);
  }
  setRegisters(RTC8564_SECONDS, 7, data);
 
  data[0] = 0x00;
  setRegisters(RTC8564_CONTROL1, 1, data);
}
 
int RTC8564Class::getDateTime(struct dateTime *dt) {
  uint8_t data[7];
   
  getRegisters(RTC8564_SECONDS, 7, data);
   
  if (data[0] & RTCS8564_CAL_VL) {
    return -1;
  }
   
  dt->second  = BCDToDecimal(data[0] & 0x7f);
  dt->minute  = BCDToDecimal(data[1] & 0x7f);
  dt->hour    = BCDToDecimal(data[2] & 0x3f);
  dt->day     = BCDToDecimal(data[3] & 0x3f);
  dt->weekday = BCDToDecimal(data[4] & 0x07);
  dt->month   = BCDToDecimal(data[5] & 0x1f);
  dt->year    = BCDToDecimal(data[6]);
   
  if (data[5] & RTCS8564_CAL_CENTURY) {
    dt->year += 100;
  }
   
  return 0;
}
 
void RTC8564Class::setAlarm(uint8_t enableFlags, struct alarmTime *at, uint8_t interruptEnable) {
  uint8_t control2;
  uint8_t data[4];
   
  // Read Control2 register
  getRegisters(RTC8564_CONTROL2, 1, &control2);
 
  control2 &= (~RTC8564_AIE_BIT);
  setRegisters(RTC8564_CONTROL2, 1, &control2);
 
  data[0] = (enableFlags & RTC8564_AE_MINUTE)  ? decimalToBCD(at->minute)  : RTC8564_AE_BIT;
  data[1] = (enableFlags & RTC8564_AE_HOUR)    ? decimalToBCD(at->hour)    : RTC8564_AE_BIT;
  data[2] = (enableFlags & RTC8564_AE_DAY)     ? decimalToBCD(at->day)     : RTC8564_AE_BIT;
  data[3] = (enableFlags & RTC8564_AE_WEEKDAY) ? decimalToBCD(at->weekday) : RTC8564_AE_BIT;
  setRegisters(RTC8564_MINUTE_ALARM, 4, data);
 
  if (interruptEnable) {
    control2 |= RTC8564_AIE_BIT;
  } else {
    control2 &= ~RTC8564_AIE_BIT;
  }
  setRegisters(RTC8564_CONTROL2, 1, &control2);
}
 
void RTC8564Class::getAlarm(uint8_t *enableFlags, struct alarmTime *at) {
  uint8_t data[4];
   
  getRegisters(RTC8564_MINUTE_ALARM, 4, data);
   
  at->minute  = BCDToDecimal(data[0] & 0x7f);
  at->hour    = BCDToDecimal(data[1] & 0x3f);
  at->day     = BCDToDecimal(data[2] & 0x3f);
  at->weekday = BCDToDecimal(data[3] & 0x07);
   
  *enableFlags = 0;
  for (int i = 0; i < 4; i++) {
    if (!(data[i] & RTC8564_AE_BIT)) {
      *enableFlags |= (1 << i);
    }
  }
}
 
int RTC8564Class::getAlarmFlag() {
  uint8_t control2;
 
  getRegisters(RTC8564_CONTROL2, 1, &control2);
 
  return (control2 & RTC8564_AF_BIT) ? true : false;
}  
 
void RTC8564Class::clearAlarmFlag() {
  uint8_t control2;
 
  getRegisters(RTC8564_CONTROL2, 1, &control2);
  control2 &= ~RTC8564_AF_BIT;
  setRegisters(RTC8564_CONTROL2, 1, &control2);
}  
 
void RTC8564Class::setTimer(uint8_t enableFlag, uint8_t repeatMode, uint8_t clockMode, uint8_t counter, uint8_t interruptEnable) {
  uint8_t control2;
  uint8_t data[4];
     
  data[0] = 0;
  setRegisters(RTC8564_TIMER_CONTROL, 1, data);
   
  if (enableFlag) {
    getRegisters(RTC8564_CONTROL2, 1, &control2);
    control2 &= ~(RTC8564_TITP_BIT | RTC8564_TF_BIT | RTC8564_TIE_BIT);
    setRegisters(RTC8564_CONTROL2, 1, &control2);
   
    if (repeatMode) {
      control2 |= RTC8564_TITP_BIT;
    }
   
    if (interruptEnable) {
      control2 |= RTC8564_TIE_BIT;
    }
   
    setRegisters(RTC8564_CONTROL2, 1, &control2);
    setRegisters(RTC8564_TIMER, 1, &counter);
   
    clockMode |= RTC8564_TE_BIT;
    setRegisters(RTC8564_TIMER_CONTROL, 1, &clockMode);
  }
}
 
int RTC8564Class::getTimerFlag() {
  uint8_t control2;
 
  getRegisters(RTC8564_CONTROL2, 1, &control2);
 
  return (control2 & RTC8564_TF_BIT) ? true : false;
}  
 
void RTC8564Class::clearTimerFlag() {
  uint8_t control2;
 
  getRegisters(RTC8564_CONTROL2, 1, &control2);
  control2 &= ~RTC8564_TF_BIT;
  setRegisters(RTC8564_CONTROL2, 1, &control2);
}
 
void RTC8564Class::setClkoutFrequency(uint8_t enableFlag, uint8_t frequency) {
  if (enableFlag) {
    frequency |= RTCS8564_FE_BIT;
  } else {
    frequency &= ~RTCS8564_FE_BIT;
  }
   
  setRegisters(RTC8564_CLKOUT_FREQUENCY, 1, &frequency);
}
 
inline int RTC8564Class::decimalToBCD(int decimal) {
  return (((decimal / 10) << 4) | (decimal % 10));
}
 
inline int RTC8564Class::BCDToDecimal(int bcd) {
  return ((bcd >> 4) * 10 + (bcd & 0x0f));
}
 
inline int RTC8564Class::waitForData() {
  while(Wire.available() < 1) {
    ;
  }
}
 
void RTC8564Class::setRegisters(uint8_t address, int numData, uint8_t *data) {
  Wire.beginTransmission(slaveAddress);
  Wire.write(address);
  Wire.write(data, numData);
  Wire.endTransmission();
}
 
void RTC8564Class::getRegisters(uint8_t address, int numData, uint8_t *data) {
  Wire.beginTransmission(slaveAddress);
  Wire.write(address);
  Wire.endTransmission();
  Wire.requestFrom(slaveAddress, numData);
   
  for (int i = 0; i < numData; i++) {
    waitForData();
    data[i] = Wire.read();
  }
}
 
RTC8564Class RTC8564;

バージョン

Hardware:Arduino Uno
Software:Arduino 1.5.5

最終更新日

November 1, 2022

inserted by FC2 system