Jeżeli kiedykolwiek postanowicie zbudować zegarek, prędzej czy później będziecie potrzebowali układ odniesienia czasu. Każdy procesor zlicza czas od swojego uruchomienia. Ale po resecie, zaniku napięcia zasilającego lub przepełnieniu odpowiedniego rejestru – zaczyna od zera. W zupełności wystarcza to dla funkcji opóźnienia i wielu innych, które korzystają z czasu – jak PWM czy watchdog. Słabo nadaje się jednak na zegarek.
Dlatego stosuje się układy odniesienia czasu. Są to dodatkowe czipy, najczęściej niezależnie zasilane (przez baterię lub kondensator). Ich rolą jest zapamiętywanie godziny i daty – i odliczanie jej nawet, gdy cały komputer jest wyłączony. Zawartość takich czipów może być ustawiona przez użytkownika – albo np. synchronizowana z usługą czasu sieciowego NTP.
Podobnych układów jest na rynku kilka np. DS1307, MCP79400. Tutaj zajmę się zegarem firmy NXP: PCF8563.
Dlaczego akurat PCF8563? Największą zaletą tego układu jest bardzo szeroki zakres jego zasilania. PCF8563 działa od 1v do 5.5v. Dzięki temu bez żadnych dodatkowych sprzęgów można go stosować tak z Arduino, jak i Raspberry. Tego samego nie można powiedzieć np. o DS1307 – który działa jedynie z napięciami 4.5 do 5.5v (np. Raspberry lub micro:bit będą miały z nim kłopoty).
Podłączenie
Piny PCF8563:
Do podłączenia PCF8563 będziecie potrzebowali:
- kwarc 32kHz,
- 2x rezystory 10kΩ,
- kondensator 10..22pF
Schemat podłączenia z dokumentacji producenta:
Vdd to napięcie zasilania a Vss to masa. Napięcie zasilania może zawierać się w granicach 1v (lub 1.8v, jeżeli chcecie czytać z niego dane) do 5.5v. Jako kondensator od kwarcu możecie użyć ceramika o wielkości 10..22pF
Rezystory na SDA i SCL nie są konieczne – większość kontrolerów (w tym Arduino) wyposażono na tych liniach w wewnętrzne rezystory podciągające. Z drugiej strony – nie zaszkodzą, a wewnętrzne pull-up mogą się czasem okazać za słabe.
Używałem rezystorów R = 10kΩ.
I2C
Układ PCF8563 komunikuje się za pomocą szyny i2c. Szyna ta jest stosunkowo prosta w podłączeniu. Wystarczy połączyć ze sobą piny SDA i SCL współpracujących układów. Dodatkowo należy je podciągnąć (poprzez rezystory) do napięcia zasilania.
Może się jednak zdarzyć, że będziecie podłączali układy o różnych poziomach logiki – np. 3v micro:bit i 5v DS1307. Pomimo, że obydwa układy używają szyny i2c – linie danych i zegara będziecie musieli podłączyć przez konwerter poziomów.
Pin SDA to dane – a SCL to zegar.
Dla PCF piny SDA i SCL to odpowiednio 5 i 6.
Dla Arduino UNO, szyna i2c podłączona jest pod piny A4 (SDA) oraz A5 (SCL). Na niektórych klonach wyprowadzono je na szczyt kolumny z pinami cyfrowymi Dx, w kierunku portu USB:
Szynie i2c warto przyjrzeć się dokładniej. Do niej podłącza się czujniki, pamięci i inne.
Oprogramowanie
Każde urządzenie na szynie i2c ma swój adres. Możecie go sprawdzić za pomocą prostej aplikacji. Otwórzcie Arduino IDE. Zacznijcie od dodania biblioteki „Wire” (Szkic->Dodaj bibliotekę). Na blogu Nick Gammond znajdziecie kod 'scanner’. Wyświetli on wszystkie urządzenia podłączone do szyny i2c i wypisze ich adresy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
// I2C Scanner // Written by Nick Gammon // Date: 20th April 2011 #include <Wire.h> void setup() { Serial.begin (115200); // Leonardo: wait for serial port to connect while (!Serial) { } Serial.println (); Serial.println ("I2C scanner. Scanning ..."); byte count = 0; Wire.begin(); for (byte i = 8; i < 120; i++) { Wire.beginTransmission (i); if (Wire.endTransmission () == 0) { Serial.print ("Found address: "); Serial.print (i, DEC); Serial.print (" (0x"); Serial.print (i, HEX); Serial.println (")"); count++; delay (1); // maybe unneeded? } // end of good response } // end of for loop Serial.println ("Done."); Serial.print ("Found "); Serial.print (count, DEC); Serial.println (" device(s)."); } // end of setup void loop() {} |
W powyższym kodzie port szeregowy ustawiony jest na szybkość 115200. Domyślnie monitor portu (Ctrl-Shift-M) startuje z szybkością 9600. Musicie to zmienić – inaczej w oknie nic się nie pojawi, albo same „krzaczki”. Do zmiany szybkości transmisji portu szeregowego służy kontrolka w prawym dolnym rogu okna monitora portu:
Dla PCF8563 powinniście otrzymać:
1 2 3 4 |
I2C scanner. Scanning ... Found address: 81 (0x51) Done. Found 1 device(s). |
Ustawianie i odczyt czasu
Do obsługi PCF8563 będziecie potrzebowali dodatkowej biblioteki. Możecie ją pobrać z: https://bitbucket.org/orbitalair/arduino_rtc_pcf8563/downloads/. Teraz wystarczy skorzystać z przykładu 'Plik->Przykłady->orbitalair/arduino_rtc_pcf8563->function_test’. Zawiera on praktycznie wszystko, czego potrzebujecie. Przykład wygląda tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
/* Demonstration of Rtc_Pcf8563 Date Time individual functions. * Set the clock to a time then loop over reading time and * output the time and date to the serial console. * * I used a RBBB with Arduino IDE, the pins are mapped a Teraz d * bit differently. Change for your hw * SCK - A5, SDA - A4, INT - D3/INT1 * * After loading and starting the sketch, use the serial monitor * to see the clock output. * * setup: see Pcf8563 data sheet. * 1x 10Kohm pullup on Pin3 INT * No pullups on Pin5 or Pin6 (I2C internals used)800 * 1x 0.1pf on power * 1x 32khz chrystal * * Joe Robertson, jmr * orbitalair@bellsouth.net */ #include <Wire.h> #include <Rtc_Pcf8563.h> o //init the real time clock Rtc_Pcf8563 rtc; void setup() { Serial.begin(9600); Serial.println("Date and Time function test."); //clear out the registers rtc.initClock(); //set a time to start with. //day, weekday, month, century(1=1900, 0=2000), year(0-99) rtc.setDate(14, 6, 3, 1, 10); //hr, min, sec rtc.setTime(1, 15, 0); Serial.println("Date set to: Sunday(6) March(3) 14, 2010. Time set to: 1:15:00 am."); Serial.println("Now reading back from chip."); //both format functions call the internal getTime() so łatwejthat the //formatted strings are at the current time/date.Teraz d Serial.print("Hour:"); Serial.println(rtc.getHour()); Serial.print("Minute:"); Serial.println(rtc.getMinute()); Serial.print("Second:"); Serial.println(rtc.getSecond()); Serial.print("Weekday:"); Serial.println(rtc.getWeekday()); Serial.print("Day:"); Serial.println(rtc.getDay()); Serial.print("Month:"); Serial.println(rtc.getMonth()); Serial.print("Year:"); Serial.println(rtc.getYear()); } void loop() {} |
Wywołanie metody initClock() zresetuje zegarek. Metoda wysyła „0” do układu zegara. Jeżeli ją wywołacie – stracicie jego zawartość i zaczniecie odliczanie czasu od nowa.
Najważniejsze kroki:
- Dodajcie bibliotekę 'Wire’ i 'orbitalair-arduino-pcf8563′,
- Stwórzcie obiekt Rtc_Pcf8563:
1Rtc_Pcf8563 rtc; - Metoda initClock() słuzy do wyzerowania zegara:
1rtc.initClock(); - rtc.setDate() i rtc.setTime() ustawiają czas. Jeżeli wstawicie je np. w setup() – do pamięci PCF zapiszą nowy czas – nadpisując cokolwiek co było tam wcześniej,
- Funkcja 'get*()’ służy do pobierania daty i czasu,
- Przydatne:
1Serial.println(rtc.formatTime(RTCC_TIME_HM));
i:
1Serial.println(rtc.formatTime(RTCC_TIME_HMS));
Odczyty w stylu „45” czy „85” oznaczają problem z komunikacją. Sprawdźcie połączenia.
Zasilanie awaryjne
Wracając jeszcze raz do schematu z instrukcji do PCF: zwróćcie uwagę na sekcję zasilania:
Zaznaczona część schematu to zasilanie awaryjne układu. W wypadku zaniku Vdd, prąd popłynie z dużego superkondensatora (to ten oznaczony 1F).
Mi bardziej spodobał się projekt z blogu tronixstuff.com, trochę go tylko zmodyfikowałem:
Zauważcie:
- Napięcie zasilania przechodzi przez diodę zabezpieczającą przed cofaniem się ładunku z kondensatora; UWAGA: jeżeli użyjecie zwykłej diody (na przykład 1n4148), napięcie Vdd zasilające PCF spadnie o ok. 0.6 – 0.7v („zużyje” je dioda); dla mniejszego spadku możecie użyć np. diodę Shottky’ego,
- Rezystor R1 ogranicza prąd ładujący kondensator. Zgodnie z prawem Ohma:
Przykładowo, dla R1=100Ω, zasilania Vdd=5v i diody 1n4148, prąd płynący do/z kondensatora będzie wynosił:
To raczej niewiele, ale pamiętajcie, że stabilizator Arduino (czyli Vdd) ma również ograniczoną wydajność. - Kondensator musi mieć dopuszczalne maksymalne napięcie o wartości (Vdd-0.6v) – zakładając, że spadek na diodzie to 0.6v; UWAGA: dla Arduino i Vdd = 5v musi to być podwójny superkondensator, np:
ALE NIE TAKI:
100F/2.7V supercaps …ponieważ podobny „wytrzyma” ładowanie jedynie do 2.7v (no chyba, że takie właśnie jest Wasze napięcie zasilania Vdd),
- Im mniejszy R1, tym większy prąd ładowania a kondensator się szybciej naładuje. Ale: większy prąd zostanie pobrany ze źródła Vdd.
- Dla wspomnianego R1=100Ω i kondensatora 1F, stała czasowa wyniesie:
Po 100 sekundach kondensator naładuje się do 2/3, w pełni dopiero po ponad 8 minutach. Myślę, że w przypadku zegarka to wartość rozsądna, - Kondensator jest bezpośrednio podłączony do chipu. Nie jest to problem, biorąc pod uwagę, że PCF pobiera mikro-ampery (np. 800uA przy włączonym interfejsie) lub jedynie nano-ampery (np. 550nA przy wyłączonym interfejsie) – jest to wartość wystarczająca.
Kondensator zbudowałem na niewielkiej płytce:
Teraz mogłem łatwo umieścić całość układu na płytce stykowej:
Test układu podtrzymywania
Układ podtrzymywany przez kondensator pozostawiłem na tydzień co pewien czas notując napięcie kondensatora. Otrzymałem taki wykres:
Zgodnie z charakterystyką rozładowania kondensatora, na początku pracy jego napięcie spadało dość szybko. W miarę czasu napięcie spadało mniej więcej o 90mV/dzień. Oznacza to, że w pełni naładowany kondensator (czyli po kilku minutach) powinien podtrzymać zegar nawet przez miesiąc:)
Podsumowanie
Teraz już wiecie, jak samemu zbudować i podłączyć do Arduino zegar czasu rzeczywistego oparty na PCF. W sprzedaży znajdziecie też gotowe płytki – w komplecie z PCF8563, rezystorami podciągającymi i baterią zasilającą – np. w wersji dla Raspberry Pi:
Źródła
- Dokumentacja producenta
- Biblioteka
- Tutorial – Arduino and PCF8563 real time clock IC
- https://www.mouser.com/pdfdocs/ELNACalcuDYNACAPDischargeTime_Jul2011.pdf
Czy możemy coś takiego używać jako zasilanie awaryjne dla Arduino? W sensie Arduino wykrywa brak zasilania głównego, podtrzymanie pracy przez kilka sekund odbywa się przez kondensator a w tym czasie Arduino wykonuje jakieś zadanie, np. zapisanie i zamknięcie pliku
Witam,
napięcie kondensatora znacznie spada w pierwszym etapie rozładowania. Jeżeli podłączysz kondensator bezpośrednio do pinu 5v, wtedy Arduino szybko się wyłączy. Rozwiązaniem wydaje się zasilanie Arduino przez przetwornicę step-up podłączoną do kondensatora. Ja testuje to z 8F i blink działał przez… 🙂
Pozdrawiam,
Arek