Go Down

Topic: Library for DS1307 Real Time Clock (Read 42513 times) previous topic - next topic

mattt

Oct 01, 2007, 05:24 am Last Edit: Oct 01, 2007, 05:30 am by mattt Reason: 1
Hi all,

I'm new to Arduino, AVR, microcontrollers, and not proficient with C.
Here is my attempt at a library for the DS1307 real-time clock, which connect via I2C.

It covers the basics of stopping and starting the clock, setting (some validation) and read time components.

It does depend on the Wire lib.

Here's how I use it.
Code: [Select]

include <Wire.h>
#include <DS1307.h>

int rtc[7];  

void setup()
{
 Serial.begin(9600);
 /*
 RTC.stop();
 RTC.set(DS1307_SEC,1);
 RTC.set(DS1307_MIN,23);
 RTC.set(DS1307_HR,12);
 RTC.set(DS1307_DOW,1);
 RTC.set(DS1307_DATE,1);
 RTC.set(DS1307_MTH,10);
 RTC.set(DS1307_YR,7);
 RTC.start();
 */
}

void loop()
{
 RTC.get(rtc,true);

 for(int i=0; i<7; i++)
 {
   Serial.print(rtc[i]);
   Serial.print(" ");
 }
 Serial.println();

 delay(1000);

}



mattt

Here's the header file.
Code: [Select]
/*
 DS1307.h - library for DS1307 rtc
*/

// ensure this library description is only included once
#ifndef DS1307_h
#define DS1307_h

// include types & constants of Wiring core API
#include <WConstants.h>

// include types & constants of Wire ic2 lib
#include <../Wire/Wire.h>

#define DS1307_SEC 0
#define DS1307_MIN 1
#define DS1307_HR 2
#define DS1307_DOW 3
#define DS1307_DATE 4
#define DS1307_MTH 5
#define DS1307_YR 6

#define DS1307_BASE_YR 2000

#define DS1307_CTRL_ID B1101000  //DS1307

// Define register bit masks  
#define DS1307_CLOCKHALT B10000000

#define DS1307_LO_BCD  B00001111
#define DS1307_HI_BCD  B11110000

#define DS1307_HI_SEC  B01110000
#define DS1307_HI_MIN  B01110000
#define DS1307_HI_HR   B00110000
#define DS1307_LO_DOW  B00000111
#define DS1307_HI_DATE B00110000
#define DS1307_HI_MTH  B00110000
#define DS1307_HI_YR   B11110000

// library interface description
class DS1307
{
 // user-accessible "public" interface
 public:
   DS1307();
   void get(int *, boolean);
   int get(int, boolean);
     void set(int, int);
   void start(void);
   void stop(void);

 // library-accessible "private" interface
 private:
   byte rtc_bcd[7]; // used prior to read/set ds1307 registers;
     void read(void);
     void save(void);
};

extern DS1307 RTC;

#endif

mattt

Here's the cpp

Code: [Select]
extern "C" {
#include <../Wire/Wire.h>
}
#include "DS1307.h"

DS1307::DS1307()
{
 Wire.begin();
}

DS1307 RTC=DS1307();

// PRIVATE FUNCTIONS

// Aquire data from the RTC chip in BCD format
// refresh the buffer
void DS1307::read(void)
{
 // use the Wire lib to connect to tho rtc
 // reset the resgiter pointer to zero
 Wire.beginTransmission(DS1307_CTRL_ID);
   Wire.send(0x00);
 Wire.endTransmission();

 // request the 7 bytes of data    (secs, min, hr, dow, date. mth, yr)
 Wire.requestFrom(DS1307_CTRL_ID, 7);
 for(int i=0; i<7; i++)
 {
   // store data in raw bcd format
   rtc_bcd[i]=Wire.receive();
 }
}

// update the data on the IC from the bcd formatted data in the buffer
void DS1307::save(void)
{
 Wire.beginTransmission(DS1307_CTRL_ID);
 Wire.send(0x00); // reset register pointer
 for(int i=0; i<7; i++)
 {
   Wire.send(rtc_bcd[i]);
 }
 Wire.endTransmission();
}


// PUBLIC FUNCTIONS
void DS1307::get(int *rtc, boolean refresh)   // Aquire data from buffer and convert to int, refresh buffer if required
{
 if(refresh) read();
 for(int i=0;i<7;i++)  // cycle through each component, create array of data
 {
     rtc[i]=get(i, 0);
 }
}

