It occurred to me that I haven’t posted the latest update on data interfacing to the Dallas/Maxim DS1302 real-time clock (RTC) chip; and then I searched and saw that I haven’t posted anything on it. I received the sample chips mid-June and started working with them after I had prototyped the A6276 LED driver and was waiting for my shipment of LEDs. Apparently once I received the LEDs, I was so excited by the bright blue glow that I forgot to get back to the timekeeping functions.
“Auditioning” the 1302
I’m looking at two different timekeeping chips to use in the clock. The DS1340 looks like the better choice but is only available in surface-mount packages, so I’ll need to make a carrier before I can breadboard it to test; thus so far I’ve been testing the DS1302.
The 1302 tracks YY/MM/DD HH:MM:SS plus day of week and choice of 24-hour or AM/PM. It uses an external 32768Hz oscillator and has a VCC2 input to allow it to run on a lithium backup battery for up to 11 years in the absence of primary power. It doesn’t do daylight savings time, has century-unaware leap year support (but my clock will not still be in use in 2100), and requires you to set day of week manually rather than calculating it algorithmically. It’s not terribly sophisticated–but I don’t need terrible sophistication to get this job done.
It uses a “simple” three-wire interface for data communications:
|CE||clock enable?||raise to begin transmission; lower to end communication|
|SCLK||serial clock?||cycle to transmit and receive data|
|I/O||I/O||data to and from clock chip|
To write data to the 1302, raise CE, transmit the register address to the 1302 (setting the low bit to indicate a write operation, and clocking each bit), transmit the data (clocking each bit), and lower CE. To read data from the 1302, raise CE and transmit the register address (clearing the low bit, and clocking), continue clocking and read the received data, and lower CE.
But in the Real World . . .
I jumpered it to my Curiously Strong LogoChip and coded up an implementation of the protocol, but was getting totally random data back. The 1302 claims to support clock speeds down to DC (i.e. as slow as you want it to go), so I slowed it down to 1Hz and put the logic probe on it to see whether the bits it was sending matched the bytes I was outputting. After watching it send ultra-high-speed flurries of data back in response to the slow requests I was sending it, I realized it behaves much more predictably when I connect its ground pin to ground instead of leaving it floating. Oopsie.
Once I was actually communicating with the chip with a common voltage reference, I was able to read and display its seconds register. I found that it changed once a second as expected; and I knew its output wouldn’t be a simple count from 0-59, because it uses binary-coded decimal (BCD), where each nybble is the binary coding for the corresponding decimal digit.
I was surprised, though, to see the values alternating high and low: 64, 192, 65, 192, 66, 194, 67, 195. This suggested a bit alignment problem–I wasn’t reading the right bit in the right position. I wrote a Perl script to translate the decimal values into binary with leading zeroes (to make the bits line up so I could scan visually for patterns) and fed it a complete 60-second cycle, with this result:
I needn’t have included the whole thing, because it’s obvious what’s happening. What I thought was the high bit was changing the most rapidly, so it must in fact be the low bit. Somehow the byte got rotated one bit to the right. It’s only fair to mention at this point that the chip will keep repeating its transmitted data as long as you keep clocking it; so a rotation like this simply means that I’m a clock cycle off, not that something utterly bizarre is happening.
I searched my code for an errant clock cycle between the address-write and the data-read, but couldn’t find anything amiss. As an ugly kludge to get it working, I changed the read routine to cycle the clock after reading the bit rather than before, and of course began to get the correct data. Then I reexamined the datasheet and found the slightest justification in their confusing language that this behavior is by design: the first bit from the DS1302 is transmitted on the falling edge of the same clock cycle whose rising edge carried the last bit of the address register. Ewwwww.
I’ve been trying to think of cleverer ways to adapt to the situation, but haven’t come up with any. Clocking after reading works for single-byte reads, and should work (I haven’t tried it yet) for multi-byte reads in burst mode. So I guess I’ll leave it that way.
Here are the first few entries of a correct seconds cycle, in all their glory:
|128||-||1 | 000 | 0000|
|129||-||1 | 000 | 0001|
|130||-||1 | 000 | 0010|
|131||-||1 | 000 | 0011|
|132||-||1 | 000 | 0100|
|133||-||1 | 000 | 0101|
|134||-||1 | 000 | 0110|
|135||-||1 | 000 | 0111|
|136||-||1 | 000 | 1000|
|137||-||1 | 000 | 1001|
|144||-||1 | 001 | 0000|
|145||-||1 | 001 | 0001|
|146||-||1 | 001 | 0010|
|147||-||1 | 001 | 0011|
The 1 in the high bit of the high nybble represents that the clock is halted (which it obviously isn’t; not sure what to make of that, unless maybe it’s a write-only register???), the next three bits represent the tens-of-seconds digit, and the lower four bits represent the seconds digit. So this short list here shows seconds 00 – 13.
Higher-Level Timekeeping Functions
In order to be able to interpret the data coming back while testing the read routines, I had already written some functions to parse the time data. I haven’t gone back to test all of them after getting the read working, and I have some cleaning up to do on them anyway. Now that I have a one-digit prototype display working, I think returning to the timekeeping is the next step. Hopefully I can soon integrate the two, so the demo display can show actual seconds information.