Go Down

Topic: DS1307 RTC resetting unexpectedly (Read 4898 times) previous topic - next topic

ricm

Jul 15, 2012, 08:53 pm Last Edit: Jul 15, 2012, 09:20 pm by ricm Reason: 1
I have a DS1307 & AT24C32 chip combination on a small RTC board bought via eBay. This is billed as "version 2" and has a slightly smaller crystal than most of the others on sale from China.

I had to add edge connectors - no problem, but because of the battery overlapping the solder pads, the battery had to be removed to solder the connectors. Connections were then made to the Mega 2560, powered by the USB: +5V and Gnd to the same pins on the 2560; SDA and SCL again to the equivalent pins in the "Communication" connectors.

I have tried a number of programs, including ones with specific libraries, but have been unable to get this board to function properly. For the purposes of describing this problem here I have reverted to the simplest possible code, first to set the time and secondly to read the time. This code is from here: http://bildr.org/2011/03/ds1307-arduino/

The code to set the time is here:

Code: [Select]
//Arduino 1.0+ Only
//Arduino 1.0+ Only

#include "Wire.h"
#define DS1307_ADDRESS 0x68
byte zero = 0x00; //workaround for issue #527

void setup(){
 Wire.begin();
 Serial.begin(9600);
 setDateTime(); //MUST CONFIGURE IN FUNCTION
}

void loop(){
 printDate();
 delay(1000);
}

void setDateTime(){

 byte second =      00; //0-59
 byte minute =      57; //0-59
 byte hour =        18; //0-23
 byte weekDay =     1; //1-7
 byte monthDay =    15; //1-31
 byte month =       7; //1-12
 byte year  =       12; //0-99

 Wire.beginTransmission(DS1307_ADDRESS);
 Wire.write(zero); //stop Oscillator

 Wire.write(decToBcd(second));
 Wire.write(decToBcd(minute));
 Wire.write(decToBcd(hour));
 Wire.write(decToBcd(weekDay));
 Wire.write(decToBcd(monthDay));
 Wire.write(decToBcd(month));
 Wire.write(decToBcd(year));

 Wire.write(zero); //start

 Wire.endTransmission();

}

byte decToBcd(byte val){
// Convert normal decimal numbers to binary coded decimal
 return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val)  {
// Convert binary coded decimal to normal decimal numbers
 return ( (val/16*10) + (val%16) );
}

void printDate(){

 // Reset the register pointer
 Wire.beginTransmission(DS1307_ADDRESS);
 Wire.write(zero);
 Wire.endTransmission();

 Wire.requestFrom(DS1307_ADDRESS, 7);

 int second = bcdToDec(Wire.read());
 int minute = bcdToDec(Wire.read());
 int hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
 int weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
 int monthDay = bcdToDec(Wire.read());
 int month = bcdToDec(Wire.read());
 int year = bcdToDec(Wire.read());

 //print the date EG   3/1/11 23:59:59
 Serial.print(month);
 Serial.print("/");
 Serial.print(monthDay);
 Serial.print("/");
 Serial.print(year);
 Serial.print(" ");
 Serial.print(hour);
 Serial.print(":");
 Serial.print(minute);
 Serial.print(":");
 Serial.println(second);

}


This code is compiled and downloaded ONCE just 10 seconds before the due time (approximately the time taken to transfer). This is a crude - but for testing purposes, acceptable approximation to real world time.

Without disconnecting or resetting or anything else, the following READ code is compiled and sent to the 2560:

Code: [Select]
//Arduino 1.0+ Only
//Arduino 1.0+ Only

#include "Wire.h"
#define DS1307_ADDRESS 0x68

void setup(){
 Wire.begin();
 Serial.begin(9600);
}

void loop(){
 printDate();
 delay(1000);
}

byte bcdToDec(byte val)  {
// Convert binary coded decimal to normal decimal numbers
 return ( (val/16*10) + (val%16) );
}

void printDate(){

 // Reset the register pointer
 Wire.beginTransmission(DS1307_ADDRESS);

 byte zero = 0x00;
 Wire.write(zero);
 Wire.endTransmission();

 Wire.requestFrom(DS1307_ADDRESS, 7);

 int second = bcdToDec(Wire.read());
 int minute = bcdToDec(Wire.read());
 int hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
 int weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
 int monthDay = bcdToDec(Wire.read());
 int month = bcdToDec(Wire.read());
 int year = bcdToDec(Wire.read());

 //print the date EG   3/1/11 23:59:59
 Serial.print(month);
 Serial.print("/");
 Serial.print(monthDay);
 Serial.print("/");
 Serial.print(year);
 Serial.print(" ");
 Serial.print(hour);
 Serial.print(":");
 Serial.print(minute);
 Serial.print(":");
 Serial.println(second);

}