int DS1307::get(int c, boolean refresh)  // aquire individual RTC item from buffer, return as int, refresh buffer if required
{
 if(refresh) read();
 int v=-1;
 switch(c)
 {
 case DS1307_SEC:
   v=(10*((rtc_bcd[DS1307_SEC] & DS1307_HI_SEC)>>4))+(rtc_bcd[DS1307_SEC] & DS1307_LO_BCD);
     break;
 case DS1307_MIN:
   v=(10*((rtc_bcd[DS1307_MIN] & DS1307_HI_MIN)>>4))+(rtc_bcd[DS1307_MIN] & DS1307_LO_BCD);
     break;
 case DS1307_HR:
   v=(10*((rtc_bcd[DS1307_HR] & DS1307_HI_HR)>>4))+(rtc_bcd[DS1307_HR] & DS1307_LO_BCD);
     break;
 case DS1307_DOW:
   v=rtc_bcd[DS1307_DOW] & DS1307_LO_DOW;
     break;
 case DS1307_DATE:
   v=(10*((rtc_bcd[DS1307_DATE] & DS1307_HI_DATE)>>4))+(rtc_bcd[DS1307_DATE] & DS1307_LO_BCD);
     break;
 case DS1307_MTH:
   v=(10*((rtc_bcd[DS1307_MTH] & DS1307_HI_MTH)>>4))+(rtc_bcd[DS1307_MTH] & DS1307_LO_BCD);
     break;
 case DS1307_YR:
   v=(10*((rtc_bcd[DS1307_YR] & DS1307_HI_YR)>>4))+(rtc_bcd[DS1307_YR] & DS1307_LO_BCD)+DS1307_BASE_YR;
     break;
 } // end switch
 return v;
}

void DS1307::set(int c, int v)  // Update buffer, then update the chip
{
 switch(c)
 {
 case DS1307_SEC:
   if(v<60 && v>-1)
   {
     //preserve existing clock state (running/stopped)
     int state=rtc_bcd[DS1307_SEC] & DS1307_CLOCKHALT;
     rtc_bcd[DS1307_SEC]=state | ((v / 10)<<4) + (v % 10);
   }
   break;
 case DS1307_MIN:
   if(v<60 && v>-1)
   {
     rtc_bcd[DS1307_MIN]=((v / 10)<<4) + (v % 10);
   }
   break;
 case DS1307_HR:
 // TODO : AM/PM  12HR/24HR
   if(v<24 && v>-1)
   {
     rtc_bcd[DS1307_HR]=((v / 10)<<4) + (v % 10);
   }
   break;
 case DS1307_DOW:
   if(v<8 && v>-1)
   {
     rtc_bcd[DS1307_DOW]=v;
   }
   break;
 case DS1307_DATE:
   if(v<31 && v>-1)
   {
     rtc_bcd[DS1307_DATE]=((v / 10)<<4) + (v % 10);
   }
   break;
 case DS1307_MTH:
   if(v<13 && v>-1)
   {
     rtc_bcd[DS1307_MTH]=((v / 10)<<4) + (v % 10);
   }
   break;
 case DS1307_YR:
   if(v<13 && v>-1)
   {
     rtc_bcd[DS1307_YR]=((v / 10)<<4) + (v % 10);
   }
   break;
 } // end switch
 save();
}

void DS1307::stop(void)
{
     // set the ClockHalt bit high to stop the rtc
     // this bit is part of the seconds byte
   rtc_bcd[DS1307_SEC]=rtc_bcd[DS1307_SEC] | DS1307_CLOCKHALT;
   save();
}

void DS1307::start(void)
{
     // unset the ClockHalt bit to start the rtc
     // TODO : preserve existing seconds
   rtc_bcd[DS1307_SEC]=0;
     save();
}




mattt

I'd really appreciate some feedback, especially around how I've a approached the BDC manipulations.

Thanks

xSmurf

I like it :) I've adapted the library for use with the DS1337 RTC. One issue I have with time setting is that it's hard to set the time wrong a source of epoch (serial, ntp).
"Pilots believe in a clean living... they never drink wisky from a dirty glass."

mattt


What the differenc ebetween the DS1307 and DS1337 (leet!), is it just the alarms ?


I have some code for setting the time, but really it's no more complex than checking the serial buffer for a command byte and then assuming the following 7 bytes are decimal Sec, Min, Hr, DOW, Date, MTh, Yr.


xSmurf

All it has more is the geek factore :P It also has a slightly different register layout. This mostly affected the stop/start functions. Also the ds1337 come in a (C) version which has a built in xtal, which can be practical.

As for setting it, sure that's what I though to, but you can't really send bytes through screen. As for epoch, I have some plan on adding NTP... which will deliver the time using an epoch stamp. What ever, that's all just playing with numbers ;)

Here's what the port looks like.
Code: [Select]

extern "C" {
     #include <Wire/Wire.h>
     #include <avr/pgmspace.h>
}

#include "DS1337.h"
#include "programStrings.h"

DS1337::DS1337()
{
     Wire.begin();
}

DS1337 RTC=DS1337();

// PRIVATE FUNCTIONS

// Aquire data from the RTC chip in BCD format
// refresh the buffer
void DS1337::read(void)
{
// use the Wire lib to connect to tho rtc
// reset the resgiter pointer to zero
     Wire.beginTransmission(DS1337_CTRL_ID);
     Wire.send(0x00);
     Wire.endTransmission();

// request the 7 bytes of data    (secs, min, hr, dow, date. mth, yr)
     Wire.requestFrom(DS1337_CTRL_ID, 7);
     for(int i=0; i<7; i++)
     {
     // store data in raw bcd format
           if (Wire.available())
                 rtc_bcd[i]=Wire.receive();
     }
}

