I've been working on a clock that will display the time down to the hundredths of a second. I am using a RTC (the DS3234) to keep the clock time and an Arduino with a highly accurate crystal to do the counting and output the time to an 8 digit 7 segment LED display (using a MAX 7219).
Basically, the Arduino gets the current time from the RTC and loads it into variables which are then incremented as needed by an ISR running at 100Hz. The program only checks the RTC when it starts up and at midnight to correct any drift that may have occurred; the rest of the time it is not being polled. The problem I am having is that the RTC clock is drifting by a huge amount, on the order of minutes per day. I have verified that the arduino can keep time (within 1-2 seconds using a standard crystal) independent of the RTC. The RTC is also accurate. However, as soon as I put the two together the time starts to drift. As soon as I restart the program or when midnight rolls around the time jumps. I even checked the RTC and found that the RTC's time was off by 2-3 minutes after 8 or so hours. The arduino was keeping time to within a second until it checked the RTC. I have no idea how this is even possible since I am not writing anything to the DS3234. It appears to be a cumulative error that is building up, because if I reset the program it after an hour or so, only a few extra seconds have been added.
The only thing I can think of is that the RTC is "ticking" too fast, but what would cause this to only be present when it is hooked up and running in conjunction with the MAX7219 and arduino?
#include <SPI.h>
#include <LedControl.h>
#include <avr/interrupt.h>
int DIN = 11; // Pin 1 on the Max72xx
int CLK = 13; // Pin 13 on the Max72xx
int LOADCS = 10; // Pin 12 on the Max72xx
int ledBrightness = 15; // range is 0-15. 0=lowest, 15 = full power
int years=0;
int months=0;
int days=0;
int hSecond=0; // these are our time variables
int seconds=0;
int minutes=0;
int hours=0;
LedControl lc=LedControl(DIN,CLK,LOADCS,1); // DIN, CLK, Load/CS, 1 = only one chip MAX chip attached
byte chip_id = 0; // This is not strictly reqd, but if using more than one display this will be needed
byte row = 0; // Set the starting position
int val = 0L; // Variable to store the data from the serial port
const int cs=8; //chip select for RTC
void setup() {
pinMode(DIN, OUTPUT); // once only, lets make the pins outputs
pinMode(CLK, OUTPUT);
pinMode(LOADCS, OUTPUT);
for(int index=0;index<lc.getDeviceCount();index++) // take pins out of power save mode
{
lc.shutdown(index,false);
}
lc.setIntensity(0,ledBrightness);
boot();
RTC_init();
//SetTimeDate(23,5,13,18,11,00); //day(1-31), month(1-12), year(0-99), hour(0-23), minute(0-59), second(0-59) only if needed
ReadTimeDate(); // load current RTC time into time variables
clearSPI();
Serial.begin(19200);
noInterrupts();
//This sets Timer1 to interrupt every 1/100 of a second, assunming a clock of 16MHz.
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; //initialize counter value to 0
// set compare match register to desired timer count: 100hz
OCR1A = 19999; //19999; // 12499 for 10mhz
// turn on CTC mode:
TCCR1B |= (1 << WGM12);
// Set CS11 bits for 8 prescaler:
TCCR1B |= (1 << CS11); // |(1 << CS10);
// enable timer compare interrupt:
TIMSK1 |= (1 << OCIE1A);//|(1 << OCIE1B);
interrupts();
}
ISR(TIMER1_COMPA_vect) // This code executes at each interrupt of Timer1
{
hSecond++;
if (hSecond >= 100) {
seconds++;
hSecond = 0;
}
if (seconds >= 60) {
minutes++;
seconds = 0;
}
if (minutes>= 60) {
hours++;
minutes = 0;
}
if (hours == 24) {
hours = 0;
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE3); // both mode 1 & 3 should work RTC_init();
ReadTimeDate();
clearSPI();
}
// Serial.print(hours);
// Serial.print(":");
// Serial.print(minutes);
// Serial.print(":");
// Serial.print(seconds);
// Serial.print(".");
// Serial.println(hSecond);
led_print(hours, 6); // Print the hour
led_print(minutes, 4); // Print the minutes
led_print(seconds, 2); // Print the seconds
led_print(hSecond, 0); //Print the hundreths of seconds
}
void loop()
{
}
//=====================================
int RTC_init(){
pinMode(cs,OUTPUT); // chip select
// start the SPI library:
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE3); // both mode 1 & 3 should work
//set control register
digitalWrite(cs, LOW);
SPI.transfer(0x8E);
SPI.transfer(0x60); //60= disable Osciallator and Battery SQ wave @1hz, temp compensation, Alarms disabled
digitalWrite(cs, HIGH);
delay(10);
}
//=====================================
void clearSPI() {
SPI.begin();
SPI.end();
}
//=====================================
int SetTimeDate(int d, int mo, int y, int h, int mi, int s){
int TimeDate [7]={s,mi,h,0,d,mo,y};
for(int i=0; i<=6;i++){
if(i==3)
i++;
int b= TimeDate[i]/10;
int a= TimeDate[i]-b*10;
if(i==2){
if (b==2)
b=B00000010;
else if (b==1)
b=B00000001;
}
TimeDate[i]= a+(b<<4);
digitalWrite(cs, LOW);
SPI.transfer(i+0x80);
SPI.transfer(TimeDate[i]);
digitalWrite(cs, HIGH);
}
}
//=====================================
String ReadTimeDate(){
String temp;
int TimeDate [7]; //second,minute,hour,null,day,month,year
for(int i=0; i<=6;i++){
if(i==3)
i++;
digitalWrite(cs, LOW);
SPI.transfer(i+0x00);
unsigned int n = SPI.transfer(0x00);
digitalWrite(cs, HIGH);
int a=n & B00001111;
if(i==2){
int b=(n & B00110000)>>4; //24 hour mode
if(b==B00000010)
b=20;
else if(b==B00000001)
b=10;
TimeDate[i]=a+b;
}
else if(i==4){
int b=(n & B00110000)>>4;
TimeDate[i]=a+b*10;
}
else if(i==5){
int b=(n & B00010000)>>4;
TimeDate[i]=a+b*10;
}
else if(i==6){
int b=(n & B11110000)>>4;
TimeDate[i]=a+b*10;
}
else{
int b=(n & B01110000)>>4;
TimeDate[i]=a+b*10;
}
}
hours = TimeDate[2];
minutes = TimeDate[1];
seconds = TimeDate[0];
years = TimeDate[6];
months = TimeDate[5];
days = TimeDate[4];
return(temp);
}
void led_print(int time_int, int pos){ // Ask for the number and the position to print
byte ones, tens; // A couple of variables to fill with digits
ones=time_int%10; // %10 divides by ten and extracts the remainder
tens=time_int/10%10; // Handy for splitting the digits in two for printing
lc.setDigit(chip_id, pos, (byte) ones, true); // These two lines send the digits to the Max7221
lc.setDigit(chip_id, pos+1, (byte) tens, false); // one by one
}