This should ensure the setting of the RTC time occurs only once and that, from then on, reading the RTC should give a reasonable approximation to real-world time.

The problem that I have is that the clock starts from 7/15/12 18:57:00 and increments every second to 7/15/12 18:57:19 it then resets back to 7/15/12 18:57:00. When the RTC is unplugged from the Arduino or the Arduino is powered down the time always starts from 7/15/12 18:57:00 and resets again at 7/15/12 18:57:19, never getting beyond this time. If I set a different time the same kind of thing happens, resetting after a certain interval, not necessarily 19 seconds, but always resetting at an interval less than 5 minutes.

I have removed the battery to reset the RTC (the read then shows 0/0/0 0:0:80). I have replaced the battery just in case. I have downloaded the read program twice (in another, different example, the author reported that two sends were required to get the RTC to start properly).

I am left with the thought that a) there is some register set somewhere that is causing the RTC to act more as an interval timer than a RTC (but have found no documentation for such a register) or b) the RTC board is duff.

Before I contact the supplier has anyone any further thoughts as to anything I may have missed?

Regards,
Ric

I should add that the board is fitted with R2 and R3 which appear to be the required pull-up resistors for SDA and SCL. If I add additional 4K7 pull-ups the result is the same as without them. In other words: I don't think the pull-ups is the issue here.

Riva

Hi ricm,

I can see no problems with your clock reading code.
Have you tried running the clock without the battery in? It will not maintain the time once power is removed but will it keep counting?
Do you have a link for the eBay item and a schematic of how you have connected to the Mega.

ricm

#2
Jul 16, 2012, 06:34 pm Last Edit: Jul 16, 2012, 06:44 pm by ricm Reason: 1
Hi Riva,

Thanks very much for replying. I have just tried running the clock without a battery in. Results are the same as before with no improvement or deterioration. I am running a different "Read" program from the one posted (attempting to touch all the bases here) and, as before, it resets after the 19th second.

The eBay item is here: http://cgi.ebay.co.uk/ws/eBayISAPI.dll?ViewItem&item=170849904275&ssPageName=ADME:X:RTQ:GB:1123

Attached are three schematics. The first shows the board connections, the second shows the connections to the mega 2560 without pull-ups; the third shows the connection to the mega 2560 with 4K7 pull-ups.

I am happy there are no solder bridges and that the joints are good. I'm happy that the connections are correct. Indeed if it were not so I would be unlikely to get ANY data. Period. The battery is obviously OK even to the extent of not dragging down the supply rail when present.

Appreciated your help,

Kind regards, Ric

PaulS

Quote
I am running a different "Read" program from the one posted (attempting to touch all the bases here) and, as before, it resets after the 19th second.

And that code looks like?

If it calls bcdToDec(), too, I'd suspect something in that function is causing issues. Try removing the calls to bcdToDec() and see if that gets you past 19 seconds. If the new code doesn't use bcdToDec(), then never mind.

ricm

The new code is here:

Code: [Select]
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib

#include <Wire.h>
#include <RTClib.h>

RTC_DS1307 RTC;

void setup () {
   Serial.begin(57600);
   Wire.begin();
   RTC.begin();

 if (! RTC.isrunning()) {
   Serial.println("RTC is NOT running!");
   // following line sets the RTC to the date & time this sketch was compiled
   //RTC.adjust(DateTime(__DATE__, __TIME__));
 }

}

void loop () {
   DateTime now = RTC.now();

   Serial.print(now.year(), DEC);
   Serial.print('/');
   Serial.print(now.month(), DEC);
   Serial.print('/');
   Serial.print(now.day(), DEC);
   Serial.print(' ');
   Serial.print(now.hour(), DEC);
   Serial.print(':');
   Serial.print(now.minute(), DEC);
   Serial.print(':');
   Serial.print(now.second(), DEC);
   Serial.println();

   Serial.print(" since 1970 = ");
   Serial.print(now.unixtime());
   Serial.print("s = ");
   Serial.print(now.unixtime() / 86400L);
   Serial.println("d");

   // calculate a date which is 7 days and 30 seconds into the future
   DateTime future (now.unixtime() + 7 * 86400L + 30);

   Serial.print(" now + 7d + 30s: ");
   Serial.print(future.year(), DEC);
   Serial.print('/');
   Serial.print(future.month(), DEC);
   Serial.print('/');
   Serial.print(future.day(), DEC);
   Serial.print(' ');
   Serial.print(future.hour(), DEC);
   Serial.print(':');
   Serial.print(future.minute(), DEC);
   Serial.print(':');
   Serial.print(future.second(), DEC);
   Serial.println();

   Serial.println();
   delay(3000);
}


