April 4, 2011

DS3231 Real-Time Clock

I've used the DS1307 Real-Time Clock (RTC) for a few projects in the past, but I'm currently working on several datalogger projects that use both RTC and SD-card. The problem is that the SD card won't survive 5V, and the DS1307 won't work at 3.3V. That particular clock chip needs a minimum of 4.5V according to both the datasheet and some inadvertent "experimental verification".

Rex Belli (One of my students and an all-around bright guy, contact me if you're hiring a summer intern) pointed me towards the DS3231 as a possible replacement. It has several advantages over the DS1307:
  • It runs fine on either 3.3V or 5V.
  • It has a built-in oscillator: no external crystal required.
  • It has two built-in alarms that can drive an interrupt pin, so if you just need a periodic interrupt signal this chip can in many cases do the job without a microcontroller.
  • It' rated to 2 minutes per year (max) drift. (My best DS1307 clock drifts about 2 minutes per month!)
One disadvantage is that the DS3231 doesn't have the eprom storage that the DS1307 has, but you can cheat and store seven bytes in the alarm registers if you had to.

I wrote an Arduino library for it, so if you want to use this clock chip with that microcontroller it makes things a bit easier.

Here's the library: DS3231.zip
The header file (DS3231.h) is extensively commented, and there are several example sketches included as well. Enjoy! If you use the library for anything interesting, send me an email. I'd be happy to hear what's been done with it.

8 comments:

  1. The DS3232 has all the features of the DS3231, plus it has 240 bytes of storage, just like the DS1307.

    http://kennethfinnegan.blogspot.com/2010/02/dot-matrix-arduino-clock.html

    ReplyDelete
  2. Thank you, I search a lot to find how to set time.

    ReplyDelete
  3. Thanks. This is very handy for me. I am making a simple 7-segment LED clock with the 3231.

    I found a minor problem in SetClockMode. You have:
    // Set the flag to the requested value:
    if (h12) {
    temp_buffer = temp_buffer | 0x01000000;
    } else {
    temp_buffer = temp_buffer & 0x10111111;
    }
    I am sure you meant those bit masks to be in binary not hex, like 0b01000000.

    Thanks for the code,
    David

    ReplyDelete
  4. Thanks for catching that, David. I've posted an update.

    I also added a getTime() function which reads all components of time in one call. This eliminates the chance of rollover error should you call, say, getHour() at 1:59:59.999 and getMinute() at 2:00:00.001. (That combination would tell you it's 1:00 instead of 2:00.) If you need just one component of time, use the appropriate getWhatever(); but if you need everything it'd probably be best to use getTime(). Thanks Andy Wickert for pointing out the need for this!

    -ea

    ReplyDelete
  5. Thanks for the library.

    I seem to have a small problem with it, and wondered if you had any idea what I was doing wrong?

    I'm setting it using the arduino Time library functions - casting ints to bytes:
    Clock.setClockMode(false); // set to 24h
    Clock.setYear((byte)(year()-2000));
    Clock.setMonth((byte)month());
    Clock.setDate((byte)day());
    Clock.setDoW((byte)weekday());
    Clock.setHour((byte)hour());
    Clock.setMinute((byte)minute());
    Clock.setSecond((byte)second());

    ...then getting the time later using this code:
    byte byear, bmonth, bdate, bDoW, bhour, bminute, bsecond;
    Clock.getTime(byear, bmonth, bdate, bDoW, bhour, bminute, bsecond);
    String ts = "";
    ts+=(int)bhour; ts+=":"; ts+=(int)bminute; ts+=":"; ts+=(int)bsecond;

    In some cases the hour isn't the expected number, for example I'm expecting 17 and getting 11.

    I've been using the DS1307 library for the DS3231, but prefer yours if I can work out what I'm doing wrong!

    Thanks
    Andy Betsworth

    ReplyDelete
  6. Andy--
    Beats me. Your code looks good, I don't see a problem with it, and I can't reproduce the problem here.

    Anyone else have a suggestion?
    -ea

    ReplyDelete
  7. I managed to work around the problem by resorting to:
    byte byear, bmonth, bdate, bDoW, bhour, bminute, bsecond;
    bool b1,b2;
    Clock.getTime(byear, bmonth, bdate, bDoW, bhour, bminute, bsecond);
    bhour = Clock.getHour(b1,b2);
    String ts = "";
    ts+=(int)bhour; ts+=":"; ts+=(int)bminute; ts+=":"; ts+=(int)bsecond;

    So calling the getHour function separately doesn't cause the problem... odd though, I can't pin the problem down, and the rollover issue still potentially applies with my code.

    ReplyDelete
  8. Possibly you are not setting the hours register correctly. If you wanted to set the hours to 17 and you are reading 11 then it seems you may not be converting the hours to Packed BCD (http://en.wikipedia.org/wiki/Packed_BCD#Packed_BCD) before setting the register.

    If you converted decimal 17 to hex then of course that would be 0x11 and if read out of the hours register in the intended manner that would then be a value of 11. However to correctly load the hours register you need to convert decimal 17 to Packed BCD which is a hex value of 0x17. The decimal 10s are stored in the upper 4 bits and the decimal units are stored in the lower 4 bits. Each nibble is converted to and from decimal as a separate conversion. Hope that makes sense. Refer the DS3231 datasheet.

    ReplyDelete