ArduinoUnoでRealTimeClockをNTPと同期させる Step.2 RTC8564用ライブラリの制作
ArduinoUnoでRealTimeClockをNTPと同期させる Step.1で制御系のだいたいの構想は立ちました。
なぜ、RealTimeClockが必要なのかは突っ込みどころ満載ですが、単に使ってみたかったからに他なりません。実際には、Timeライブラリを使えばArduinoの内部クロックで計時できます。
が、リアルタイムクロックのタイマー機能などを使えば、定期的に温度計測するタイミングなどを、単純なコードで記述できますし、arduinoのCPUの負荷も若干軽減できるはずです。
使ったリアルタイムクロックは秋月電子のものです。エプソントヨコムのRTC8564というチップをのせています。データシートも付属していましたが、エプソントヨコムのWEBサイトから詳細なマニュアルが入手できました。
このリアルタイムクロック用のライブラリを作成してみます。まあ、作らなくてもいいんですが・・・。一応、時計あわせやタイマー、アラームを設定できるようにしたりという機能を実装してみます。制作時間30分くらいなので、あまり突っ込んだ内容になっていないのは笑いですませます。
extern "C" { #include <stdlib.h> #include <string.h> #include <inttypes.h> } /************************************************/ /* Real Time Clock Module */ /* RTC-8564JE/NB */ /* */ /* 1pin CLKOE - open */ /* 2pin CLKOUT - Arduino D7 */ /* 3pin INT - open */ /* 4pin Vss - Arduino GND */ /* 5pin SDA - Arduino A4 */ /* 6pin SCL - Arduino A5 */ /* 7pin NC - open */ /* 8pin Vdd - Arduino 5V <<< (or 3V3) */ /************************************************/ /************************************************/ /* includes */ /************************************************/ #include <WConstants.h> #include <Wire.h> #include "RTC8564.h" /************************************************/ /* defines */ /************************************************/ //Register address #define RTC8564_SLAVE_ADRS (0xA2 >> 1) //0x51 #define RTC8564_CTRL1 0x00 #define RTC8564_CTRL2 0x01 #define RTC8564_SEC 0x02 #define RTC8564_MIN 0x03 #define RTC8564_HOUR 0x04 #define RTC8564_DAY 0x05 #define RTC8564_WEEK 0x06 #define RTC8564_MONTH 0x07 #define RTC8564_YEAR 0x08 #define RTC8564_MIN_ALM 0x09 #define RTC8564_HOUR_ALM 0x0A #define RTC8564_DAY_ALM 0x0B #define RTC8564_WEEK_ALM 0x0C #define RTC8564_CLK_FREQ 0x0D #define RTC8564_TIM_CTRL 0x0E #define RTC8564_TIMER 0x0F #define BCD2Decimal(x) (((x>>4)*10)+(x&0xf)) //For mask #define CLOCK_STOP 0x20 //RTC停止 #define CLOCK_START 0x00 //RTC始動 ////For Alarm #define ALARM_CLEAR 0x07 //AFのみ0クリア #define ALARM_INT_DIS 0x05 //AFを0クリアし、/INTを禁止 #define ALARM_INT_ENA 0x02 ///INTを許可*/ ////For Timer #define TIMER_INT_DIS 0x0A //TFを0クリアし、/INTを禁止 #define TIMER_INT_ENA 0x01 ///INTを許可 #define TIMER_CLEAR 0x0B //TFのみ0クリア ////For bit operation #define AND 0 #define OR 1 /************************************************/ /* constructors */ /************************************************/ RTC8564::RTC8564() : _seconds(0), _minutes(0), _hours(0), _days(0), _weekdays(0), _months(0), _years(0), _century(0) { } //Public Methods////////////////////////////////// /************************************************/ /* Initialization of RTC */ /************************************************/ void RTC8564::begin(void){ Wire.begin(); if(isvalid() == false){ init(); } } /************************************************/ /* The same period of time */ /************************************************/ void RTC8564::sync(uint8_t date_time[],uint8_t size){ controlClock(CLOCK_STOP); Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_SEC); //Set of time data Wire.send(date_time, size); Wire.endTransmission(); controlClock(CLOCK_START); } /*******************************************************************/ /* Settings of alarm */ /* >>>The alarm value is set with BCD. */ /* */ /* 0 clearing of AF. And /INT output is prohibited.<<<ALARM_CLEAR */ /* 0 clearing of TF. And /INT output is prohibited.<<<TIMER_CLEAR */ /* Other values are not changed. */ /* flag='AND' -> c2reg & val */ /* flag='OR' -> c2reg | val */ /*******************************************************************/ uint8_t set_c2reg(int val,int flag){ uint8_t buff; Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_CTRL2); Wire.requestFrom(RTC8564_SLAVE_ADRS, 1); if(flag==0){ buff = Wire.receive() & val;//val=0x05 AF>>0 AIE>>0 /INTの出力禁止 }else{ buff = Wire.receive() | val; } Wire.endTransmission(); //return buff; Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_CTRL2); Wire.send(buff); Wire.endTransmission(); } /************************************************/ /* Set at alarm time */ /************************************************/ void RTC8564::alarm_set(int aweek,int aday,int ahour,int amin){ set_c2reg(ALARM_INT_DIS,AND); //アラーム機能停止 Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_MIN_ALM); //アラーム分のアドレス Wire.send(amin); //アラーム分の値 0-59 Wire.send(ahour); //アラーム時の値 0-23 Wire.send(aday); //アラーム日の値 1-31 Wire.send(aweek); //アラーム曜の値 日月火水木金土 0123456 Wire.endTransmission(); //上位5bitは Don't Care set_c2reg(ALARM_INT_ENA,OR); //アラーム機能開始 } /************************************************/ /* Reset alarm flag(only AF flag) */ /************************************************/ void RTC8564::alarm_reset(void){ set_c2reg(ALARM_CLEAR,AND); //AFのみ0クリアする 0x07 } /************************************************/ /* Set Timer */ /************************************************/ void RTC8564::timer_set(int timer){ set_c2reg(TIMER_INT_DIS,AND); //TFを0クリアする TIEに0を書き込み /INTの出力禁止 Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_TIM_CTRL); Wire.send(0x02); //TE=0 TD1=1 TD0=0(1Hz 1秒桁更新にセット) Wire.send(timer); //タイマー間隔(秒指定) Wire.endTransmission(); set_c2reg(TIMER_INT_ENA,OR); //TIEに1を書き込み /INTの出力許可 } /************************************************/ /* Repetition processing of the timer */ /************************************************/ void RTC8564::timer_reload(bool repeat){ uint8_t buff; Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_CTRL2); Wire.requestFrom(RTC8564_SLAVE_ADRS, 1); if(repeat == false){ buff = Wire.receive() & 0x0f;//TI/TPを0にする(繰り返しなし) } else{ buff = Wire.receive() | 0x10;//TI/TPを1にする(繰り返しあり) } Wire.endTransmission(); Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_CTRL2); Wire.send(buff); Wire.endTransmission(); } /******************************************************/ /* Set clockout */ /* tfreq: 32768Hz=0x80,1024Hz=0x81,32Hz=0x82,1Hz=0x83 */ /******************************************************/ void RTC8564::timer_clockout(int tfreq){ Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_CLK_FREQ); Wire.send(tfreq); Wire.endTransmission(); } /************************************************/ /* Timer beginning */ /* 4096Hz:0x81,64Hz:0x81,1sec:0x82,1min:0x83 */ /* After setting the interval time, */ /* you begin the timer. */ /* 4096Hz:0x81,64Hz:0x81,1sec:0x82,1min:0x83 */ /************************************************/ void RTC8564::timer_start(int tc){ Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_TIM_CTRL); Wire.send(tc); //timer値 (0x82)>>>TE=1 TD1=1 TD0=0 1Hz 1秒桁更新 Wire.endTransmission(); } /************************************************/ /* Reset timer flag(only TF flag) */ /************************************************/ void RTC8564::timer_reset(void){ set_c2reg(TIMER_CLEAR,AND); //0x0B TFのみを0クリアする } /************************************************/ /* The value of the C2 register is obtained. */ /************************************************/ int RTC8564::get_c2reg(void){ uint8_t buff; Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_CTRL2); Wire.endTransmission(); Wire.requestFrom(RTC8564_SLAVE_ADRS, 1); buff = Wire.receive() & 0x0C;//AF,TF以外をマスク Wire.endTransmission(); return buff; //0x08>>>AF=1 0x04>>>TF=1 0x0C>>>AF=TF=1 0x00 AF=TF=0 } /************************************************/ /* Whether RTC can use it is examined. */ /************************************************/ bool RTC8564::available(void){ uint8_t buff[7]; Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_SEC); // 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.receive(); } } _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); } /************************************************/ /* The flag of VL is examined. */ /************************************************/ bool RTC8564::isvalid(void){ Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_SEC); Wire.endTransmission(); Wire.requestFrom(RTC8564_SLAVE_ADRS, 1); if(Wire.available()){ uint8_t buff = Wire.receive(); return (buff & 0x80 ? false : true); //VLをチェックする。 } return false; } /************************************************/ /* The flag of AF is examined. */ /************************************************/ bool RTC8564::alarm_check(void){ Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_CTRL2); Wire.endTransmission(); Wire.requestFrom(RTC8564_SLAVE_ADRS,1); if(Wire.available()) { return ((Wire.receive() & 0x08) != 0x08 ? false : true); } return false; } /************************************************/ /* The flag of TF is examined. */ /************************************************/ bool RTC8564::timer_check(void){ Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(RTC8564_CTRL2); Wire.endTransmission(); Wire.requestFrom(RTC8564_SLAVE_ADRS, 1); if(Wire.available()) { return ((Wire.receive() & 0x04) != 0x04 ? false : true); } return false; } //Private Methods///////////////////////////////// /************************************************/ /* Initialization */ /************************************************/ void RTC8564::init(void) { delay(3000); //tSTA >> Ta=+25C Vdd=1V8 Max->3.0s Wire.beginTransmission(RTC8564_SLAVE_ADRS); //0x51 Wire.send(RTC8564_CTRL1); //The following values are written from the register address 0x00. Wire.send(0x20); //00 Control 1 >> STOP=1 Wire.send(0x00); //01 Control 2 >> FLAG CLEAR Wire.send(0x00); //02 Seconds Wire.send(0x00); //03 Minutes Wire.send(0x09); //04 Hours Wire.send(0x01); //05 Days Wire.send(0x01); //06 Weekdays Wire.send(0x01); //07 Months Wire.send(0x01); //08 Years Wire.send(0x00); //09 Minutes Alarm Wire.send(0x00); //0A Hours Alarm Wire.send(0x00); //0B Days Alarm Wire.send(0x00); //0C Weekdays Alarm Wire.send(0x00); //0D CLKOUT Wire.send(0x00); //0E Timer control Wire.send(0x00); //0F Timer Wire.send(0x00); //00 Control 1, STOP=0 Wire.endTransmission(); } /************************************************/ /* STOP flag control function */ /* 0x20 STOP=1(Clock stop) */ /* 0x00 STOP=0(Clock start) */ /************************************************/ void RTC8564::controlClock(int flag){ Wire.beginTransmission(RTC8564_SLAVE_ADRS); //0x51 Wire.send(RTC8564_CTRL1); //0x00 Wire.send(flag); //STOP Wire.endTransmission(); } 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();
ヘッダファイルが
#ifndef RTC8564_h #define RTC8564_h #include <inttypes.h> class RTC8564 { private: void init(void); void controlClock(int flag); 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); void alarm_set(int aweek,int aday,int ahour,int amin); void alarm_reset(void); bool alarm_check(void); bool timer_check(void); void timer_set(int timer); void timer_start(int tc); void timer_reload(bool repeat); void timer_clockout(int tfreq); void timer_reset(void); int get_c2reg(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; }; extern RTC8564 RTC; #endif
使い方は
RTC.sync(0×01,0×02,0×03,0×04,05,0×06,0×10);
でリアルタイムクロックに時刻をセットしたりできます。
ちなみに10年6月4日金曜日3時2分1秒をセットしています。具体的なスケッチは次のエントリーで。
[...] Chameleon Cyber The sky is the limit. « ArduinoUnoでRealTimeClockをNTPと同期させる Step.2 RTC8564用ライブラリの制作 [...]