// update the data on the IC from the bcd formatted data in the buffer
void DS1337::save(void)
{
     Wire.beginTransmission(DS1337_CTRL_ID);
     Wire.send(0x00); // reset register pointer
     for(int i=0; i<7; i++)
     {
           Wire.send(rtc_bcd[i]);
     }
     Wire.endTransmission();
}

unsigned char DS1337::getRegister(unsigned char registerNumber)
{
     Wire.beginTransmission(DS1337_CTRL_ID);
     Wire.send(registerNumber);
     Wire.endTransmission();

     Wire.requestFrom(DS1337_CTRL_ID, 1);
     
     return Wire.receive();
}

void DS1337::setRegister(unsigned char registerNumber, unsigned char registerMask)
{
     Wire.beginTransmission(DS1337_CTRL_ID);
     Wire.send(registerNumber); // reset register pointer
     
     Wire.send(registerMask);
     
     Wire.endTransmission();
}

void DS1337::unsetRegister(unsigned char registerNumber, unsigned char registerMask)
{
     setRegister(registerNumber, (getRegister(registerNumber) & ~registerNumber));
}

// PUBLIC FUNCTIONS
void DS1337::get(int *rtc, boolean refresh)   // Aquire data from buffer and convert to int, refresh buffer if required
{
     if(refresh) read();
     for(int i=0;i<7;i++)  // cycle through each component, create array of data
     {
           rtc[i]=get(i, 0);
     }
}

int DS1337::get(int c, boolean refresh)  // aquire individual RTC item from buffer, return as int, refresh buffer if required
{
     if(refresh) read();
     int v=-1;
     switch(c)
     {
           case DS1337_SEC:
           v=(10*((rtc_bcd[DS1337_SEC] & DS1337_HI_SEC)>>4))+(rtc_bcd[DS1337_SEC] & DS1337_LO_BCD);
           break;
           case DS1337_MIN:
           v=(10*((rtc_bcd[DS1337_MIN] & DS1337_HI_MIN)>>4))+(rtc_bcd[DS1337_MIN] & DS1337_LO_BCD);
           break;
           case DS1337_HR:
           v=(10*((rtc_bcd[DS1337_HR] & DS1337_HI_HR)>>4))+(rtc_bcd[DS1337_HR] & DS1337_LO_BCD);
           break;
           case DS1337_DOW:
           v=rtc_bcd[DS1337_DOW] & DS1337_LO_DOW;
           break;
           case DS1337_DATE:
           v=(10*((rtc_bcd[DS1337_DATE] & DS1337_HI_DATE)>>4))+(rtc_bcd[DS1337_DATE] & DS1337_LO_BCD);
           break;
           case DS1337_MTH:
           v=(10*((rtc_bcd[DS1337_MTH] & DS1337_HI_MTH)>>4))+(rtc_bcd[DS1337_MTH] & DS1337_LO_BCD);
           break;
           case DS1337_YR:
           v=(10*((rtc_bcd[DS1337_YR] & DS1337_HI_YR)>>4))+(rtc_bcd[DS1337_YR] & DS1337_LO_BCD)+DS1337_BASE_YR;
           break;
     } // end switch
     return v;
}

void DS1337::set(int c, int v)  // Update buffer, then update the chip
{
     switch(c)
     {
           case DS1337_SEC:
           if(v<60 && v>-1)
           {
           //preserve existing clock state (running/stopped)
                 int state=rtc_bcd[DS1337_SEC] & DS1337_CLOCKHALT;
                 rtc_bcd[DS1337_SEC]=state | ((v / 10)<<4) + (v % 10);
           }
           break;
           case DS1337_MIN:
           if(v<60 && v>-1)
           {
                 rtc_bcd[DS1337_MIN]=((v / 10)<<4) + (v % 10);
           }
           break;
           case DS1337_HR:
           // TODO : AM/PM  12HR/24HR
           if(v<24 && v>-1)
           {
                 rtc_bcd[DS1337_HR]=((v / 10)<<4) + (v % 10);
           }
           break;
           case DS1337_DOW:
           if(v<8 && v>-1)
           {
                 rtc_bcd[DS1337_DOW]=v;
           }
           break;
           case DS1337_DATE:
           if(v<31 && v>-1)
           {
                 rtc_bcd[DS1337_DATE]=((v / 10)<<4) + (v % 10);
           }
           break;
           case DS1337_MTH:
           if(v<13 && v>-1)
           {
                 rtc_bcd[DS1337_MTH]=((v / 10)<<4) + (v % 10);
           }
           break;
           case DS1337_YR:
           if(v<13 && v>-1)
           {
                 rtc_bcd[DS1337_YR]=((v / 10)<<4) + (v % 10);
           }
           break;
     } // end switch
     save();
}

void DS1337::stop(void)
{
     setRegister(DS1337_SP, DS1337_SP_EOSC);
}

void DS1337::start(void)
{
     unsetRegister(DS1337_SP, DS1337_SP_EOSC);
}

"Pilots believe in a clean living... they never drink wisky from a dirty glass."

xSmurf

I forgot the header file.

Code: [Select]

/*
 DS1337.h - library for DS1337 rtc
*/

