Dallas/Maxim DS3234-based Clock with Propellor
tomcrawford
Posts: 1,129
Introduction
The Dallas/Maxim DS3234 is an updated replacement for the DS1302/1307 Timekeeping chip. The main additions are internal crystal providing +/- 2 ppm accuracy at room temperature, an on-chip thermometer, and on-chip TOD alarm registers. The DS3234 comes in a 20-pin SO package, requiring a surface mount PCB/assembly or a breakout board. Here is a pointer to the data sheet. http://www.maximintegrated.com/datasheet/index.mvp/id/4051
This note discusses a clock based on the DS3234, Parallax Propeller, and a Parallax 4 x 20 serial LCD display. The programming is 100% spin and includes examples of SPI Bus interfacing and a serial interface to the display. The code is attached.
Breadboard and Breakout Boards
The attached photo shows the solderless breadboard with the Prop chip and its standard accoutrements, power conditioning, connectors for the display and the DS3234, and a speaker. I used a 5-volt regulator as well as 3.3-volt regulator because I didnt have a 5-volt wall-wart with banana plugs. The Prop pin assignments are clear from the program listing.
The DS3234 is available only in a SO package. This means some sort of adapter is needful to use it on a DIP-oriented breadboard. One can use a SOIC-to-DIP adapter, available from people like digikey, etc. There are also DS3234-specific breakout boards available from a number of sources; Google ds3234 breakout. The one I used came from cutedigi. It has a socket for the backup battery and a reset switch. All the DS3234 pins are brought to a SIP connector. For reasons I dont understand, the SIP connector is mounted on the component side of the board. I connected it my breadboard with a cable but would have preferred mounting it directly to the breadboard component side up.
TimeKeeping
The DS3234 timing chain begins with a 32kHz (two to the fifteenth Hz) oscillator controlled by a crystal in the SO package. Having an on-chip crystal is extremely cool because you dont have to worry about finding the right crystal with the right capacitors and the right layout. Even better, it allows Maxim to guarantee the accuracy because they have control over the crystal, capacitors, and layout. They specify +/- 2 ppm, which works out to about 1 second per week.
The 32 kHz is divided to 1 Hz and then in a chain of registers containing seconds, minutes, date-of-month, month, year, and day-of week. These registers are directly accessible for reading or writing. The monthly roll-over is corrected for months of less than 31 days, including corrections for leap years. It is possible to set dates that make no sense such as April 31. Whether ones includes logic to prevent this is, I suppose, a personal choice.
It ought to be noted that the values in the time and date registers are BCD rather than pure binary. I chose to convert immediately on reading and last thing before writing.
The attached spin program allows the time, day, and date to be set. A CASE statement runs once a second to increment or decrement whichever value is currently being modified. If we are just keeping time, the date and time are displayed once a second.
Daylight Saving Time is corrected for automatically unless the clock is programmed for Arizona (or Hawaii) time.
There is a bit in a status register that may be used to judge the validity of the timekeeping data. I check that bit on power-up to determine whether to program the chip to default values and whether to assume the status information in the on-chip RAM is valid. Status kept in the on-chip RAM includes Daylight Saving status and alarm status.
The DS3234 includes an on-chip thermometer. The temperature is in one-quarter degrees centigrade and can be read at any time. I round the integer part up if the fraction is one-half or three-quarters, round down otherwise. The accuracy is +/- 3 degrees, which renders the precision a little bogus.
Alarms
The DS3234 includes two alarms. One can program hours, minutes, and seconds or hours and minutes or hours, minutes, seconds, and date or hours, minutes, seconds and day. The chip can be programmed to drive a pin when an alarm matches. Since I use that pin for a 1 Hz square wave, I interrogate a status register once a second to see if an alarm is sounding.
The alarm day function is interesting. One would like to program an alarm for one or more days of the week, say Monday though Friday. But the register contains a value, not a bit mask. So I programmed the alarm hardware to ignore the day and date and implemented the function in software. The alarm functions ended up taking as much code as the time setting and keeping functions.
SPI Driver
The low level driver for the DS3234 is shown here. I implemented one function to write a register given the register address and value, and another that returns the contents of the specified register. The following code fragment shows the drivers.
Serial Driver
The serial driver for the display is shown here. The elegant little asynchronous serializer (which I cribbed from whoever wrote it first, Thank you very much) transmits one eight-bit character; the other function shown here sends a text string beginning at the current cursor position.
Program Notes
The program is attached as a spin file. It is divided into four major sections: time keeping and time setting, alarm functions, DS3234 functions, and display functions. Each section has its own CON and VAR definitions.
The main starts up the DS3234 and the display. In starting the DS3234, I select a 1 HZ square wave used to run the code once a second. I check SWMode to traverse among functions, and then dispatch in the CASE Mode statement. Normally I just display the time, date, temperature, and alarm status, check to see if its time to adjust for DayLight Saving, check for pending alarms, and check for bells and whistles. If we are not in normal Mode, we execute the code corresponding to the variable being varied (such as hours, alarm control, etc).
The alarm logic is next; it has its own set of constants and variables. There are two alarms, so most of the functions take whichalarm as an argument. This results in a lot of LOOKUPs to choose register addresses. There are four vectors, each with one entry for each alarm. These are Alarm[3], AlarmMin[3], etc. The reason these are defined as three entries instead of two is because the alarms are named 1 and 2 rather than 0 and 1 and I didnt want to be constantly writing WhichAlarm-1. AlarmDay(WhichAlarm) contains a mask of one bit for each day of the week; this provides a solution to the Monday-through-Friday issue.
Next is the code specific to the DS3234. The constants are register names, bit names within registers, and pin names. There are low-level access to registers, both read and write, access to on-chip RAM, code to access the DS3234 both when it is coming out of battery-backup and when it is being programmed initially. The BCD2Bin and Bin2BCD functions are here.
Last is the logic for the Parallax Serial Display. The constants are mostly special character for setting the cursor, backlight on/off, etc. Two special characters are defined, a degree sign and a plus/minus sign. The serializer and textstring functions are already described.
The Dallas/Maxim DS3234 is an updated replacement for the DS1302/1307 Timekeeping chip. The main additions are internal crystal providing +/- 2 ppm accuracy at room temperature, an on-chip thermometer, and on-chip TOD alarm registers. The DS3234 comes in a 20-pin SO package, requiring a surface mount PCB/assembly or a breakout board. Here is a pointer to the data sheet. http://www.maximintegrated.com/datasheet/index.mvp/id/4051
This note discusses a clock based on the DS3234, Parallax Propeller, and a Parallax 4 x 20 serial LCD display. The programming is 100% spin and includes examples of SPI Bus interfacing and a serial interface to the display. The code is attached.
Breadboard and Breakout Boards
The attached photo shows the solderless breadboard with the Prop chip and its standard accoutrements, power conditioning, connectors for the display and the DS3234, and a speaker. I used a 5-volt regulator as well as 3.3-volt regulator because I didnt have a 5-volt wall-wart with banana plugs. The Prop pin assignments are clear from the program listing.
The DS3234 is available only in a SO package. This means some sort of adapter is needful to use it on a DIP-oriented breadboard. One can use a SOIC-to-DIP adapter, available from people like digikey, etc. There are also DS3234-specific breakout boards available from a number of sources; Google ds3234 breakout. The one I used came from cutedigi. It has a socket for the backup battery and a reset switch. All the DS3234 pins are brought to a SIP connector. For reasons I dont understand, the SIP connector is mounted on the component side of the board. I connected it my breadboard with a cable but would have preferred mounting it directly to the breadboard component side up.
TimeKeeping
The DS3234 timing chain begins with a 32kHz (two to the fifteenth Hz) oscillator controlled by a crystal in the SO package. Having an on-chip crystal is extremely cool because you dont have to worry about finding the right crystal with the right capacitors and the right layout. Even better, it allows Maxim to guarantee the accuracy because they have control over the crystal, capacitors, and layout. They specify +/- 2 ppm, which works out to about 1 second per week.
The 32 kHz is divided to 1 Hz and then in a chain of registers containing seconds, minutes, date-of-month, month, year, and day-of week. These registers are directly accessible for reading or writing. The monthly roll-over is corrected for months of less than 31 days, including corrections for leap years. It is possible to set dates that make no sense such as April 31. Whether ones includes logic to prevent this is, I suppose, a personal choice.
It ought to be noted that the values in the time and date registers are BCD rather than pure binary. I chose to convert immediately on reading and last thing before writing.
The attached spin program allows the time, day, and date to be set. A CASE statement runs once a second to increment or decrement whichever value is currently being modified. If we are just keeping time, the date and time are displayed once a second.
Daylight Saving Time is corrected for automatically unless the clock is programmed for Arizona (or Hawaii) time.
There is a bit in a status register that may be used to judge the validity of the timekeeping data. I check that bit on power-up to determine whether to program the chip to default values and whether to assume the status information in the on-chip RAM is valid. Status kept in the on-chip RAM includes Daylight Saving status and alarm status.
The DS3234 includes an on-chip thermometer. The temperature is in one-quarter degrees centigrade and can be read at any time. I round the integer part up if the fraction is one-half or three-quarters, round down otherwise. The accuracy is +/- 3 degrees, which renders the precision a little bogus.
Alarms
The DS3234 includes two alarms. One can program hours, minutes, and seconds or hours and minutes or hours, minutes, seconds, and date or hours, minutes, seconds and day. The chip can be programmed to drive a pin when an alarm matches. Since I use that pin for a 1 Hz square wave, I interrogate a status register once a second to see if an alarm is sounding.
The alarm day function is interesting. One would like to program an alarm for one or more days of the week, say Monday though Friday. But the register contains a value, not a bit mask. So I programmed the alarm hardware to ignore the day and date and implemented the function in software. The alarm functions ended up taking as much code as the time setting and keeping functions.
SPI Driver
The low level driver for the DS3234 is shown here. I implemented one function to write a register given the register address and value, and another that returns the contents of the specified register. The following code fragment shows the drivers.
Pub Write3234Reg(RegAd, WData) 'write to a register in the 3234 outa[DS3234CLK]~~ 'make sure clock is high outa[DS3234CS]~ 'when CS goes active outa[DS3234CLK]~ 'clock goes low ready for first bit of address RegAd := RegAd| $80 'set write flag repeat 8 'write flag plus 7 bits of address if (RegAd & $80) == 0 outa[DS3234DI]~ 'set a zero else outa[DS3234DI]~~ 'or else a one outa[DS3234CLK]~~ 'clock high outa[DS3234CLK]~ 'then low RegAd := RegAd << 1 'prepare next bit repeat 8 '8 bits of data if (WData & $80) == 0 outa[DS3234DI]~ 'set a zero else outa[DS3234DI]~~ 'or else a one outa[DS3234CLK]~~ 'clock high outa[DS3234CLK]~ 'then low WData := WData << 1 outa[DS3234CS]~~ 'leave chip select high outa[DS3234CLK]~~ 'and clock high Pub Read3234Reg(RegAd) 'read a register in the 3234 outa[DS3234CLK]~~ 'make sure clock is high outa[DS3234CS]~ 'when CS goes active outa[DS3234CLK]~ 'clock goes low ready for first bit of address repeat 8 '(read flag) plus 7 bits of address if (RegAd & $80) == 0 outa[DS3234DI]~ 'set a zero else outa[DS3234DI]~~ 'or else a one outa[DS3234CLK]~~ 'clock high outa[DS3234CLK]~ 'then low RegAd := RegAd << 1 'prepare next bit outa[DS3234DI]~ 'leave DI Low repeat 8 '8 bits of data result := result << 1 'scale what we have outa[DS3234CLK]~~ 'clock data bit out of DS3234 if ina[DS3234DO] <> 0 'clock to data valid is < 200 nsec Result := Result | 1 'set a one outa[DS3234CLK]~ 'low for next bit outa[DS3234CS]~~ 'leaving CS high outa[DS3234CLK]~~
Serial Driver
The serial driver for the display is shown here. The elegant little asynchronous serializer (which I cribbed from whoever wrote it first, Thank you very much) transmits one eight-bit character; the other function shown here sends a text string beginning at the current cursor position.
PUB TextString(TextPoint) 'put a string of text on display at current cursor CharPoint := TextPoint repeat Ascii := byte[CharPoint] if (Ascii ==0) or (Ascii == $A) or (Ascii == $D) return 'end of string SerialChar(Ascii) 'onto display CharPoint ++ 'on to next character Pub SerialChar(TheByte) 'Serialize one character SerializerCnt := cnt 'initialize timing outa[SerOut]~ 'start bit waitcnt(SerializerCnt+=SerBitTime) 'wait for start bit to complete repeat 8 'going to do eight data bits outa[SerOut] := TheByte & %1 'set the bit to zero or one TheByte := TheByte >> 1 'align the next bit waitcnt(SerializerCnt+=SerBitTime) 'one bit time (Pretty much dead on) ' outa[SerOut]~~ 'stop bit waitcnt(SerializerCnt+=SerBitTime) 'one last bit time
Program Notes
The program is attached as a spin file. It is divided into four major sections: time keeping and time setting, alarm functions, DS3234 functions, and display functions. Each section has its own CON and VAR definitions.
The main starts up the DS3234 and the display. In starting the DS3234, I select a 1 HZ square wave used to run the code once a second. I check SWMode to traverse among functions, and then dispatch in the CASE Mode statement. Normally I just display the time, date, temperature, and alarm status, check to see if its time to adjust for DayLight Saving, check for pending alarms, and check for bells and whistles. If we are not in normal Mode, we execute the code corresponding to the variable being varied (such as hours, alarm control, etc).
The alarm logic is next; it has its own set of constants and variables. There are two alarms, so most of the functions take whichalarm as an argument. This results in a lot of LOOKUPs to choose register addresses. There are four vectors, each with one entry for each alarm. These are Alarm[3], AlarmMin[3], etc. The reason these are defined as three entries instead of two is because the alarms are named 1 and 2 rather than 0 and 1 and I didnt want to be constantly writing WhichAlarm-1. AlarmDay(WhichAlarm) contains a mask of one bit for each day of the week; this provides a solution to the Monday-through-Friday issue.
Next is the code specific to the DS3234. The constants are register names, bit names within registers, and pin names. There are low-level access to registers, both read and write, access to on-chip RAM, code to access the DS3234 both when it is coming out of battery-backup and when it is being programmed initially. The BCD2Bin and Bin2BCD functions are here.
Last is the logic for the Parallax Serial Display. The constants are mostly special character for setting the cursor, backlight on/off, etc. Two special characters are defined, a degree sign and a plus/minus sign. The serializer and textstring functions are already described.
Comments
I recently purchased a NXP PCF2127A that has almost the same specs. I really like the on board xtal and the large SRAM.
I may have to get one of these to play with.
Jim
I down loaded your code, and then walked away from it when I saw it was spi. I have the ds3231 which I have been trying to code modifying Kye's ds1302 code from obex. The 3231 is I2C. I need to revisit your code and check out your bcd conversion routines. My biggest problem with the code has come with writing the 12/not24 mode bit and the pm/not am flags in reg 02H. Looking at the notes I have on my debugging in binary, I may have a problem with the string decoding and recoding. I set the 12 hr and pm flags and I get back 23hours! Currently the only access I have to the forum is my iPad and that doesn't allow me to view your archived code. When you read back hours in 12 hour mode, are you masking off the upper 3bits?
Jim
The BCD conversions are pretty simple with the following notes: BCD2Bin requires valid BCD inputs and BIn2BCD assumes positive integers in the range 00-99.
Um, I can look at the code on my iPad; just tap the main.spin at the bottom of the original note. The formatting is pretty broken (most every line occupies two iPad lines), but you *can* see it. Actually, if I print it from my iPad (wifi to hp whatever), it looks pretty good.
I would also point out that the readReg and writeReg methods are really well isolated. Changing them out for i2c ought to be a straight-foorward exercise.
Please let me know if I can provide any further help
By the way, here's a breakout board that I made for the DS3231...
Do you have these for sale?
I drew the boards in Eagle, had them made at OSH Park (3 bds. for $4.50), and soldered them up in my cheap convection toaster oven (callback to discussion on Savage Circuits).
RS_Jim: I use the DS1307_RTCEngine.spin object from the OBEX for conversing with the DS3231. I've been using another object, unix_time.spin, to deal with handling the date and time. Phil Pilgrim wrote the code, and this is from the file: This object provides methods that convert back and forth between Unix time and year.month.day hour:minute:second values. Unix time is a signed, 32-bit integer representing the number of seconds elapsed since 01 Jan 1970 at 00:00:00 UTC, ignoring leap seconds. The object also provides date and time from Unix time in ASCII string format.
I modified the original to add two new public functions to return the date and the time in the format I wanted (mm/dd/yy and hh:mm).
I make a call to the DS1307 object ReadTime method to get the current time
Another routine displays the date and time on the LCD
Here's the code (in unix_time.spin) that builds the time string for display
I am attaching an archive of my ds3231 code to have you run on your chip is you would. There is some debug code in it that shows what is being sent to the clock chip in binary and what is being returned from the chip.
DS3231_RTCDemo_072114 - Archive [Date 2014.07.22 Time 08.14].zip
_clockdatapin,_clockClockpin are couuently set at 1&0, you can edit in constants to match your setup.
Thanks
Jim
The only thing I changed in the code was setting _clockClockPin = 28 and _clockDataPin = 29 to share the I2C line already established for the EEPROM. If you use some other pins, don't
forget to add a 10K pull-up to the clock and data lines.
Here's the PST session when setting the time. Notice that the time is displayed as 23:58:nn AM, although I entered the time as 10 PM.
The first capture is from your program. The second is from the original DS1307 RTCEngine Demo dated 7/27/2010 by Kye.
Ok there is nothing wrong with my clock chip. You get the exact same results that I get. I wonder what the issue is with setting the 12 hour bit and the pm bit. I am sure I have gone over the data sheet thoroughly and %01000000 sets the hours to 12 & %00100000 sets the pm flag. As you can see the chip is scrambling the bits. Kye just uses 24 hour and subtracts 12 and sets the pm flag in software, I was trying to use the chips own capability.
Thanks for checking on your chip. If you get a chance, look at the routine in time to see if you spot aby problems.
Thanks again for checking.
Jim
I'm interested in buying a couple of those from you. Any chance you have some for sale?
Paul
Is there a chance you could mod your code to check the 12/24 hour feature un that chip. Everyone seems to be running 24 hour and correcting for 12.
Jim
Jim
I tried the am/pm mode and it seems to work fine (wouldn't have expected anything else). I prefer to use 24-hour mode because it is much simpler, even with correcting. Setting the hour is just too messy in am/pm.
If you want to use am/pm, be sure and do NOT go through BCD2BIn when reading back the hour register. Also do NOT use Bin2BCD when writing it. That bit me for a while.
Google "Maxim Tutorial 5413" for a really good description of how 12-hour am/pm works.
tc
I suspect that thebcd2bin bin2bcd is where my problems are originating.
Jim