Arduino i czas czyli DS1307

Arduino nie ma wbudowanego zegara. Znaczy to, że pewne operacje związane z czasem da się przeprowadzać (opóźnienia o zadany interwał), ale już kontrolowanie czasu i daty nie jest takie proste.

Aby Arduino mogło wiedzieć dokładnie jaki jest czas potrzebny jest zewnętrzny układ zegara. Takim układem jest Dallas DS1307, dostępny na Nettigo.pl w formie modułu do Arduino. Moduł ten posiada podtrzymywanie bateryjne, więc czas raz ustawiony nie zostaje skasowany po wyłączeniu Arduino.

Montaż

Moduł jest zlutowany, ale kwestia podłączenia do Arduino pozostaje otwarta. Moduł wygląda tak:

Moduł zegara DS1307
Moduł zegara DS1307

W lewym górnym rogu znajduje czteropinowe złącze o rastrze (odstępie między pinami) 2.54 mm. Niestety nie posiadamy na razie w ofercie kabelków do tego złącza. Pozostaje kombinowanie samemu, lub podłączenie się przez gniazdo (do ICSP) i goldpiny. Elementy te są dostępne razem z modułem (jak widać na zdjęciu), ale trzeba je przylutować do modułu (proste nawet dla początkujących).

Korzystając z kabelka możemy moduł podłączyć do dowolnego kontrolera wspierającego protokół I2C.

Gniazdo do podłączenia ICSP (z którego moduł w takim układzie bierze zasilanie) ma dwie pozycje w które można go przylutować. Jedna jest dla Arduino Duemilanove (i klonów zgodnych jeśli chodzi o rozmiary i wyprowadzenie pinów) lub Seeeduino Mega. Moduł przy takim podłączniu (przez ICSP i goldpiny) nie współpracuje z Arduino Mega (wzajemne położenie pinów ICSP i wyjścia magistrali I2C są różne).

Oczywiście zawsze zostaje opcja przylutowania kabelków zamiast goldpina oraz ICSP i wtedy możemy podłączyć się do dowolnego mikrokontrolera wspierającego I2C. Jak wygląda moduł z przylutowanym gniazdem i goldpine oraz założony na Arduino pokazują poniższe zdjęcia:

Moduł RTC zlutowany
Moduł RTC zlutowany
Moduł DS1307 założony na Arduino
Moduł DS1307 założony na Arduino

Co możemy zrobić?

Zbudujemy teraz prosty zegar wyświetlający czas i datę na wyświetlaczu LCD. Podłączenie wyświetlacza jest identyczne jak w opisie LCD Kitu. Podłączamy LCD, zakładamy zlutowany moduł zegara i wygląda to mniej więcej tak:

Prosty zegar
Prosty zegar

Co widać na zdjęciu? Oprócz LCD Kitu widać połowę ScrewShielda, czyli rozwiązania na wypadające kabelki. Dzięki niemu podłączenie jest solidne i nie wypada nic nawet gdy lekko zostanie szarpnięte.

Moduł zegara założony w ten sposób zasłania złącza Analog oraz zasilania, co na pierwszy rzut oka utrudnia podłączanie. Faktycznie po założeniu modułu, raczej nie ma mowy o podłączeniu czegoś do wejść analogowych, ale jeżeli wygniemy kabelek korzystając z małych cążków możemy podłączyć się bez problemu przed założeniem modułu a na dodatek, przyciśnie on kable powodując że nie będą one łatwo wypadać. Tak właśnie zasilanie jest doprowadzone do modułu LCD:

Wygięty kabelek do podłączenia zasilania
Wygięty kabelek do podłączenia zasilania
Złożona całość
Złożona całość

Pozostaje tylko oprogramować całość. Oto szkic napędzający całość:

#include <LiquidCrystal.h>
#include 
#include <Wire.h>
#include 
#include 

//Ustawić zegar w setup czy nie?
boolean SETUP=false;

LiquidCrystal lcd (12,11,10,9,8,7);