// ensure this library description is only included once
#ifndef DS1337_h
#define DS1337_h

// include types & constants of Wiring core API
#include <WConstants.h>

// include types & constants of Wire ic2 lib
#include <Wire/Wire.h>

#define DS1337_SEC      0
#define DS1337_MIN      1
#define DS1337_HR      2
#define DS1337_DOW      3
#define DS1337_DATE 4
#define DS1337_MTH      5
#define DS1337_YR      6

#define DS1337_BASE_YR            2000

#define DS1337_CTRL_ID            B1101000

// Define register bit masks  
#define DS1337_CLOCKHALT      B10000000

#define DS1337_LO_BCD            B00001111
#define DS1337_HI_BCD            B11110000

#define DS1337_HI_SEC            B01110000
#define DS1337_HI_MIN            B01110000
#define DS1337_HI_HR            B00110000
#define DS1337_LO_DOW            B00000111
#define DS1337_HI_DATE            B00110000
#define DS1337_HI_MTH            B00110000
#define DS1337_HI_YR            B11110000

#define DS1337_ARLM1            0x07
#define DS1337_ARLM1_LO_SEC      B00001111
#define DS1337_ARLM1_HI_SEC      B01110000
#define DS1337_ARLM1_LO_MIN      B01110000
#define DS1337_ARLM1_HI_MIN      B00001111

#define DS1337_SP                  0x0E
#define      DS1337_SP_EOSC            B10000000
#define      DS1337_SP_RS2            B00010000
#define      DS1337_SP_RS1            B00001000
#define      DS1337_SP_INTCN            B00000100
#define      DS1337_SP_A2IE            B00000010
#define      DS1337_SP_A1IE            B00000001

#define DS1337_STATUS            0x0F
#define DS1337_STATUS_OSF      B10000000
#define DS1337_STATUS_A2F      B00000010
#define DS1337_STATUS_A1F      B00000001

// library interface description
class DS1337
{
     // user-accessible "public" interface
     public:
           DS1337();
           void      get(int *, boolean);
           int            get(int, boolean);
           void      set(int, int);
           void      start(void);
           void      stop(void);
           unsigned char getRegister(unsigned char registerNumber);
           void      setRegister(unsigned char registerNumber, unsigned char registerValue);
           void      unsetRegister(unsigned char registerNumber, unsigned char registerMask);

     // library-accessible "private" interface
     private:
           byte      rtc_bcd[7]; // used prior to read/set DS1337 registers;
           void      read(void);
           void      save(void);
};

extern DS1337 RTC;

#endif
"Pilots believe in a clean living... they never drink wisky from a dirty glass."

xSmurf

Oh yeah, it's not actually perfect, I'm not even sure it works properly  :-[ lol
"Pilots believe in a clean living... they never drink wisky from a dirty glass."

xSmurf

Yeah the clock definitely doesn't want to start. I don't see anything in the datasheet about the high bit of the seconds register. I believe the stop/start is tied to EOSC, which I handle in the start/stop functions. :/
"Pilots believe in a clean living... they never drink wisky from a dirty glass."

xSmurf

#10
Oct 13, 2007, 10:50 pm Last Edit: Oct 13, 2007, 11:44 pm by xSmurf Reason: 1
Got it :D

Apparently the internal xtal's capacitance already matched the DS's internal caps. But I had two 22pF caps there... removed them and bam :D

I'll post some more code and stuff a bit later

EDIT: this may still not be perfect, the xtal might want some caps, cause the OSF flag is always risen and the possible reasons are
1) The first time power is applied.
2) The voltage present on VCC is insufficient to support oscillation.
3) The [not]EOSC bit is turned off.
4) External influences on the crystal (e.g., noise, leakage, etc.).

It's not the first time power is applied, vcc is fine, EOSC is turned off (note reason 2 has [not]!). So that leaves only reason 4. But is seems to work just fine. I'm fooling around with some Epoch code as we speak.
"Pilots believe in a clean living... they never drink wisky from a dirty glass."

xSmurf

#11
Oct 15, 2007, 03:07 am Last Edit: Oct 15, 2007, 03:23 am by xSmurf Reason: 1
Here it is.. the whole library. It includes a test in the init to detect the RTC (if you have the I2C scanner mod I posted here). Setting the time using the old method or the new epoch functions which also calculates US and EU (even pre 2006 if needed!) DST and calculates the GMT offset (configurable at compile). And yes that function is almost as big as the rest of the driver! The old way of getting the time is still the current one as it's a lot more practical for printing in a strftime-less environment. But for good measure I added a way to get the current Epoch (*doesn't care about dst!*, but if you need that it should be easy to fix). My only issue right now is that OSCF (oscillator flag), which would be nice for the original time setting condition. But the time seems to stay accurate (at least after a few hours).

The RTC config "rtcConfig.h":
Code: [Select]
#ifndef RTC_CONFIG_H
#defin#ifndef RTC_CONFIG_H
#define RTC_CONFIG_H

/**
* Set this to your local GMT offset
*       Comment to skip GMT offset calculations
**/
#define      RTC_GMT_OFFSET      -5