It uses a library called RTClib from jeelabs.org. Here is RTClib.h:

Code: [Select]
// Code by JeeLabs http://news.jeelabs.org/code/
// Released to the public domain! Enjoy!

// Simple general-purpose date/time class (no TZ / DST / leap second handling!)
class DateTime {
public:
   DateTime (uint32_t t =0);
   DateTime (uint16_t year, uint8_t month, uint8_t day,
               uint8_t hour =0, uint8_t min =0, uint8_t sec =0);
   DateTime (const char* date, const char* time);
   uint16_t year() const { return 2000 + yOff; }
   uint8_t month() const { return m; }
   uint8_t day() const { return d; }
   uint8_t hour() const { return hh; }
   uint8_t minute() const { return mm; }
   uint8_t second() const { return ss; }
   uint8_t dayOfWeek() const;

   // 32-bit times as seconds since 1/1/2000
   long secondstime() const;
   // 32-bit times as seconds since 1/1/1970
   uint32_t unixtime(void) const;

protected:
   uint8_t yOff, m, d, hh, mm, ss;
};

// RTC based on the DS1307 chip connected via I2C and the Wire library
class RTC_DS1307 {
public:
 static uint8_t begin(void);
   static void adjust(const DateTime& dt);
   uint8_t isrunning(void);
   static DateTime now();
};

// RTC using the internal millis() clock, has to be initialized before use
// NOTE: this clock won't be correct once the millis() timer rolls over (>49d?)
class RTC_Millis {
public:
   static void begin(const DateTime& dt) { adjust(dt); }
   static void adjust(const DateTime& dt);
   static DateTime now();

protected:
   static long offset;
};


...cont/ next post

ricm

Here is RTClib.cpp. (too big to include in previous post). Note that it uses bcd2bin and bin2bcd:

Code: [Select]
// Code by JeeLabs http://news.jeelabs.org/code/
// Released to the public domain! Enjoy!

#include <Wire.h>
#include <avr/pgmspace.h>
#include "RTClib.h"

#define DS1307_ADDRESS 0x68
#define SECONDS_PER_DAY 86400L

#define SECONDS_FROM_1970_TO_2000 946684800

#if (ARDUINO >= 100)
#include <Arduino.h> // capital A so it is error prone on case-sensitive filesystems
#else
#include <WProgram.h>
#endif

int i = 0; //The new wire library needs to take an int when you are sending for the zero register
////////////////////////////////////////////////////////////////////////////////
// utility code, some of this could be exposed in the DateTime API if needed

const uint8_t daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 }; //has to be const or compiler compaints

// number of days since 2000/01/01, valid for 2001..2099
static uint16_t date2days(uint16_t y, uint8_t m, uint8_t d) {
    if (y >= 2000)
        y -= 2000;
    uint16_t days = d;
    for (uint8_t i = 1; i < m; ++i)
        days += pgm_read_byte(daysInMonth + i - 1);
    if (m > 2 && y % 4 == 0)
        ++days;
    return days + 365 * y + (y + 3) / 4 - 1;
}

static long time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s) {
    return ((days * 24L + h) * 60 + m) * 60 + s;
}

////////////////////////////////////////////////////////////////////////////////
// DateTime implementation - ignores time zones and DST changes
// NOTE: also ignores leap seconds, see http://en.wikipedia.org/wiki/Leap_second

DateTime::DateTime (uint32_t t) {
  t -= SECONDS_FROM_1970_TO_2000; // bring to 2000 timestamp from 1970

    ss = t % 60;
    t /= 60;
    mm = t % 60;
    t /= 60;
    hh = t % 24;
    uint16_t days = t / 24;
    uint8_t leap;
    for (yOff = 0; ; ++yOff) {
        leap = yOff % 4 == 0;
        if (days < 365 + leap)
            break;
        days -= 365 + leap;
    }
    for (m = 1; ; ++m) {
        uint8_t daysPerMonth = pgm_read_byte(daysInMonth + m - 1);
        if (leap && m == 2)
            ++daysPerMonth;
        if (days < daysPerMonth)
            break;
        days -= daysPerMonth;
    }
    d = days + 1;
}

DateTime::DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) {
    if (year >= 2000)
        year -= 2000;
    yOff = year;
    m = month;
    d = day;
    hh = hour;
    mm = min;
    ss = sec;
}

