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秒をセットしています。具体的なスケッチは次のエントリーで。

Download

One Response - “ArduinoUnoでRealTimeClockをNTPと同期させる Step.2 RTC8564用ライブラリの制作”

  1. Chameleon Cyber » Blog Archive » ArduinoUnoでRealTimeClockをNTPと同期させる Step.3 Aruduino IDE Ver.0021用にライブラリを修正する : 2010/12/03 - 00:15:32 -

    [...] Chameleon Cyber The sky is the limit. « ArduinoUnoでRealTimeClockをNTPと同期させる Step.2 RTC8564用ライブラリの制作 [...]

Leave a Reply

XHTML: You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>