/**
* Select your DST type
*       Comment all to skip DST calculation
**/
#define      RTC_DST_TYPE      0 /* US */
//#define      RTC_DST_TYPE      1 /* EU */

// Uncomment this only if you need to check DST prior to 2006
//#define      RTC_CHECK_OLD_DST

#define RTC_DOW_1      PSTR("Sun")      /* First day of the week */
#define RTC_DOW_2      PSTR("Mon")
#define RTC_DOW_3      PSTR("Tue")
#define RTC_DOW_4      PSTR("Wed")
#define RTC_DOW_5      PSTR("Thu")
#define RTC_DOW_6      PSTR("Fri")
#define RTC_DOW_7      PSTR("Sat")

#define RTC_DOW_0      RTC_DOW_4      /* Wednesday is the first day of Epoch: This probably shouldn't change! */

#endif


Rest to follow....

EDIT: Pre 2006 check is now optional to save space.
"Pilots believe in a clean living... they never drink wisky from a dirty glass."

xSmurf

Header file "ds1337.h":
Code: [Select]
/*
 DS1337.h - library for DS1337 rtc
*/

// ensure this library description is only included once
#ifndef DS1337_h
#define DS1337_h

// include types & constants of Wiring core API
#include <WConstants.h>

// include types & constants of Wire ic2 lib
#include <Wire/Wire.h>

#include "rtcConfig.h"

#define DS1337_SEC      0
#define DS1337_MIN      1
#define DS1337_HR      2
#define DS1337_DOW      3
#define DS1337_DATE 4
#define DS1337_MTH      5
#define DS1337_YR      6
#define DS1337_CNTY      7

#define RTC_SEC            DS1337_SEC
#define RTC_MIN            DS1337_MIN
#define RTC_HR            DS1337_HR
#define RTC_DOW            DS1337_DOW
#define RTC_DATE      DS1337_DATE
#define RTC_MTH            DS1337_MTH
#define RTC_YR            DS1337_YR
#define RTC_CNTY      DS1337_CNTY

#define DS1337_WADDR            0x68
#define DS1337_RADDR            DS1337_WADDR | 0x01

/**
* Define register bit masks
**/
#define DS1337_LO_BCD            B00001111
#define DS1337_HI_BCD            B01110000

#define DS1337_HI_SEC            B01110000
#define DS1337_HI_MIN            B01110000
#define DS1337_HI_HR            B00110000
#define DS1337_LO_DOW            B00000111
#define DS1337_HI_DATE            B00110000
#define DS1337_HI_MTH            B00010000
#define DS1337_LO_CNTY            B10000000
#define DS1337_HI_YR            B11110000

#define DS1337_ARLM1            0x07
#define DS1337_ARLM1_LO_SEC      B00001111
#define DS1337_ARLM1_HI_SEC      B01110000
#define DS1337_ARLM1_LO_MIN      B01110000
#define DS1337_ARLM1_HI_MIN      B00001111

#define DS1337_SP                  0x0E
#define      DS1337_SP_EOSC            B10000000
#define      DS1337_SP_RS2            B00010000
#define      DS1337_SP_RS1            B00001000
#define      DS1337_SP_INTCN            B00000100
#define      DS1337_SP_A2IE            B00000010
#define      DS1337_SP_A1IE            B00000001

#define DS1337_STATUS            0x0F
#define DS1337_STATUS_OSF      B10000000
#define DS1337_STATUS_A2F      B00000010
#define DS1337_STATUS_A1F      B00000001

/**
* Macros
**/
#define clockStart()                              unsetRegister(DS1337_SP, DS1337_SP_EOSC)
#define clockStop()                                    setRegister(DS1337_SP, DS1337_SP_EOSC)

#define getRegisterSP()                              getRegisterSP(DS1337_SP)
#define getRegisterStatus()                        getRegisterStatus(DS1337_STATUS)

#define getRegisterBit(reg, bitMask)      getRegister(reg) & bitMask

#define isleap(y)                                    ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)

#define bcdToBin(val)                               (((val)&15) + ((val)>>4)*10)
#define binToBcd(val)                               ((((val)/10)<<4) + (val)%10)

/**
* getUTS: Macro for calculateUTS
* returns the time as a unix time stamp
* This function doesn't take into account having DST set or not!
**/
#define      getUTS(refresh)                        calculateUTS(      RTC.clockGet(DS1337_YR, true), RTC.clockGet(DS1337_MTH, false), \
                                                                                   RTC.clockGet(DS1337_DATE, false), RTC.clockGet(DS1337_HR, false), \
                                                                                   RTC.clockGet(DS1337_MIN, false), RTC.clockGet(DS1337_SEC, false) \
                                                                             )

#define clockSet(UTS)                              clockSetWithUTS(UTS, false)

// library interface description
class DS1337
{
     // user-accessible "public" interface
     public:
           /**
            * clockExists: keeps track of the whether or not the RTC exists
           **/
     #ifdef WIRE_LIB_SCAN_MOD
           bool            clockExists;
     #endif
           /**
            * Class constructor
           **/
           DS1337();
           
