[117] RTC - układy czasu rzeczywistego cz. 2
![[117] RTC - układy czasu rzeczywistego cz. 2](/_next/image?url=https%3A%2F%2Farduino.pl%2Fimgproxy%2F3lPdFamNavu5_mzl8KNatAnyKR4M3Y-7R-474_sCL-Y%2Ff%3Awebp%2Fw%3A1200%2FbG9jYWw6Ly8vaW1hZ2VzLzAtNjg1L2FmYjIyLzNkMmQ0LzAtNjg1YWZiMjIzZDJkNDIzMjE0MDAyOC5wbmc%3D.webp&w=3840&q=75)
W poprzednim artykule opisałem zagadnienie zegarów RTC i przedstawiłem przykład: układ DS1302. Czas na nieco nowocześniejszy DS1307. Tutaj mamy już klasyczną magistralę I2C, więc potrzeba tylko dwóch linii do rozmów ze scalakiem. Pominę nieistotne dla nas dodatkowe wyjście, na którym może się pojawić jedna z częstotliwości do wyboru: 1 Hz, 4 lub 8 kHz albo częstotliwość kwarcu. Układ posiada 56 bajtów użytkownika, ale wymaga już pięciowoltowego standardu zasilania. Posiada też stały adres magistrali (dziesiętnie: 104), którego nie da się zmienić.

Ponieważ układ używa innej magistrali, należy go podłączyć do linii SDA i SCL. Na płytce Uno znajdziemy osobne piny połączone z tą magistralą, ale są to tak naprawdę linie A4 i A5, bo mają one – jak wszystkie inne – wielokrotną funkcjonalność.

Płytka, której tu użyłem zawiera dodatkowo kostkę pamięci EEPROM o rozmiarze 4kB. Można oczywiście użyć jej sobie, ale o tej pamięci kiedyś już pisałem i dziś temat pominę. Kostka nie przeszkadza niczemu i możemy ją zignorować.
#include <DFRobot_DS1307.h> // Biblioteka obsługująca zegar DS1307
DFRobot_DS1307 zegar; // Deklaracja nazwy zegara.
const byte resetujZegar = 5; // Stan wysoki zezwala na reset zegara po resecie Arduino.
void setup() {
pinMode(resetujZegar, INPUT_PULLUP); // Deklaruj port resetu zegara.
Serial.begin(115200); // Inicjuj monitor.
while (!(zegar.begin())) { // Inicjuj sensor zegara.
Serial.println("Brak łączności z układem DS1307"); // Brak łączności.
delay(1000);
}
if (digitalRead(resetujZegar) == HIGH) { // Jeśli podczas resetu Arduino przycisk jest wciśnięty...
// Metoda pierwsza - wysyłanie ciągu danych.
unsigned int setTimeBuff[7] = { 57, 59, 23, 2, 31, 12, 2024 }; // Ustaw: sekundy, minuty, godziny, dzień tygodnia, miesiąca, miesiąc, rok.
zegar.setTime(setTimeBuff); // Wyślij dane do układu zegara.
// Metoda druga - ustawianie wybranych wartości.
zegar.setTypeTime(zegar.eSEC, 57); // Sekunda.
zegar.setTypeTime(zegar.eMIN, 59); // Minuta.
zegar.setTypeTime(zegar.eHR, 23); // Godzina.
zegar.setTypeTime(zegar.eDOW, 2); // Dzień tygodnia (1-7, nie jest obliczany wg kalendarza).
zegar.setTypeTime(zegar.eDATE, 31); // Dzień miesiąca (uwzględniane są lata przestępne).
zegar.setTypeTime(zegar.eMTH, 12); // Numer miesiąca.
zegar.setTypeTime(zegar.eYR, 2024); // Rok (pełna, czterocyfrowa liczba).
}
}
void loop() {
Serial.print(zegar.getTypeTime(zegar.eYR)); // Rok (pełna, czterocyfrowa liczba).
Serial.print("-");
Serial.print(zegar.getTypeTime(zegar.eMTH)); // Numer miesiąca.
Serial.print("-");
Serial.print(zegar.getTypeTime(zegar.eDATE)); // Dzień miesiąca (uwzględniane są lata przestępne).
Serial.print(" (");
Serial.print(zegar.getTypeTime(zegar.eDOW)); // Dzień tygodnia (1-7, nie jest obliczany wg kalendarza).
Serial.print(") ");
Serial.print(zegar.getTypeTime(zegar.eHR)); // Godzina.
Serial.print(":");
Serial.print(zegar.getTypeTime(zegar.eMIN)); // Minuta.
Serial.print(":");
Serial.println(zegar.getTypeTime(zegar.eSEC)); // Sekunda.
delay(1000);
}
Program będzie bardzo podobny, choć oczywiście w szczegółach będzie się różnił. Bibliotek także znajdziemy wiele, zdecydowałem się na firmową od producenta płytki: DFRobot_DS1307.h ponieważ w razie potrzeby wspiera także wspomnianą pamięć. Poza zmianami w deklaracjach związanych z biblioteką, zmienia się także format wyrażeń związanych z komunikacją z zegarem. Jego ustawianie możemy zrealizować na dwa sposoby: ciągiem – jak w poprzednim przykładzie, albo wygodniej, zmienna po zmiennej.
// Metoda druga - ustawianie wybranych wartości.
zegar.setTypeTime(zegar.eSEC, 57); // Sekunda.
zegar.setTypeTime(zegar.eMIN, 59); // Minuta.
zegar.setTypeTime(zegar.eHR, 23); // Godzina.
zegar.setTypeTime(zegar.eDOW, 2); // Dzień tygodnia (1-7, nie jest obliczany wg kalendarza).
zegar.setTypeTime(zegar.eDATE, 31); // Dzień miesiąca (uwzględniane są lata przestępne).
zegar.setTypeTime(zegar.eMTH, 12); // Numer miesiąca.
zegar.setTypeTime(zegar.eYR, 2024); // Rok (pełna, czterocyfrowa liczba).
Dzięki temu możemy ograniczyć się tylko do zerowania sekund bądź ustawiania samego czasu. Odczyt także jest możliwy jednym ciągiem, ale jego użyteczność jest niewielka, bo wynik należy rozbijać na składowe: nie mówię już o realizacji budzika, ale samo formowanie cyfr będzie tego wymagać. W przykładzie zademonstrowałem sposób kolejnego uzyskiwania danych. Przy ich wyświetlaniu, podobnie jak w poprzednim szkicu, musimy pamiętać o dokładaniu zer wiodących dla minut i sekund, inaczej prezentacja czasu będzie wyglądała źle. Trzeba też pamiętać o przesuwaniu pozostałych wartości, w zależności od tego, czy będą jedno, czy dwucyfrowe.
Po skompilowaniu układ będzie pracował dokładnie tak samo. Pamiętajmy o wciśnięciu przycisku za pierwszym razem, gdy wstawimy baterię. Oczywiście po odłączeniu zasilania głównego zegar powinien pracować nadal.

