DS1307 to chyba jeden z najpopularniejszych ukÅ‚adów RTC sÅ‚użących do odmierzania czasu w projektach Arduino podczas tworzenia różnego rodzaju zegarków. UkÅ‚ad ten możemy swobodnie kupić już za okoÅ‚o 3 zÅ‚, dlatego też jest bardzo chÄ™tnie wykorzystywany w ogromnej iloÅ›ci gotowych moduÅ‚ów.
Ma tu wpływ nie tylko jego cena, ale zdolność komunikacji z naszym mikrokontrolerem za pośrednictwem magistrali I2C.
DS1307 posiada możliwość zasilania bateryjnego z zakresu 2V ÷ 3.5V, które ma na zadanie podtrzymanie pracy zegara po zaniku gÅ‚ównego zasilania ukÅ‚adu. NapiÄ™cie zasilania może mieÅ›cić siÄ™ w zakresie od 4.5V ÷ 5.5V przy poborze prÄ…du na poziomie 1mA. Do poprawnego dziaÅ‚ania wymagany jest zewnÄ™trzny kwarc 32.768Hz o pojemnoÅ›ci obciążenia CL=12.5pF.
Warto tutaj wspomnieć o dodatkowych wÅ‚aÅ›ciwoÅ›ciach ukÅ‚adu, takich jak generator przebiegu prostokÄ…tnego, o czÄ™stotliwoÅ›ciach 1Hz, 4096Hz, 8192Hz lub 32768Hz. DS1307 oferuje również podtrzymywanÄ… bateryjne pamięć o rozmiarze 56 bajtów. Te dwie cechy sÄ… sukcesywnie pomijane przez twórców bibliotek oraz producentów gotowych moduÅ‚ów, poprzez nie umieszczanie wyprowadzenia do odpowiedniego pinu SQW/OUT.
A szkoda - sygnaÅ‚ 1Hz możemy wykorzystać do obsÅ‚ugi przerwania aktualizacji wyÅ›wietlania czasu na ekranie LCD lub podÅ‚Ä…czenia mrugajÄ…cego dwukropka pomiÄ™dzy godzinÄ… a minutÄ…, oszczÄ™dzajÄ…c tym samym pin cyfrowy Arduino i miejsce w pamiÄ™ci na zbÄ™dny fragment programu. Ale o tym późnej.
UkÅ‚ad DS1307 dostÄ™pny jest w dwóch obudowach SO-8 oraz DIP-8. W obu wariantach, rozkÅ‚ad nóżek jest identyczny:
Dokumentacja techniczna: http://www.jarzebski.pl/datasheets/DS1307.pdf
PoÅ‚Ä…czenie DS1307 jest bardzo proste. PodÅ‚Ä…czamy zasilanie 5V (nóżka 8), masÄ™ GND (nóżka 4) oraz kwarc zegarkowy 32768Hz pomiÄ™dzy nóżki 1 i 2 . Jak wspomniaÅ‚em wczeÅ›niej, ukÅ‚ad ten komunikuje siÄ™ z Arduino za pomocÄ… magistrali I2C, a wiÄ™c SCL (nóżka 6) do pinu A5, a SDA (nóżka 5) do pinu A4. Nie zapomnijmy podciÄ…gnąć linii sygnaÅ‚owych rezystorami o wartoÅ›ci 10kΩ do zasilania 5V.
Należy zwrócić tutaj szczególnÄ… uwagÄ™ na wyjÅ›cie SQW/OUT, które również podciÄ…gamy rezystorem o wartoÅ›ci 10kΩ do zasilania 5V. Jest to element wymagany ponieważ wyjÅ›cie SQW/OUT dziaÅ‚a w ukÅ‚adzie otwartego drenu. Dodatkowo jeÅ›li chcemy podÅ‚Ä…czyć do niego diodÄ™ LED, należy podÅ‚Ä…czyć tutaj katodÄ™, anodÄ™ zaÅ› - do zasilania. JeÅ›li nie planujemy korzystać z SQW/OUT, możemy pozostawić je "w powietrzu".
Ja zamiast baterii 3V podłączyłem zwykłe zasilanie 3.3V z Arduino. W normalnych warunkach oczywiście skorzystajmy z baterii :)
Do obsÅ‚ugi moduÅ‚ów z ukÅ‚adami DS1307 przygotowaÅ‚em bibliotekÄ™ dla Arduino, którÄ… można pobrać z repozytorium Git: https://github.com/jarzebski/Arduino-DS1307. Bazuje ona bibliotekach JeeLabs oraz Adafruit, jednak oferuje ona dodatkowe funkcje do sterowania wyjÅ›ciem SQW/OUT, dostÄ™pu do pamiÄ™ci ukÅ‚adu oraz formatowanie daty na wzór date() z PHP.
#include <Wire.h>
#include <DS1307.h>
DS1307 clock;
RTCDateTime dt;
void setup()
{
Serial.begin(9600);
// Inicjalizacja DS1307
Serial.println("Inicjalizacja DS1307");;
clock.begin();
// Jeśli nie ustawiono daty, ustawiamy
if (!clock.isReady())
{
// Data i czas z momentu kompilacji
clock.setDateTime(__DATE__, __TIME__);
}
}
void loop()
{
dt = clock.getDateTime();
Serial.print("Raw data: ");
Serial.print(dt.year); Serial.print("-");
Serial.print(dt.month); Serial.print("-");
Serial.print(dt.day); Serial.print(" ");
Serial.print(dt.hour); Serial.print(":");
Serial.print(dt.minute); Serial.print(":");
Serial.print(dt.second); Serial.println("");
delay(1000);
}
Jeśli chcemy wyświetlić czas i datę w odpowiednim dla nas formacie, możemy skorzystać z funkcji dateFormat().
#include <Wire.h>
#include <DS1307.h>
DS1307 clock;
#include <DS1307.h>
void setup()
{
Serial.begin(9600);
// Inicjalizacja DS1307
Serial.println("Inicjalizacja DS1307");;
clock.begin();
// Jeśli nie ustawiono daty, ustawiamy
if (!clock.isReady())
{
// Data i czas z momentu kompilacji
clock.setDateTime(__DATE__, __TIME__);
// UNIX timestamp
// clock.setDateTime(1397408400);
// Recznie (YYYY, MM, DD, HH, II, SS
// clock.setDateTime(2014, 4, 13, 19, 21, 00);
}
}
void loop()
{
dt = clock.getDateTime();
Serial.print("Long number format: ");
Serial.println(clock.dateFormat("d-m-Y H:i:s", dt));
Serial.print("Long format with month name: ");
Serial.println(clock.dateFormat("d F Y H:i:s", dt));
Serial.print("Short format witch 12h mode: ");
Serial.println(clock.dateFormat("jS M y, h:ia", dt));
Serial.print("Today is: ");
Serial.print(clock.dateFormat("l, z", dt));
Serial.println(" days of the year.");
Serial.print("Actual month has: ");
Serial.print(clock.dateFormat("t", dt));
Serial.println(" days.");
Serial.print("Unixtime: ");
Serial.println(clock.dateFormat("U", dt));
delay(1000);
}
Jak już wiemy, DS1307 oferuje nam pamięć o rozmiarze 56 bajtów, której zawartość jest podtrzymywana bateryjnie. DostÄ™p do tej pamiÄ™ci odbywa siÄ™ poprzez dwie funkcje writeByte() oraz readByte(). W ramach przykÅ‚adu zostaÅ‚a ona zapisana wartoÅ›ciami 0xFF - 0xC8, a nastÄ™pnie odczytana.
#include <Wire.h>
#include <DS1307.h>
DS1307 clock;
char tmp[16];
void setup()
{
Serial.begin(9600);
// Inicjalizacja DS1307
Serial.println("Initialize DS1307");
clock.begin();
// Wypelniamy pamiec
Serial.println("Filling memory");
for (byte i = 0; i < 56; i++)
{
clock.writeByte(i, 255-i);
}
Serial.println("... Done");
Serial.println();
}
void loop()
{
// Odczyt pamieci
Serial.println("Reading memory");
Serial.println();
Serial.print(" ");
for (byte i = 0; i < 16; i++)
{
sprintf(tmp, "0x%.2X ", i, 2);
Serial.print(tmp);
}
Serial.println();
Serial.println("---------------------------------------------------------------------------------------");
for (byte j = 0; j < 4; j++)
{
sprintf(tmp, " 0x%.2X : ", (j*16), 2);
Serial.print(tmp);
for (byte i = 0; i < 16; i++)
{
if ((j*16 + i) > 55)
{
break;
}
sprintf(tmp, "0x%.2X ", clock.readByte(j*16 + i), 2);
Serial.print(tmp);
}
Serial.println();
}
delay(5000);
}
W tym przykładzie możemy zobaczyć jak włączyć generator przebiegu prostokątnego. Należy skonfigurować odpowiedni rejestr wybierając interesującą nas częstotliwość i włączyć SQW. Sygnał ten możemy wykorzystać do obsługi przerwania lub podłączyć diodę LED jako sygnalizator sekund, oszczędzając tym samym cyfrowy pin Arduino. Działanie możecie zobaczyć na filmie znajdujący się na końcu artykułu.
#include <Wire.h>
#include <DS1307.h>
DS1307 clock;
void setup()
{
Serial.begin(9600);
// Inicjalizacja DS1307
Serial.println("Initialize DS1307");;
clock.begin();
// Ustawiamy date
if (!clock.isReady())
{
// Data i czas z momentu kompilacji
clock.setDateTime(__DATE__, __TIME__);
}
// Wybieramy 1Hz
clock.setOutput(DS1307_1HZ);
switch (clock.getOutput())
{
case DS1307_LOW: Serial.println("SQW = LOW"); break;
case DS1307_HIGH: Serial.println("SQW = HIGH"); break;
case DS1307_1HZ: Serial.println("SQW = 1Hz"); break;
case DS1307_4096HZ: Serial.println("SQW = 4096Hz"); break;
case DS1307_8192HZ: Serial.println("SQW = 8192Hz"); break;
case DS1307_32768HZ: Serial.println("SQW = 32768Hz"); break;
default: Serial.println("SQW = Unknown"); break; }
}
void loop()
{
}
WyjÅ›cie SQW możemy również skonfigurować jako dodatkowy pin cyfrowy OUT, który traktujemy tak samo jak pin cyfrowy w Arduino. DziaÅ‚anie możecie zobaczyć na filmie znajdujÄ…cy siÄ™ na koÅ„cu artykuÅ‚u.
#include <Wire.h>
#include <DS1307.h>
DS1307 clock;
boolean state;
void setup()
{
Serial.begin(9600);
// Inicjalizacja DS1307
Serial.println("Inicjalizacja DS1307");;
clock.begin();
}
void loop()
{
// Ustawiamy wyjscie
clock.setOutput(state);
// Zmieniamy stan
state = !state;
// Opoznienie
delay(100);
}
Bardzo ważnym elementem w zegarach czasu rzeczywistego jest odpowiedni dobór i montaż zewnÄ™trznego rezonatora kwarcowego. Najczęściej stosowane sÄ… tutaj tak zwane kwarce zegarkowe 32768 Hz o dokÅ‚adnoÅ›ci ±20ppm. Aby zrozumieć dokÅ‚adniej problem dokÅ‚adnoÅ›ci zegarów RTC musimy zastanowić siÄ™, jaki może być bÅ‚Ä…d czasu dla kwarców tego typu.
PPM to skrót od angielskiego terminu Parts Per Milion. Innymi sÅ‚owy oznacza, o ile części na jeden milion może pomylić siÄ™ ów kwarc. Na poczÄ…tek obliczmy sobie procentowy bÅ‚Ä…d dla typowej wartoÅ›ci 20ppm.
δF = = (20ppm / 1000000) * 100% = 0,002%
Niby nie dużo, ale przeliczając to na dobę, uzyskujemy całkiem pokażną wartość:
(0.002 [%] * 86400 [s]) / 100% = 1.728 [s]
W ciÄ…gu doby nasz zegarek może pomylić siÄ™ w skrajnym przypadku o 1.728 sekundy, a w ciÄ…gu miesiÄ…ca prawie o 53 sekund, rocznie zaÅ› prawie o 10 minut. OczywiÅ›cie wartość ta może być niższa ze wzglÄ™du na to, że kwarc może pomylić siÄ™ o mniejszÄ… ilość impulsów, zarówno w górÄ™ jak i w dóÅ‚.
Należy zwrócić uwagÄ™, że bÅ‚Ä…d ten jest silnie zależny od temperatury otoczenia w jakich pracuje kwarc. Typowe kwarce zegarkowe optymalizowane sÄ… najczęściej do pracy w temperaturze 25°C, gdzie bÅ‚Ä…d powinien wynosić 0%. OczywiÅ›cie w praktyce nie jest to możliwe. Zależność wielkoÅ›ci ppm od temperatury przedstawia krzywa obok, która jest zawsze podawana w karcie katalogowej danego kwarcu.
Jak widzimy na wykresie, dokÅ‚adność ±20ppm obowiÄ…zuje dla zakresu temperatur 2°C ÷ 48°C. Analogicznie - jeśłi nasz kwarc pracowaÅ‚by w temperaturze od 10°C ÷ 40°C to dokÅ‚adność takiego kwarcu powinna mieÅ›cić siÄ™ w granicach ±10ppm.
JeÅ›li jednak nasz zegar myli siÄ™ powiedzmy o 5 sekund na dobÄ™, a nie pracuje w ekstremalnej temperaturze -10°C lub 60°C, to jego dokÅ‚adność jest o wiele mniejsza i wynosi:
5 [s] / 86400 [s] = 0,000057 = 57 ppm
Otrzymane 57 ppm oznacza tyle, że diabeł się nim interesuje :) Ale na poważnie, przyczyn może być kilka:
A wiÄ™c, aby zminimalizować bÅ‚Ä…d zegara musimy posiadać odpowiedni kwarc. Dodatkowo powinien on znajdować siÄ™ jak najbliżej nóżek DS1307, ale w wiÄ™kszej odlegÅ‚oÅ›ci od Å›cieżek sygnaÅ‚owych. ZalecanÄ… metodÄ… jest również otoczenie poÅ‚Ä…czenia kwarcu z ukÅ‚adem pierÅ›cieniem masy (Guard ring). JeÅ›li mamy możliwość zaprojektowania pÅ‚ytki na dwóch warstwach, możemy otoczyć pole na którym znajduje siÄ™ kwarc dodatkowÄ… masÄ… (Local ground plane) i przylutować do niej metalowÄ… obudowÄ™. Odradzane jest również prowadzenie jakichkolwiek Å›cieżek sygnaÅ‚owych w tych obszarach.
JeÅ›li speÅ‚nimy wszystkie powyższe warunki... to i tak nasz zegarek nie bÄ™dzie odmierzaÅ‚ czasu dokÅ‚adnie (tada!). Nawet jeÅ›li zapewnimy mu przez caÅ‚y czas jego pracy idealnÄ… temperaturÄ™ 25°C.
Jak już pewnie zdążyliÅ›cie zauważyć, najwiÄ™kszym problemem jest tutaj czynnik temperatury. Istnieje kilka metod poprawy dokÅ‚adnoÅ›ci kwarców. Możemy zastosować trymer poprawiajÄ…c jego stabilność, możemy co jakiÅ› czas synchronizować czas z zewnÄ™trznego wzorca (np.: komputera). Możemy oczywiÅ›cie skorzystać z innych ukÅ‚adów RTC z wbudowanÄ… kompensacjÄ… temperatury (DS3231). Jednak jeÅ›li zależy nam na DS1307 warto zainteresować siÄ™ oscylatorem DS32kHz z kompensacjÄ… temperaturowÄ… (TCXO).
DS32kHz dostępny jest w obudowach SO oraz EDIP. W naszym przykładzie wykorzystamy ten w obudowie EDIP.
Dokumentacja techniczna: http://www.jarzebski.pl/datasheets/DS32kHz.pdf
Jego dokÅ‚adność w zakresie temperatury 0°C ÷ 40°C wynosi aż ±2ppm, a w zakresie temperatur przemysÅ‚owych -40°C ÷ 85°C aż ±7.5ppm. Dla porównania obliczmy odchyÅ‚kÄ™ czasu dla 2ppm:
δF = = (2ppm / 1000000) * 100% = 0,0002%
(0.0002 [%] * 86400 [s]) / 100% = 0.1728 [s] na dzień
0.1728 [s] * 355 [dni] = 61 [s] na rok
podczas gdy standardowy kwarc zegarkowy
1.728 [s] * 355 [dni] = 613 [s] na rok
PodÅ‚Ä…czenie DS1307 pozostaje bez zmian. JedynÄ… różnicÄ… jest podÅ‚Ä…czenie pod X1 i X2 rezystora o wartoÅ›ci 33kΩ. WyjÅ›cie 32KHZ z DS32kHz (nóżka 12) podÅ‚Ä…czamy natomiast do X1 poprzez szeregowo poÅ‚Ä…czony rezystor 1MΩ i kondensator 100pF. OczywiÅ›cie DS32kHz wymaga również zasilania bateryjnego w przypadku zaniku zasilania gÅ‚ównego. CaÅ‚y ukÅ‚ad zasilamy napiÄ™ciem 5V i podÅ‚Ä…czamy do masy (wymagane jest również podÅ‚Ä…czenie wszystkich pinów NC do masy). DS32kHz posiada jeszcze inne warianty zasilania, ale o nich możecie poczytać w dokumencie PDF (strona 7).
Od teraz, wasz zegarek będzie stabilny jak skała.
Dokumentacja techniczna: http://www.jarzebski.pl/datasheets/DS32kHz.pdf
Dokumentacja techniczna: http://www.jarzebski.pl/datasheets/DS1307.pdf
Biblioteka Arduino: https://github.com/jarzebski/Arduino-DS1307