Hi there
I'm having real difficulty with a clock project I've built, based on an Atmel 328. It's my first arduino project, so my inclination is to blame myself for any mistakes, but I am running out of ideas.
I'm using a DS1307 and the Wire library, and I'm able to set a time and read it back. The battery-backed standby function of the DS1307 is working fine, so it continues keeping time while the rest of the board is powered down.
In every way, the system is behaving as expected, except that, while the clock seems fairly stable, it is losing 3 seconds every minute!
This seems outside the range of normal accuracy problems due to things like excessive capacitance reported by others using the same setup, which are generally reported as seconds per day, or at most per hour.
Things I've already tried:
- swapping the DS1307 with another from the same supplier
- swapping the crystal with another from the same supplier
- running the setup and time-polling sketch on an arduino 2009
- running the setup and time-polling sketch on breadboard (with a separate 16MHz clock for the atmel)
- powering it from the arduino
- powering it from a separate power adapter fed through a 7805 Voltage regulator
- checking the backup-battery voltage (3.3V measured vs 3V specified, but below the 3.6V limit listed on the datasheet)
- soldering the components directly to each other (rather than using a breadboard) to avoid introducing capacitance
- rewiring the circuit so that no other wires go near the crystal
The only remaining thing I can think is that the crystals are not putting out 32768 Hz. Losing 3 seconds per minute means the clock is running 5% slow, and 5% off of 32768 is about 30000.
Is there such a thing as a 30kHz crystal freely available?
I checked and these are the parts I ordered from SK Pang:
3 x 32.768kHz Crystal (XT32KHZ)
5 x Atmega328 IC (ATMEGA328)
3 x DS1307 I2C Bus RTC (DS1307)
The actual crystal is pretty non-descript, it's a tiny 7mm x 2mm silver "canister" with two very fine wires coming out, with the code JB01 printed on the side.
When I swapped out the components, I used the other ones from the same order since that's all I have to hand.
Does this sound like a plausible explanation? Is there any easy way to verify the clock rate (although I suppose that's what my circuit is doing anyway)?
The DS1307 part of the circuit is absolutely minimal, it has the crystal connected across pins 1 and 2, +3V battery input on 3, battery and Atmel ground on 4, data on 5 and 6, pin 7 is unused, Atmel +5V on pin 8. Not even any resistors, Wire activates the internal pullups (comms is working fine).
The code I'm using is as follows (includes the method I originally used to set the time):
#include "Wire.h"
#define DS1307_I2C_ADDRESS 0x68
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
return ( (val/10*16) + (val%10) );
}
// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
return ( (val/16*10) + (val%16) );
}
void setup()
{
Serial.begin(9600);
Wire.begin();
}
// Gets the date and time from the ds1307
void getDateDs1307(byte *second,
byte *iminute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
// Reset the register pointer
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.send(0);
Wire.endTransmission();
Wire.requestFrom(DS1307_I2C_ADDRESS, 7);
// A few of these need masks because certain bits are control bits
*second = bcdToDec(Wire.receive() & 0x7f);
*iminute = bcdToDec(Wire.receive());
*hour = bcdToDec(Wire.receive() & 0x3f); // Need to change this if 12 hour am/pm
*dayOfWeek = bcdToDec(Wire.receive());
*dayOfMonth = bcdToDec(Wire.receive());
*month = bcdToDec(Wire.receive());
*year = bcdToDec(Wire.receive());
}
// 1) Sets the date and time on the ds1307
// 2) Starts the clock
// 3) Sets hour mode to 24 hour clock
// Assumes you're passing in valid numbers
void setDateDs1307(byte second, // 0-59
byte minute, // 0-59
byte hour, // 1-23
byte dayOfWeek, // 1-7
byte dayOfMonth, // 1-28/29/30/31
byte month, // 1-12
byte year) // 0-99
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.send(0);
Wire.send(decToBcd(second)); // 0 to bit 7 starts the clock
Wire.send(decToBcd(minute));
Wire.send(decToBcd(hour)); // If you want 12 hour am/pm you need to set
// bit 6 (also need to change readDateDs1307)
Wire.send(decToBcd(dayOfWeek));
Wire.send(decToBcd(dayOfMonth));
Wire.send(decToBcd(month));
Wire.send(decToBcd(year));
Wire.endTransmission();
}
void loop()
{
byte isecond, iminute, ihour, idayOfWeek, idayOfMonth, imonth, iyear;
getDateDs1307 (&isecond, &iminute, &ihour, &idayOfWeek, &idayOfMonth, &imonth, &iyear);
Serial.print("Time is ");
Serial.print((int)ihour);
Serial.print(":");
Serial.print((int)iminute);
Serial.print(":");
Serial.print((int)isecond);
Serial.println("");
}
void set()
{
byte isecond, iminute, ihour, idayOfWeek, idayOfMonth, imonth, iyear;
iminute = 24;
ihour=21;
isecond=0;
idayOfWeek=4;
idayOfMonth=23;
imonth=9;
iyear=10;
setDateDs1307(isecond, iminute, ihour, idayOfWeek, idayOfMonth, imonth, iyear);
}
Any other ideas?
All suggestions appreciated.