Na koniec zostawiłem sobie nowe Uno. Zawiera ono bowiem w strukturze RTC i grzechem byłoby go nie wykorzystać.

Oczywiście ten element także potrzebuje podtrzymania zasilania baterią i napięcie podaje się go na jeden z trzech dodatkowych pinów, nieistniejących w poprzednich odsłonach Uno.
#include "RTC.h" // Biblioteka obsługująca zegar Uno R4
const byte resetujZegar = 5; // Stan wysoki zezwala na reset zegara po resecie Arduino.
void setup() {
pinMode(resetujZegar, INPUT_PULLUP); // Deklaruj port resetu zegara.
Serial.begin(115200); // Inicjuj monitor.
while (!Serial) {} // Czekaj dopóki nie będzie połączenia z portem szeregowym.
RTC.begin(); // Inicjuj sensor zegara.
if (digitalRead(resetujZegar) == HIGH) { // Jeśli podczas resetu Arduino przycisk jest wciśnięty...
RTCTime zapiszCzas(31, Month::DECEMBER, 2024, 23, 59, 57, DayOfWeek::TUESDAY, SaveLight::SAVING_TIME_ACTIVE); // Ustaw: dzień miesiąca, miesiąc - słownie, rok, godziny, minuty, sekundy, dzień tygodnia - słownie, czas letni.
RTC.setTime(zapiszCzas); // Wyślij dane do układu zegara.
}
}
void loop() {
RTCTime odczytajCzas; // Powołaj bufor na czas odczytany z układu RTC.
RTC.getTime(odczytajCzas); // Załaduj go wartościami.
Serial.print(odczytajCzas.getYear()); // Rok (pełna, czterocyfrowa liczba).
Serial.print("-");
Serial.print(Month2int(odczytajCzas.getMonth())); // Numer miesiąca (wymaga konwersji z reprezentacji tekstowej).
Serial.print("-");
Serial.print(odczytajCzas.getDayOfMonth()); // Dzień miesiąca (uwzględniane są lata przestępne).
Serial.print(" (");
DayOfWeek dzienTygodnia = odczytajCzas.getDayOfWeek(); // Dzień tygodnia (1-7, nie jest obliczany wg kalendarza, wymaga konwersji z reprezentacji tekstowej).
Serial.print((int)dzienTygodnia);
Serial.print(") ");
Serial.print(odczytajCzas.getHour()); // Godzina.
Serial.print(":");
Serial.print(odczytajCzas.getMinutes()); // Minuta.
Serial.print(":");
Serial.println(odczytajCzas.getSeconds()); // Sekunda.
delay(1000);
}
Zegar wraz z biblioteką ma więcej funkcji, między innymi możliwość wybudzenia uśpionej płytki o ustalonym czasie. Pomińmy na razie wszystkie możliwości poza podstawowymi. Biblioteka jest nieco zawiła i kiepsko opisana, toteż chwilę zajęło mi stworzenie kopii logicznej poprzednich szkiców. Ale wszystko już działa jak należy. Jak zwykle importujemy bibliotekę – tym razem RTC.h Samo zapisanie wartości w RTC różni się nie tylko kolejnością, ale też wymogiem określenia miesiąca i dnia tygodnia w postaci ciągów liter. Doszedł dodatkowo parametr czasu letniego.
RTCTime zapiszCzas(31, Month::DECEMBER, 2024, 23, 59, 57, DayOfWeek::TUESDAY, SaveLight::SAVING_TIME_ACTIVE); // Ustaw: dzień miesiąca, miesiąc - słownie, rok, godziny, minuty, sekundy, dzień tygodnia - słownie, czas letni.
Odczyt także nie jest klarowny. Wymienione dwie wartości muszą być tłumaczone za pomocą instrukcji zwracającej liczbę. Kiedyś rozwinę temat, natomiast teraz po prostu skompilujmy kod i będziemy się cieszyć działającym zegarem.
W każdym przypadku mamy już gotowy moduł do budowy choćby budzika, o którym pisałem niedawno. Dokładność takiego zegara będzie sięgała sekundy na dobę, co jest dużo wyższą wartością od zegara czysto programowego. Wciąż jednak zegar taki należy ustawiać ręcznie i kontrolować wartości przynajmniej raz na miesiąc. Mając WiFi można uzyskiwać zawsze dokładny czas nez ustawiania czegokolwiek. Zresztą nie jest to jedyna metoda, ale o tym napiszę kiedy indziej.