static uint8_t conv2d(const char* p) {
    uint8_t v = 0;
    if ('0' <= *p && *p <= '9')
        v = *p - '0';
    return 10 * v + *++p - '0';
}

// A convenient constructor for using "the compiler's time":
// DateTime now (__DATE__, __TIME__);
// NOTE: using PSTR would further reduce the RAM footprint
DateTime::DateTime (const char* date, const char* time) {
    // sample input: date = "Dec 26 2009", time = "12:34:56"
    yOff = conv2d(date + 9);
    // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
    switch (date[0]) {
        case 'J': m = date[1] == 'a' ? 1 : m = date[2] == 'n' ? 6 : 7; break;
        case 'F': m = 2; break;
        case 'A': m = date[2] == 'r' ? 4 : 8; break;
        case 'M': m = date[2] == 'r' ? 3 : 5; break;
        case 'S': m = 9; break;
        case 'O': m = 10; break;
        case 'N': m = 11; break;
        case 'D': m = 12; break;
    }
    d = conv2d(date + 4);
    hh = conv2d(time);
    mm = conv2d(time + 3);
    ss = conv2d(time + 6);
}

uint8_t DateTime::dayOfWeek() const {
    uint16_t day = date2days(yOff, m, d);
    return (day + 6) % 7; // Jan 1, 2000 is a Saturday, i.e. returns 6
}

uint32_t DateTime::unixtime(void) const {
  uint32_t t;
  uint16_t days = date2days(yOff, m, d);
  t = time2long(days, hh, mm, ss);
  t += SECONDS_FROM_1970_TO_2000; // seconds from 1970 to 2000

  return t;
}

////////////////////////////////////////////////////////////////////////////////
// RTC_DS1307 implementation

static uint8_t bcd2bin (uint8_t val) { return val - 6 * (val >> 4); }
static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); }

uint8_t RTC_DS1307::begin(void) {
  return 1;
}


#if (ARDUINO >= 100)

uint8_t RTC_DS1307::isrunning(void) {
  Wire.beginTransmission(DS1307_ADDRESS);
  Wire.write(i);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_ADDRESS, 1);
  uint8_t ss = Wire.read();
  return !(ss>>7);
}

void RTC_DS1307::adjust(const DateTime& dt) {
    Wire.beginTransmission(DS1307_ADDRESS);
    Wire.write(i);
    Wire.write(bin2bcd(dt.second()));
    Wire.write(bin2bcd(dt.minute()));
    Wire.write(bin2bcd(dt.hour()));
    Wire.write(bin2bcd(0));
    Wire.write(bin2bcd(dt.day()));
    Wire.write(bin2bcd(dt.month()));
    Wire.write(bin2bcd(dt.year() - 2000));
    Wire.write(i);
    Wire.endTransmission();
}

DateTime RTC_DS1307::now() {
  Wire.beginTransmission(DS1307_ADDRESS);
  Wire.write(i);
  Wire.endTransmission();
 
  Wire.requestFrom(DS1307_ADDRESS, 7);
  uint8_t ss = bcd2bin(Wire.read() & 0x7F);
  uint8_t mm = bcd2bin(Wire.read());
  uint8_t hh = bcd2bin(Wire.read());
  Wire.read();
  uint8_t d = bcd2bin(Wire.read());
  uint8_t m = bcd2bin(Wire.read());
  uint16_t y = bcd2bin(Wire.read()) + 2000;
 
  return DateTime (y, m, d, hh, mm, ss);
}

#else

uint8_t RTC_DS1307::isrunning(void) {
  Wire.beginTransmission(DS1307_ADDRESS);
  Wire.send(i);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_ADDRESS, 1);
  uint8_t ss = Wire.receive();
  return !(ss>>7);
}

void RTC_DS1307::adjust(const DateTime& dt) {
    Wire.beginTransmission(DS1307_ADDRESS);
    Wire.send(i);
    Wire.send(bin2bcd(dt.second()));
    Wire.send(bin2bcd(dt.minute()));
    Wire.send(bin2bcd(dt.hour()));
    Wire.send(bin2bcd(0));
    Wire.send(bin2bcd(dt.day()));
    Wire.send(bin2bcd(dt.month()));
    Wire.send(bin2bcd(dt.year() - 2000));
    Wire.send(i);
    Wire.endTransmission();
}