void setup()
{
  Serial.begin(9600);
  lcd.begin(16,2);
  if (SETUP) {
    RTC.stop();

  RTC.set(DS1307_SEC,1);        //set the seconds
  RTC.set(DS1307_MIN,49);     //set the minutes
  RTC.set(DS1307_HR,19);       //set the hours
  RTC.set(DS1307_DOW,6);       //set the day of the week
  RTC.set(DS1307_DATE,1);       //set the date
  RTC.set(DS1307_MTH,5);        //set the month
  RTC.set(DS1307_YR,10);         //set the year
  RTC.start();
  } 


}

char time [20];
char date [20];

void getTime(char *t, char *d) {
  sprintf(t,"%.2i:%.2i:%.2i",
   RTC.get(DS1307_HR, true),
   RTC.get(DS1307_MIN, false),
   RTC.get(DS1307_SEC, false)
   );
  sprintf(d, "%i/%i/%i",
   RTC.get(DS1307_DATE, false),
   RTC.get(DS1307_MTH, false),
   RTC.get(DS1307_YR, false)
 );  
}
void loop()
{

  getTime(time, date);
  Serial.println(time);
  lcd.setCursor(0,0);
  lcd.print(time);
  lcd.setCursor(0,1);
  lcd.print(date);
  delay(1000);

}


Biblioteka do obsługi modułu jest do ściągnięcia ze strony produktu, w zakładce Pliki.

Biblioteka dostarczona do modułu zajmuje się komunikacją z DS1307 poprzez I2C. Magistrala I2C na Arduino jest wyprowadzona w wejściach analogowych 4 i 5 – z nich nie będzie można korzystać do odczytywania wartości analogowych.

W szkicu zdefiniowana jest zmienna SETUP, która ustawiona na true spowoduje ustawienie zegara na z góry zaprogramowany czas. Zrobione to jest tak aby uprościć ninejszy szkic.

Czyli w treści programu w setup ustawiamy jakiś czas z nieodległej przyszłości. SETUP ustawiamy na true i wgrywamy szkic. Arduino przestawi zegar, ale na razie się nie przejmujemy, że czas jest z przyszłości. Czekamy aż nadejdzie godzina ustawiona w setup i w tym momencie albo włączamy Arduino ponownie, albo wgrywam szkic jeszcze raz. Funkcja setup znowu ustawi czas, tym razem będzie dobry.

Teraz trzeba wgrać nowy szkic tym razem z SETUP ustawionym na false. Od teraz restart nie będzie ustawiał zegara od nowa.

Gwoli wyjaśnienia ustawienie zegara wykonuje się tak – zatrzymujemy zegar przez RTC.stop() następnie ustawiamy wszystkie segmenty przez RTC.set() korzystając z predefiniowanych stałych do określania sekund, minut, godzin, dni, miesięcy, roku oraz dnia tygodnia. Następnie zegar puszczamy, przez RTC.start(). Jeżeli moduł ma założoną baterię wówczas czas będzie odmierzany non-stop i przy następnym starcie nie trzeba znowu używać RTC.start().

Mamy ustawiony czas, pozostaje go odczytać i wyświetlić. Do odczytu zegara wykorzystywana jest funkcja RTC.get(SEGMENT,odczyt_z_bufora). SEGMENT to predefiniowana stała taka sama jak użyta w RTC.set do ustawienia poszczególnych części czasu (minut, sekund, etc). Wartość odczyt_z_bufora jest zmienną typu logicznego (boolean) i jeżeli jest ustawiona na true wówczas biblioteka odpyta układ DS1307 o czas i zachowa jego odczyt w buforze, tak aby następne RTC.get z ustawionym odczyt_z_bufora na false miały zagwarantowane, że odczytują ten sam czas (bo można sobie wyobrazić, że kolejne odczyty zostaną dokonane po zmianie czasu np z 23:59:59 na 00:00:00).

W powyższym szkicu odczyt czasu odbywa się w funkcji getTime biorącej dwa argumenty – bufory znakowe w które zostanie zapisany odpowiednio czas i data, sformatowane dzięki funkcji sprintf. Pozostaje jeszcze tylko wyświetlić odczytane wartości w dwóch liniach wyświetlacza. Dzieje się to już w pętli loop.

Szkic do pobrania tutaj.

Pozostaje jeszcze pokazać jak wygląda całość w działaniu:

Całość w działaniu
Całość w działaniu