           /**
            * clockInit: initializes the clock
            * If the I2C scan mod is available, it'll verify the RTC is reachable
           **/
           int8_t            clockInit(void);
           
           /**
            * setRegister: sets a register bit fromt he register number and bitmask
           **/
           void            setRegister(uint8_t, uint8_t);
           
           /**
            * unsetRegister: unsets a register bit fromt he register number and bitmask
           **/
           void            unsetRegister(uint8_t, uint8_t);
           
           /**
            * getRegister: returns the specified register
           **/
           uint8_t            getRegister(uint8_t);
           
           /**
            * clockGet: fills an array with the current time data
           **/
           void            clockGet(uint16_t *);
           
           /**
            * clockGet: gets a specific item from the clock buffer
            * use the second param to specify a buffer refresh
           **/
           uint16_t      clockGet(uint8_t, boolean);
           
           /**
            * calculateUTS: returns the time as a unix time stamp
            * This function doesn't take into account having DST set or not!
           **/
           uint32_t      calculateUTS(uint16_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
           
           /**
            * clockSetWithUTS: sets the date & time from a unix time stamp
            * pass the second param as true to skip DTS and GMT calculation
           **/
           void            clockSetWithUTS(uint32_t, boolean);
           
           /**
            * clockSet: Set the clock time using integer values
           **/
           void            clockSet(uint8_t, uint16_t);
           
           /**
            * Prints all of the DS1337 registers
           **/
           void            printRegisters(void);
     private:
           uint8_t            rtc_bcd[8];
           void            writeRegister(uint8_t, uint8_t);
           void            clockSave(void);
           void            clockRead(void);
};

extern DS1337 RTC;

#endif
"Pilots believe in a clean living... they never drink wisky from a dirty glass."

xSmurf

#13
Oct 15, 2007, 03:13 am Last Edit: Oct 15, 2007, 04:23 am by xSmurf Reason: 1
Driver "ds1337.cpp":
Code: [Select]
extern "C" {
     #include <Wire/Wire.h>
     #include <avr/pgmspace.h>
     #include <HardwareSerial.h>
}

#include "programStrings.h"
#include "ds1337.h"
#include "rtcConfig.h"

DS1337::DS1337()
{            
#ifdef WIRE_LIB_SCAN_MOD
     clockExists = false;
#endif
     Wire.begin();
}

DS1337 RTC = DS1337();

#ifdef WIRE_LIB_SCAN_MOD
int8_t DS1337::clockInit(void)
{
     // Account for the crystal power up!
     delay(250);
     
     // Check address and returns false is there is an error
     if (Wire.checkAddress(DS1337_WADDR)) {
           // Possibly set the default registers here
           
           clockExists      = true;
           
           // Start the oscillator if need
           if (getRegisterBit(DS1337_SP, DS1337_SP_EOSC))
           {
                 clockStart();
           }
           
           return DS1337_WADDR;
     } else clockExists      = false;
     
     return -1;      
}
#endif

void DS1337::setRegister(uint8_t registerNumber, uint8_t registerMask)
{
     writeRegister(registerNumber, (getRegister(registerNumber) | registerMask));
}

void DS1337::unsetRegister(uint8_t registerNumber, uint8_t registerMask)
{
     writeRegister(registerNumber, (getRegister(registerNumber) & ~registerMask));
}

void DS1337::writeRegister(uint8_t registerNumber, uint8_t registerValue)
{
     Wire.beginTransmission(DS1337_WADDR);
     Wire.send(registerNumber);
     
     Wire.send(registerValue);
     
     Wire.endTransmission();
}

uint8_t DS1337::getRegister(uint8_t registerNumber)
{
     Wire.beginTransmission(DS1337_WADDR);
     Wire.send(registerNumber);
     Wire.endTransmission();
     
     Wire.requestFrom(DS1337_WADDR, 1);
     
     return Wire.receive();
}

// PRIVATE FUNCTIONS
void DS1337::clockRead(void)
{
     Wire.beginTransmission(DS1337_WADDR);
     Wire.send(0x00);
     Wire.endTransmission();

     Wire.requestFrom(DS1337_WADDR, 7);
     for(int i=0; i<7; i++)
     {
           if (Wire.available())
                 rtc_bcd[i]      = Wire.receive();
     }
}

void DS1337::clockSave(void)
{
     Wire.beginTransmission(DS1337_WADDR);
     Wire.send(0x00);
     
     for(int i=0; i<7; i++)
     {
           Wire.send(rtc_bcd[i]);
     }
     
     Wire.endTransmission();
}

// PUBLIC FUNCTIONS
void DS1337::clockGet(uint16_t *rtc)
{
     clockRead();
     
     for(int i=0;i<8;i++)  // cycle through each component, create array of data
     {
           rtc[i]=clockGet(i, 0);
     }
}

uint16_t DS1337::clockGet(uint8_t c, boolean refresh)
{
     if(refresh) clockRead();
     
     int timeValue=-1;
     switch(c)
     {
           case DS1337_SEC:
                 timeValue = (10*((rtc_bcd[DS1337_SEC] & DS1337_HI_SEC)>>4))+(rtc_bcd[DS1337_SEC] & DS1337_LO_BCD);
           break;
           case DS1337_MIN:
                 timeValue = (10*((rtc_bcd[DS1337_MIN] & DS1337_HI_MIN)>>4))+(rtc_bcd[DS1337_MIN] & DS1337_LO_BCD);
           break;
           case DS1337_HR:
                 timeValue = (10*((rtc_bcd[DS1337_HR] & DS1337_HI_HR)>>4))+(rtc_bcd[DS1337_HR] & DS1337_LO_BCD);
           break;
           case DS1337_DOW:
                 timeValue = rtc_bcd[DS1337_DOW] & DS1337_LO_DOW;
           break;
           case DS1337_DATE:
                 timeValue = (10*((rtc_bcd[DS1337_DATE] & DS1337_HI_DATE)>>4))+(rtc_bcd[DS1337_DATE] & DS1337_LO_BCD);
           break;
           case DS1337_MTH:
                 timeValue = (10*((rtc_bcd[DS1337_MTH] & DS1337_HI_MTH)>>4))+(rtc_bcd[DS1337_MTH] & DS1337_LO_BCD) & ~DS1337_LO_CNTY;
           break;
           case DS1337_YR:
                 timeValue = (10*((rtc_bcd[DS1337_YR] & DS1337_HI_YR)>>4))+(rtc_bcd[DS1337_YR] & DS1337_LO_BCD)+(1900 + (rtc_bcd[DS1337_MTH] & DS1337_LO_CNTY ? 100 : 0));
           break;
           
           case DS1337_CNTY:
                 timeValue = rtc_bcd[DS1337_MTH] & DS1337_LO_CNTY>>7;
           break;
     } // end switch
     
     return timeValue;
}

void DS1337::clockSet(uint8_t timeSection, uint16_t timeValue)
{
     switch(timeSection)
     {
           case DS1337_SEC:
           if(timeValue<60 && timeValue>-1)
           {
                 rtc_bcd[DS1337_SEC]            = binToBcd(timeValue);
           }
           break;
           
           case DS1337_MIN:
           if(timeValue<60 && timeValue>-1)
           {
                 rtc_bcd[DS1337_MIN]            = binToBcd(timeValue);
           }
           break;
           
           case DS1337_HR:
           // TODO : AM/PM  12HR/24HR
           if(timeValue<24 && timeValue>-1)
           {
                 rtc_bcd[DS1337_HR]            = binToBcd(timeValue);
           }
           break;
           
           case DS1337_DOW:
           if(timeValue<8 && timeValue>-1)
           {
                 rtc_bcd[DS1337_DOW]            = timeValue;
           }
           break;
           
           case DS1337_DATE:
           if(timeValue<31 && timeValue>-1)
           {
                 rtc_bcd[DS1337_DATE]      = binToBcd(timeValue);
           }
           break;
           
           case DS1337_MTH:
           if(timeValue<13 && timeValue>-1)
           {
                 rtc_bcd[DS1337_MTH]            = (binToBcd(timeValue) & ~DS1337_LO_CNTY) | (rtc_bcd[DS1337_MTH] & DS1337_LO_CNTY);
           }
           break;
           
           case DS1337_YR:
           if(timeValue<1000 && timeValue>-1)
           {
                 rtc_bcd[DS1337_YR]            = binToBcd(timeValue);
           }
           break;
           
           case DS1337_CNTY:
           if (timeValue > 0)
           {
                 rtc_bcd[DS1337_MTH]      = (rtc_bcd[DS1337_MTH] | DS1337_LO_CNTY);
           } else {
                 rtc_bcd[DS1337_MTH]      = (rtc_bcd[DS1337_MTH] & ~DS1337_LO_CNTY);
           }
           break;
     } // end switch
     
     clockSave();
}

uint32_t DS1337::calculateUTS(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec)
{
     /* Number of days per month */
     uint32_t tt;
     const uint16_t monthcount[] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
     
     /* Compute days */
     tt = (year - 1970) * 365 + monthcount[month] + day - 1;
     
     /* Compute for leap year */
     for (month <= 2 ? year-- : 0; year >= 1970; year--)
           if (isleap(year))
                 tt++;
     
     /* Plus the time */
     tt = sec + 60 * (min + 60 * (tt * 24 + hour - RTC_GMT_OFFSET));
     
     return tt;
}


Rest to follow....
"Pilots believe in a clean living... they never drink wisky from a dirty glass."

xSmurf

#14
Oct 15, 2007, 03:14 am Last Edit: Oct 15, 2007, 04:23 am by xSmurf Reason: 1
Rest of "ds1337.cpp":
Code: [Select]


void DS1337::clockSetWithUTS(uint32_t unixTimeStamp, boolean correctedTime)
{
     uint16_t      leapCorrection = 0;
     uint32_t      tt;
     uint8_t            thisDate;
     int                  ii;
#if defined(RTC_DST_TYPE)
     uint16_t      thisYear;
#endif
     uint16_t      year;
     const uint16_t      monthcount[] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
     
     /**
      * Calculate GMT and DST
     **/      
#if defined(RTC_GMT_OFFSET)
     if (!correctedTime) unixTimeStamp = unixTimeStamp + (RTC_GMT_OFFSET * 3600);
#endif

     // Years
     tt                  = unixTimeStamp / 3600 / 24 / 365;
     year            = tt + 1970;
     
#if defined(RTC_DST_TYPE)
     if (!correctedTime) {
           thisYear      = year;
     }
#endif
     
     // Set the century bit
     if (tt > 30) {
           rtc_bcd[DS1337_MTH]      = (rtc_bcd[DS1337_MTH] | DS1337_LO_CNTY);
           tt-= 30;
     } else {
           rtc_bcd[DS1337_MTH]      = (rtc_bcd[DS1337_MTH] & ~DS1337_LO_CNTY);
     }
     
     // Set the year
     rtc_bcd[DS1337_YR]            = binToBcd(tt);
     
     // Number of days left in the year
     tt = (unixTimeStamp%31536000 / 3600 / 24) + 1;
     
     // leap year correction
     for (year--; year > 1970; year--) {
           if (isleap(year))
           {
                 leapCorrection++;
                 tt--;
           }
     }
     
     // Set the month
     for (ii = 1; ii < 12; ii++)
     {
           if (monthcount[ii+1] > (tt + ((ii == 2 && isleap(thisYear)) * 1)))
           {
                 rtc_bcd[DS1337_MTH]            = (binToBcd(ii) & ~DS1337_LO_CNTY) | (rtc_bcd[DS1337_MTH] & DS1337_LO_CNTY);
                 break;
           }
     }
     
     // Date
#if defined(RTC_DST_TYPE)
     if (!correctedTime) {
           thisDate = tt - monthcount[ii];
     }
#endif
     
     rtc_bcd[DS1337_DATE]      = binToBcd(tt - monthcount[ii]);
     
     // Day of the week
     rtc_bcd[DS1337_DOW]            = ((tt)%7 + 1) & DS1337_LO_DOW;
     
     // Hour
     tt = unixTimeStamp%86400 / 3600;
     rtc_bcd[DS1337_HR]            = binToBcd(tt);
     
#if defined(RTC_DST_TYPE)
     if (!correctedTime) {
           uint8_t dstStartMo, dstStopMo, dstStart, dstStop;
           
     #ifndef RTC_CHECK_OLD_DST
           dstStart      =  (31-((thisYear * 5 / 4) + 1) % 7);
     #if RTC_DST_TYPE == 1
            dstStop            =  (31-((thisYear * 5 / 4) + 1) % 7);      // EU DST
     #else
           dstStop            = 7 - ((1 + thisYear * 5 / 4) % 7);            // US DST
     #endif
           dstStartMo      = 3;
           dstStopMo      = 11;
     #else
           if (thisYear < 2006) {
                 dstStart      = (2+6 * thisYear - (thisYear / 4) ) % 7 + 1;
                 dstStop            = 14 - ((1 + thisYear * 5 / 4) % 7);
                 dstStartMo      = 4;
                 dstStopMo      = 10;
           } else {
                 dstStart      =  (31-((thisYear * 5 / 4) + 1) % 7);
           #if RTC_DST_TYPE == 1
                  dstStop            =  (31-((thisYear * 5 / 4) + 1) % 7);      // EU DST
           #else
                 dstStop            = 7 - ((1 + thisYear * 5 / 4) % 7);            // US DST
           #endif
                 dstStartMo      = 3;
                 dstStopMo      = 11;
           }
     #endif
           if (ii >= dstStartMo && ii <= dstStopMo)
           {
                 if (ii < dstStopMo)
                 {
                       if (ii > dstStartMo || thisDate > dstStart || thisDate == dstStart && tt >= 2)
                       {
                             clockSetWithUTS(unixTimeStamp + 3600, true);
                             return;
                       }
                 } else {
                       if (thisDate < dstStop || thisDate == dstStop && tt < 2)
                       {
                             clockSetWithUTS(unixTimeStamp + 3600, true);
                             return;
                       }
                 }
           }
     }
#endif
     
     // Minutes
     tt = unixTimeStamp%3600 / 60;
     rtc_bcd[DS1337_MIN]            = binToBcd(tt);
     
     // Seconds
     tt = (unixTimeStamp%3600)%60;
     rtc_bcd[DS1337_SEC]            = binToBcd(tt);
     
     // Stop the clock
     //clockStop();
     
     // Save buffer to the RTC
     clockSave();
     
     // Restart the oscillator
     //clockStart();
}

void DS1337::printRegisters(void)
{
     for(int ii=0;ii<0x10;ii++)
     {
           SPrint("0x");
           Serial.print(ii, HEX);
           SPrint(" ");
           Serial.println(getRegister(ii), BIN);
     }

     delay(200);
}



EDIT: No need to stop/start the oscillator when reading or writing to the ds1337 as the IC as in internal buffer and it will read/write only when all of the memory is read for the operation. Also, if the scan mod is used the clock is always started when the address is found and the osc. is stopped.
"Pilots believe in a clean living... they never drink wisky from a dirty glass."

Go Up