DateTime RTC_DS1307::now() {
  Wire.beginTransmission(DS1307_ADDRESS);
  Wire.send(i);
  Wire.endTransmission();
 
  Wire.requestFrom(DS1307_ADDRESS, 7);
  uint8_t ss = bcd2bin(Wire.receive() & 0x7F);
  uint8_t mm = bcd2bin(Wire.receive());
  uint8_t hh = bcd2bin(Wire.receive());
  Wire.receive();
  uint8_t d = bcd2bin(Wire.receive());
  uint8_t m = bcd2bin(Wire.receive());
  uint16_t y = bcd2bin(Wire.receive()) + 2000;
 
  return DateTime (y, m, d, hh, mm, ss);
}

#endif


////////////////////////////////////////////////////////////////////////////////
// RTC_Millis implementation

long RTC_Millis::offset = 0;

void RTC_Millis::adjust(const DateTime& dt) {
    offset = dt.unixtime() - millis() / 1000;
}

DateTime RTC_Millis::now() {
  return (uint32_t)(offset + millis() / 1000);
}

////////////////////////////////////////////////////////////////////////////////


In the meantime I might just go back to the simpler read code and look again at the bcdToDec and decToBcd functions.

Thanks,
Ric

PaulS

That code is doing a lot more than reading the time. Cut the code down to the minimum that illustrates your problem.

ricm

Here is the cut-down code:

Code: [Select]
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib

#include <Wire.h>
#include <RTClib.h>

RTC_DS1307 RTC;

void setup () {
Serial.begin(57600);
Wire.begin();
RTC.begin();

if (! RTC.isrunning()) {
Serial.println("RTC is NOT running!");
// following line sets the RTC to the date & time this sketch was compiled
//RTC.adjust(DateTime(__DATE__, __TIME__));
}
}

void loop () {
DateTime now = RTC.now();

Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(' ');
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
delay(1000);
}


Attached is the SerialMonitor output.

I did not edit the code for fear of introducing artifacts the 2nd programmer had not intended, please let me know if you want cut-down libraries as well.
Ric

PaulS

Add
Code: [Select]
Serial.print("The Arduino was reset");
after the Serial.begin() statement. We need to see that the Arduino really is being reset, vs. getting incorrect data from the clock, or processing that data incorrectly. It seems very strange that the code consistently operates correctly exactly 20 times then fails.

ricm

Agreed, the behaviour is perplexing. Attached the image of the output as requested.

Ric

Riva

Have you tried connecting the device up as shown in the official documentation and testing it with there sketch's?
http://www.dfrobot.com/index.php?route=product/product&product_id=511
I can see no reason it should not be working but maybe there is some hardware fault with the device. It also has a 4K eprom chip on the board but mentions little or nothing about it.

ricm

Hi, Those diagrams (and most of the other projects using the DS1307) use the analogue pins 4 & 5 - a setup that predates the Mega 2560 which has its own dedicated SDA and SCL pins as part of the Communication row of connectors. Naturally I have used the latter. The DS connection, as far as I can tell, is for the on-board temperature sensor which is not present on my board and therefore not connected.

The sketch associated with the link you gave is one I came across yesterday. It uses the "DS1307new" library which contains lots of extra functions and procedures over those documented so far in this thread. I ran this and a related sketch last night but the results were the same (shown in the attached jpgs). I think my only hope is to create a new sketch that reads the data addresses directly and explicitly and retrieve the raw values (I think the first thing would be to check just the seconds which should not be rolling over every 20 seconds, perhaps checking the minutes, etc., if the seconds check out.).

As to the AT24C32 storage, well that's for later when I get the basics sorted. This RTC board is the start of a much longer project involving data collection and will be replaced by a more accurate clock in the final version. For now it's being used to develop the principles of how the project elements will hang together and for developing appropriate code. So far I've just used sketches and libraries that are currently available that have been developed by others so that I can test each out. Later, the code I use will have been optimised for the specific tasks at hand.

Thanks for looking into it for me. Appreciated.
Ric


Riva

Thought you have probably tested with all the DS1307 libraries you could find I use this https://github.com/davidhbrown/RealTimeClockDS1307 for my DS1307 RTC (see the examples) and it works fine on my UNO. As you said the SDA & SCL lines are on mega pins 20 & 21.

ricm

Well this is hopefully a last update. A friend has a similar RTC board, bought here: http://www.oomlout.co.uk/real-time-clock-kit-p-265.html

This particular board has no 4K RAM. I built the kit for him this afternoon and plugged it straight into my Mega2560, still running one of the programs already installed for my tests. The board worked straight away without issue.

The conclusion has to be that the board I purchased is indeed faulty. Time to chase up the manufacturer I think.

I'd like to thank everyone who has thought about this issue and contributed towards solving the problem. All very much appreciated.

Regards,
Ric

ricm

A replacement board arrived from the supplier free of charge and when hooked up worked perfectly.

Go Up