2014-07-11
・EPSON TOYOCOM製のリアルタイムクロックをDIP基板にしたモジュール
・arduino-リアルタイムクロック間のインターフェースはI2C。arduinoではWireというライブラリーを使う
I2Cは2本の電線だけで複数デバイスと通信する仕組みなのでSPIよりお作法が多い。サンプルプログラムが無いと理解に時間がかかる
・リアルタイムクロックには水晶を外付けするタイプのものもあるがこれは内蔵されている
・RTC8564はNCピンが多いので8ピンで全機能が使える
・割り込み機能はタイマーによる一定周期、アラーム機能による指定日時の2種がある
INT端子は1つしかないので出力はORになる
・曜日の計算機能は内蔵されていないのでarduinoスケッチ側で対応する必要があるが
曜日はレジスターに保持/変更されるので初期化時に1度だけツェラーの公式で計算しRTC8564に書き込めば済む
回路図。通信プロトコルはI2C
以下のプログラムの出力結果
0001 //---------------------------------------------------------------------------
0002 // EPSON TOYOCOM RTC-8564 テストプログラム
0003 //
0004 // 配線:
0005 // Arduino Analog 4 - RTC8564 SDA
0006 // Arduino Analog 5 - RTC8564 SCL
0007 //---------------------------------------------------------------------------
0008 #include <Wire.h>
0009
0010 #define REG_ADDR_CONTROL1 0x00
0011 #define REG_ADDR_CONTROL2 0x01
0012 #define REG_ADDR_SECONDS 0x02
0013 #define REG_ADDR_MINUTES 0x03
0014 #define REG_ADDR_HOURS 0x04
0015 #define REG_ADDR_DAYS 0x05
0016 #define REG_ADDR_WEEKDAYS 0x06
0017 #define REG_ADDR_MONTHS 0x07
0018 #define REG_ADDR_YEARS 0x08
0019 #define REG_ADDR_MINUTE_ALARM 0x09
0020 #define REG_ADDR_HOUR_ALARM 0x0A
0021 #define REG_ADDR_DAY_ALARM 0x0B
0022 #define REG_ADDR_WEEKDAY_ALARM 0x0C
0023 #define REG_ADDR_CLOCKOUT_FREQ 0x0D
0024 #define REG_ADDR_TIMER_CONTROL 0x0E
0025 #define REG_ADDR_TIMER 0x0F
0026
0027 #define STOP_BIT 5 // CONTROL1
0028 #define INTERRUPT_PERIODIC 4 // CONTROL2
0029 #define ALARM_FLAG 3 // CONTROL2
0030 #define TIMER_FLAG 2 // CONTROL2
0031 #define ALARM_INTERRUPT_ENABLE 1 // CONTROL2
0032 #define TIMER_INTERRUPT_ENABLE 0 // CONTROL2
0033 #define VOLTAGE_LOW 7 // SECONDS
0034 #define ALARM_ENABLE 7 // MIN ALARAM - WEEKDAY ALARAM
0035 #define CLOCK_OUT_ENABLE 7 // CLKOUT
0036 #define CLOCK_OUT_FREQ_32768Hz 0x00 // CLKOUT
0037 #define CLOCK_OUT_FREQ_1024Hz 0x01 // CLKOUT
0038 #define CLOCK_OUT_FREQ_32Hz 0x02 // CLKOUT
0039 #define CLOCK_OUT_FREQ_1Hz 0x03 // CLKOUT
0040 #define TIMER_ENABLE 7 // TIMER CONTROL
0041 #define TIMER_CLOCK_4096Hz 0 // TIMER CONTROL
0042 #define TIMER_CLOCK_64Hz 1 // TIMER CONTROL
0043 #define TIMER_CLOCK_1Hz 2 // TIMER CONTROL
0044 #define TIMER_CLOCK_1_60Hz 3 // TIMER CONTROL
0045
0046 #define MINUTES_MASK 0b01111111
0047 #define HOURS_MASK 0b00111111
0048 #define DAYS_MASK 0b00111111
0049 #define WEEKDAYS_MASK 0b00000111
0050 #define MONTHS_MASK 0b00011111
0051
0052 #define RTC8564_ADDR 0x51 // I2C 7bit address
0053
0054 typedef struct {
0055 int year;
0056 byte month;
0057 byte day;
0058 byte hour;
0059 byte min;
0060 byte sec;
0061 byte weekday;
0062 } RTC8564_TIME;
0063
0064 void rtc8564_get_time( RTC8564_TIME *tm );
0065 void rtc8564_sprintf( char *buf, RTC8564_TIME tm );
0066
0067 //---------------------------------------------------------------------------
0068 // DECIMAL -> BCD 変換
0069 //---------------------------------------------------------------------------
0070 byte rtc8564_dec2bcd( byte data )
0071 {
0072 return ((( data / 10) << 4) + (data % 10));
0073 }
0074
0075 //---------------------------------------------------------------------------
0076 // BCD -> DECIMAL 変換
0077 //---------------------------------------------------------------------------
0078 byte rtc8564_bcd2dec( byte data )
0079 {
0080 return ((( data >> 4) * 10) + (data % 16));
0081 }
0082
0083 //---------------------------------------------------------------------------
0084 // CLKOUT端子の周波数設定
0085 //
0086 // 引数:
0087 // CLOCK_OUT_FREQ_32768Hz : 32.768 kHz
0088 // CLOCK_OUT_FREQ_1024Hz : 1.024 kHz
0089 // CLOCK_OUT_FREQ_32Hz : 32 Hz
0090 // CLOCK_OUT_FREQ_1Hz : 1 Hz
0091 //---------------------------------------------------------------------------
0092 void rtc8564_clock_out_freq( byte freq )
0093 {
0094 rtc8564_write_byte( REG_ADDR_CLOCKOUT_FREQ, freq );
0095 }
0096
0097 //----- CLKOUT端子には出力しない(消費電力減) ---------------------
0098 void rtc8564_clock_out_enable( void )
0099 {
0100 rtc8564_set_bit( REG_ADDR_CLOCKOUT_FREQ, CLOCK_OUT_ENABLE );
0101 }
0102
0103 //----- CLKOUT端子に出力する -----------------------------------
0104 void rtc8564_clock_out_disable( void )
0105 {
0106 rtc8564_clear_bit( REG_ADDR_CLOCKOUT_FREQ, CLOCK_OUT_ENABLE );
0107 }
0108
0109 //---------------------------------------------------------------------------
0110 // 1バイトRTC8564に書き込む
0111 //
0112 // 引数1:RTC8564のレジスターアドレス
0113 // 引数2:レジスターに書き込む値
0114 //---------------------------------------------------------------------------
0115 void rtc8564_write_byte(byte addr, byte data)
0116 {
0117 Wire.beginTransmission(RTC8564_ADDR);
0118 Wire.write(addr);
0119 Wire.write(data);
0120 Wire.endTransmission();
0121 }
0122
0123 //---------------------------------------------------------------------------
0124 // 1バイトRTC8564から読み出す
0125 //
0126 // 戻り値:読み出した値
0127 //---------------------------------------------------------------------------
0128 byte rtc8564_read_byte(byte addr)
0129 {
0130 Wire.beginTransmission(RTC8564_ADDR);
0131 Wire.write(addr);
0132 Wire.endTransmission();
0133 Wire.requestFrom(RTC8564_ADDR, 1);
0134
0135 return Wire.read();
0136 }
0137
0138 //---------------------------------------------------------------------------
0139 // RTC8564のレジスターの特定ビットの値を調べる
0140 //
0141 // 戻り値 true:ビットの値が1 false:ビットの値が0
0142 //---------------------------------------------------------------------------
0143 boolean rtc8564_test_bit(byte addr, byte bit_position)
0144 {
0145 byte data;
0146
0147 data = rtc8564_read_byte( addr );
0148 data &= (0x01 << bit_position);
0149 if( data == 0x00 ) {
0150 return false;
0151 } else {
0152 return true;
0153 }
0154 }
0155
0156 //---------------------------------------------------------------------------
0157 // RTC8564のレジスターの特定ビットをセットする
0158 //---------------------------------------------------------------------------
0159 void rtc8564_set_bit(byte addr, byte bit_position)
0160 {
0161 byte data;
0162
0163 data = rtc8564_read_byte( addr );
0164 data |= (0x01 << bit_position);
0165 rtc8564_write_byte( addr, data );
0166 }
0167
0168 //---------------------------------------------------------------------------
0169 // RTC8564のレジスターの特定ビットをクリヤする
0170 //---------------------------------------------------------------------------
0171 void rtc8564_clear_bit(byte addr, byte bit_position)
0172 {
0173 byte data;
0174
0175 data = rtc8564_read_byte( addr );
0176 data &= ~(0x01 << bit_position);
0177 rtc8564_write_byte( addr, data );
0178 }
0179
0180 //---------------------------------------------------------------------------
0181 // RTC8564の初期化
0182 //
0183 // 日時設定、アラーム無効、CLKOUT出力なし、タイマー無効
0184 //---------------------------------------------------------------------------
0185 void rtc8564_init(
0186 int year,
0187 byte month,
0188 byte day,
0189 byte hour,
0190 byte minute,
0191 byte second)
0192 {
0193 rtc8564_write_byte(REG_ADDR_CONTROL1,0x20);
0194 rtc8564_write_byte(REG_ADDR_CONTROL2,0x00);
0195 rtc8564_set_time( year, month, day, hour, minute, second);
0196 rtc8564_alarm_disable();
0197 rtc8564_clock_out_disable();
0198 rtc8564_write_byte(REG_ADDR_TIMER_CONTROL,0x00);
0199 rtc8564_write_byte(REG_ADDR_TIMER,0x00);
0200 rtc8564_clear_bit( REG_ADDR_CONTROL1, STOP_BIT );
0201 }
0202
0203 //---------------------------------------------------------------------------
0204 // アラーム機能
0205 // ・日時週時刻はそれぞれ個別に設定可能なため
0206 // 毎週月曜の12:00にアラーム発生といった使い方ができる
0207 //---------------------------------------------------------------------------
0208
0209 //------ アラーム発生日の設定 -------------------------------------
0210 void rtc8564_set_alarm_day( byte day )
0211 {
0212 rtc8564_write_byte(REG_ADDR_DAY_ALARM, rtc8564_dec2bcd(day));
0213 }
0214
0215 //------ アラーム発生時刻の設定 -----------------------------------
0216 void rtc8564_set_alarm_hour( byte hour )
0217 {
0218 rtc8564_write_byte(REG_ADDR_HOUR_ALARM, rtc8564_dec2bcd(hour));
0219 }
0220
0221 //------ アラーム発生分の設定 -------------------------------------
0222 void rtc8564_set_alarm_minute( byte minute )
0223 {
0224 rtc8564_write_byte(REG_ADDR_MINUTE_ALARM, rtc8564_dec2bcd(minute));
0225 }
0226
0227 //------ アラーム発生週の設定 -------------------------------------
0228 void rtc8564_set_alarm_weekday( byte weekday )
0229 {
0230 rtc8564_write_byte(REG_ADDR_WEEKDAY_ALARM, weekday );
0231 }
0232
0233 //------ アラーム発生時にINT端子をLOWにする -----------------------
0234 void rtc8564_alarm_interrupt_enable( void )
0235 {
0236 rtc8564_set_bit( REG_ADDR_CONTROL2, ALARM_INTERRUPT_ENABLE );
0237 }
0238
0239 //------ INT端子には出力しない ------------------------------------
0240 void rtc8564_alarm_interrupt_disable( void )
0241 {
0242 rtc8564_clear_bit( REG_ADDR_CONTROL2, ALARM_INTERRUPT_ENABLE );
0243 rtc8564_alarm_disable();
0244 }
0245
0246 //------ アラームを無効にする -------------------------------------
0247 void rtc8564_alarm_disable( void )
0248 {
0249 rtc8564_set_bit( REG_ADDR_DAY_ALARM, ALARM_ENABLE );
0250 rtc8564_set_bit( REG_ADDR_HOUR_ALARM, ALARM_ENABLE );
0251 rtc8564_set_bit( REG_ADDR_MINUTE_ALARM, ALARM_ENABLE );
0252 rtc8564_set_bit( REG_ADDR_WEEKDAY_ALARM, ALARM_ENABLE );
0253 rtc8564_alarm_clear();
0254 }
0255
0256 //------ アラームイベント(アラーム発生フラグ)のクリヤ -------------
0257 void rtc8564_alarm_clear( void )
0258 {
0259 rtc8564_clear_bit( REG_ADDR_CONTROL2, ALARM_FLAG );
0260 }
0261
0262 //------ アラームが発生したか否かの確認 ---------------------------
0263 // これを使ってアラームイベント発生を検知するためにはloop内で呼び続ける必要がある
0264 // 戻り値
0265 // true:イベント発生
0266 // true:イベントは発生していない
0267 //-----------------------------------------------------------------
0268 boolean rtc8564_alarm_test( void )
0269 {
0270 if( rtc8564_test_bit(REG_ADDR_CONTROL2, ALARM_FLAG ) ) {
0271 return true;
0272 } else {
0273 return false;
0274 }
0275 }
0276
0277 //---------------------------------------------------------------------------
0278 // タイマー設定
0279 // ・一定周期でイベントを発生させるために使う
0280 // ・タイマーはダウンカウンターでゼロになるとイベント発生
0281 //---------------------------------------------------------------------------
0282
0283 //----- タイマーの設定 --------------------------------------------
0284 // 2つの引数の組み合わせでイベント発生させる
0285 // 引数1:ダウンカウンタープリセット値
0286 // 引数2:カウントダウンさせるためのクロック速度
0287 //-----------------------------------------------------------------
0288 void rtc8564_timer_set( byte count, byte clock )
0289 {
0290 rtc8564_timer_disable();
0291 rtc8564_write_byte(REG_ADDR_TIMER_CONTROL, clock );
0292 rtc8564_write_byte(REG_ADDR_TIMER, count );
0293 rtc8564_timer_enable();
0294 }
0295
0296 //----- タイマーを有効にする --------------------------------------
0297 void rtc8564_timer_enable( void )
0298 {
0299 rtc8564_set_bit( REG_ADDR_TIMER_CONTROL, TIMER_ENABLE );
0300 }
0301
0302 //----- タイマーを無効にする --------------------------------------
0303 void rtc8564_timer_disable( void )
0304 {
0305 rtc8564_clear_bit( REG_ADDR_TIMER_CONTROL, TIMER_ENABLE );
0306 }
0307
0308 //----- イベント発生時にINT端子の出力をLOWにする ------------------
0309 void rtc8564_timer_interrupt_enable( void )
0310 {
0311 rtc8564_set_bit( REG_ADDR_CONTROL2, TIMER_INTERRUPT_ENABLE );
0312 }
0313
0314 //----- イベント発生時にINT端子に出力しない -----------------------
0315 void rtc8564_timer_interrupt_disable( void )
0316 {
0317 rtc8564_clear_bit( REG_ADDR_CONTROL2, TIMER_INTERRUPT_ENABLE );
0318 }
0319
0320 //----- イベント発生フラグをクリヤする ----------------------------
0321 void rtc8564_timer_clear( void )
0322 {
0323 rtc8564_clear_bit( REG_ADDR_CONTROL2, TIMER_FLAG );
0324 }
0325
0326 //----- イベントが発生したか否かをテストする ----------------------
0327 // これを使ってイベント発生を検知するためにはloop内で呼び続ける必要がある
0328 // 戻り値
0329 // true:イベント発生
0330 // true:イベントは発生していない
0331 //-----------------------------------------------------------------
0332 boolean rtc8564_timer_test( void )
0333 {
0334 if( rtc8564_test_bit(REG_ADDR_CONTROL2, TIMER_FLAG ) ) {
0335 return true;
0336 } else {
0337 return false;
0338 }
0339 }
0340
0341 //---------------------------------------------------------------------------
0342 // RTC8564に日時を設定する
0343 //---------------------------------------------------------------------------
0344 void rtc8564_set_time(
0345 int year,
0346 byte month,
0347 byte day,
0348 byte hour,
0349 byte minute,
0350 byte second)
0351 {
0352 rtc8564_write_byte(REG_ADDR_SECONDS, rtc8564_dec2bcd(second));
0353 rtc8564_write_byte(REG_ADDR_MINUTES, rtc8564_dec2bcd(minute));
0354 rtc8564_write_byte(REG_ADDR_HOURS, rtc8564_dec2bcd(hour));
0355 rtc8564_write_byte(REG_ADDR_DAYS, rtc8564_dec2bcd(day));
0356 rtc8564_write_byte(REG_ADDR_WEEKDAYS, rtc8564_calc_weekday(year,month,day));
0357 rtc8564_write_byte(REG_ADDR_MONTHS, rtc8564_dec2bcd(month));
0358 rtc8564_write_byte(REG_ADDR_YEARS, rtc8564_dec2bcd((byte)(year-2000)));
0359 }
0360
0361 //---------------------------------------------------------------------------
0362 // RTC8564から日付時刻を取得する
0363 //
0364 // 引数:日時構造体へのポインタ
0365 //---------------------------------------------------------------------------
0366 void rtc8564_get_time( RTC8564_TIME *tm )
0367 {
0368 tm->year = rtc8564_bcd2dec( rtc8564_read_byte( REG_ADDR_YEARS )) + 2000;
0369 tm->month = rtc8564_bcd2dec( rtc8564_read_byte( REG_ADDR_MONTHS ) & MONTHS_MASK );
0370 tm->day = rtc8564_bcd2dec( rtc8564_read_byte( REG_ADDR_DAYS ) & DAYS_MASK );
0371 tm->hour = rtc8564_bcd2dec( rtc8564_read_byte( REG_ADDR_HOURS ) & HOURS_MASK );
0372 tm->min = rtc8564_bcd2dec( rtc8564_read_byte( REG_ADDR_MINUTES ) & MINUTES_MASK);
0373 tm->sec = rtc8564_bcd2dec( rtc8564_read_byte( REG_ADDR_SECONDS ));
0374 tm->weekday = rtc8564_bcd2dec( rtc8564_read_byte( REG_ADDR_WEEKDAYS ));
0375 }
0376
0377 //---------------------------------------------------------------------------
0378 // 日時を書式整形して出力する rtc8564_get_time()で日時を取得した後に呼び出す
0379 //
0380 // 引数1:文字列の先頭アドレス
0381 // 引数2:日時が格納されている構造体
0382 //---------------------------------------------------------------------------
0383 void rtc8564_sprintf( char *buf, RTC8564_TIME tm )
0384 {
0385 sprintf( buf,"%04u-%02u-%02u %02u:%02u:%02u\n",
0386 tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec );
0387 }
0388
0389 //---------------------------------------------------------------------------
0390 // Zellerの公式による曜日の計算 RTC8564は曜日を自動計算できない
0391 //
0392 // return: 0:日曜 6:土曜
0393 //---------------------------------------------------------------------------
0394 byte rtc8564_calc_weekday( int year, int month, int day )
0395 {
0396 if( month <= 2 ) {
0397 month += 12;
0398 year--;
0399 }
0400
0401 return (byte)((year + year/4 - year/100 + year/400 + ((13 * month + 8)/5) + day) % 7);
0402 }
0403
0404 //---------------------------------------------------------------------------
0405 // RTC8564の初期電源投入確認
0406 // Arduinoの電源は切るがRTC8564は常時通電の場合、これを呼び出して
0407 //
0408 // 戻り値 true:最初の電源投入 false:すでに電源投入初期化済み
0409 //---------------------------------------------------------------------------
0410 boolean rtc8564_is_power_on( void )
0411 {
0412 if( rtc8564_test_bit(REG_ADDR_SECONDS, VOLTAGE_LOW ) ) {
0413 return true;
0414 } else {
0415 return false;
0416 }
0417 }
0418
0419 //---------------------------------------------------------------------------
0420 // arduino setup
0421 //---------------------------------------------------------------------------
0422 void setup()
0423 {
0424 Serial.begin(57600);
0425
0426 Wire.begin();
0427 delay(1000); // RTC8564が安定動作するのを待つ
0428 if( rtc8564_is_power_on() ) {
0429 rtc8564_init( 2014, 7, 10, 12, 0, 0 ); // 電源初期投入時のみ時刻の設定
0430 }
0431 Serial.println("start");
0432 rtc8564_clock_out_freq( CLOCK_OUT_FREQ_1Hz ); // CLKOUTの周波数を1Hzに設定
0433 rtc8564_clock_out_enable(); // CLKOUT端子に出力
0434
0435 rtc8564_set_alarm_minute( 1 ); // 毎時01分にアラームベントを発生させる 1時間ごとに発生する
0436 rtc8564_alarm_interrupt_enable(); // アラームイベントでINT端子をLOWにする
0437
0438 rtc8564_timer_set( 10, TIMER_CLOCK_1Hz ); // 10秒おきにタイマーイベントを発生させる
0439 rtc8564_timer_interrupt_enable(); // タイマーイベントでINT端子をLOWにする
0440 }
0441
0442 //---------------------------------------------------------------------------
0443 // arduino loop
0444 //---------------------------------------------------------------------------
0445 RTC8564_TIME rtc_time;
0446
0447 void loop()
0448 {
0449 char buf[32];
0450
0451 rtc8564_get_time( &rtc_time );
0452 rtc8564_sprintf( buf, rtc_time );
0453 Serial.write( buf );
0454
0455 if( rtc8564_alarm_test() ) { // アラームイベント発生確認
0456 Serial.println("alarm");
0457 rtc8564_alarm_clear();
0458 }
0459
0460 if( rtc8564_timer_test() ) { // タイマーイベント発生確認
0461 Serial.println("timer");
0462 rtc8564_timer_clear();
0463 }
0464
0465 delay(1000);
0466 }
0467
0468 //--------- E N D -----------------------------------------------------------
0469