AVR: Real Time Clock DS1307

|



DS1307 to bardzo dobry i tani zegar czasu rzeczywistego. Posiada swój własny kwarc oraz baterię niezależną od pozostałych części naszego urządzenia, dzięki czemu jest niezależny i działa zawsze, nawet w przypadku awarii nadrzędnego procesora. Układ ma także kalendarz z obsługą lat przestępnych i jest bardzo prosty w obsłudze - działa na magistrali I2C, która jest wspierana sprzętowo przez mikrokontrolery AVR. DS1307 umożliwia także wygenerowanie sygnału prostokątnego o częstotliwości 1Hz, 4kHz, 8kHz, 32kHz.

Elementy elektroniczne, zestawy prototypowe, Arduino

Układ DS1307 wykorzystuje tani i łatwo dostępny rezonator kwarcowy 32kHz. Należy go podłączyć do wejść X1 i X2 bez dodatkowych kondensatorów. Zaleca się położyć ten kwarc w pozycji poziomej i jego obudowę połączyć z masą układu. Do zasilania awaryjnego można wykorzystać popularną baterię CR2023 podłączoną do nóżki VBAT, która wystarczy na kilka lat. W przypadku kiedy układ nie ma baterii, wejście VBAT należy podłączyć do masy, bo w przeciwnym razie DS1307 nie będzie działać. Wyjścia SDA, SCL i SQW są wyjściami z otwartym kolektorem, więc należy zastosować odpowiednie rezystory podciągające, typowo 4,7k.

Przykładowy schemat DS1307


Zapis do DS1307


Nie będę tutaj omawiał tajników komunikacji przez I2C, a jedynie samą procedurę zapisu. Na początek zobaczmy mapę pamięci RAM tego układu, która ma 64 bajty. Jak widać wykorzystywane jest tylko 8 bajtów, a resztę możemy sobie wykorzystać do własnych celów.


Koniecznie należy zwrócić uwagę na to, że DS1307 zapisuje liczby w postaci BCD a nie binarnej! Czyli w jednym bajcie zapisywane są dwie cyfry - pierwsza na bitach od 0 do 3, a druga cyfra jest zapisana w bitach od 4 do 7.

Procedura zapisu godziny do pamięci wygląda w ten sposób:
  • Zainicjalizuj I2C (chyba, że już wcześniej zostało zainicjalizowane)
  • Wyślij sygnał START
  • Wyślij adres zapisu 208 - to adres DS1307 na magistrali I2C
  • Wyślij adres pierwszego bajtu, który będzie zapisywany. Najczęściej zapisujemy całą pamięć od początku, czyli od zera.
  • Wyślij sekundy
  • Wyślij minuty
  • Wyślij godziny - bit 6 decyduje czy zapisujemy godzinę w formacie 24h czy 12h. Jeżeli bit 6 jest ustawiony, to bit 5 decyduje czy mamy czas przed południem AM, czy po południu PM. Aby było "po naszemu", nie zawracamy sobie głowy bitem 6 i wpisujemy godziny tak samo jak minuty i sekundy.
  • Wyślij dzień tygodnia - ważne: niedziela oznaczona jest przez 1, poniedziałek przez 2, wtorek przez 3...
  • Wyślij dzień miesiąca
  • Wyślij miesiąc
  • Wyślij dwie ostatnie cyfry roku
  • Wyślij bajt konfiguracyjny (jeśli trzeba)
  • Dalsze komórki pamięci RAM można wypełnić swoimi danymi
  • Wyślij sygnał STOP

Bajt konfiguracyjny decyduje o roli wyjścia SQW. Aby uruchomić wyjście generatora, należy bit SQWE ustawić na 1, a bity RS0 i RS1 ustawić wg poniższej tabelki. Jeżeli nie chcemy włączyć generatora, bo bierze on dodatkowy prąd z baterii, ustawiamy go na 0, a wtedy możemy sterować stanem wyjścia SQW poprzez bit OUT. Szczegóły wyjaśnia tabela poniżej.


Przykład funkcji, która zapisuje godzinę do DS1307:
void ds1307_write(uint8 h, uint8 m, 
    uint8 dd, uint8 mm, uint8 yy, uint8 dt) {        
    // dt to dzień tygodnia (poniedziałek=2)
    
    i2c_init(100);                  // częstotliwość SCL 100kHz
    i2c_start();
    i2c_write(208);                 // adres DS1307
    if(TWSR != 0x18) {              // sprawdzanie czy DS1307 odpowiada
        t("zegar nie odpowiada");
        return 0;
    }
    
    i2c_write(0);                   // zapis od poczatku pamieci

//  sekundy
    i2c_write(0);

//  minuty
    i2c_write(((m/10)<<4)+(m%10));

//  godziny
    i2c_write(((h/10)<<4)+(h%10));

//  dzień tygodnia
    i2c_write(dt);

//  dzień
    i2c_write(((dd/10)<<4)+(dd%10));

//  miesiąc
    i2c_write(((mm/10)<<4)+(mm%10));
    
//  rok
    i2c_write(((yy/10)<<4)+(yy%10));
    
    i2c_stop();
    return;
}


Odczyt z DS1307


Jak to zwykle bywa w układach I2C, najpierw należy zapisać adres, od którego będziemy odczytywać pamięć, zresetować transmisję i dopiero potem odczytywać dane.
  • Zainicjalizuj I2C (chyba, że już wcześniej zostało zainicjalizowane)
  • Wyślij sygnał START
  • Wyślij adres zapisu 208 - to adres DS1307 na magistrali I2C
  • Wyślij adres pierwszego bajtu, od którego będziemy odczytywać pamięć. Najczęściej odczytujemy całą pamięć od początku, czyli od zera.
  • Wyślij ponownie sygnał START
  • Wyślij adres odczytu 209
  • Odczytuj po kolei wszystkie komórki pamięci
  • Przy odczytywaniu ostatniej komórki nie należy wysyłać sygnały potwierdzenia
  • Wyślij sygnał STOP

Przykład funkcji odczytującej godzinę i datę z DS1307:
void ds1307_read(void) {
    uint8_t sek, min, godz, dztyg, dzien, mies, rok;
    i2c_init(100);
    i2c_start();
    i2c_write(208);                       // adres zapisu DS1307
    if(TWSR != 0x18) {                    // sprawdzanie czy DS1307 odpowiada
        t("zegar nie odpowiada");
        return;
    }
    i2c_write(0);
    i2c_start();
    i2c_write(209);                        // adres odczytu DS1307
    sek = i2c_read();
    min = i2c_read();
    godz = i2c_read();
    dztyg = i2c_read();
    dzien = i2c_read();
    mies = i2c_read();
    rok = i2c_read_nack();
    i2c_stop();
    
//  wszystkie zmienne przechowują wartości w kodzie BCD
//  jeżeli w programie masz gdzieś funkcje wyświetlającą liczbę
//  szesnastkową, to możesz ją tutaj wykorzystać
    out_hex(godz);      
    t(":");
    out_hex(min);
    t(":");
    out_hex(sek);
    t(" ");                
    out_hex(dzien);
    t(".");
    out_hex(mies);
    t(".20");
    out_hex(rok);
    t(" ");
    switch(dztyg) {
        case 1:
            t("nd");
            break;
        case 2:
            t("pn");
            break;
        case 3:
            t("wt");
            break;
        case 4:
            t("sr");
            break;
        case 5:
            t("cz");
            break;
        case 6:
            t("pt");
            break;
        case 7:
            t("sb");
            break;
    }
    return;
}
 

2 komentarze :

Djuke pisze...

Mógłbyś podać linka do biblioteki i2c którą wykorzystałeś ?

Dominik Leon Bieczyński pisze...

W dużej mierze są to funkcje skopiowane prosto z dokumentacji Atmela. Szukaj w rozdziale o TWI (to podróbka I2C ale działa tak samo)

Prześlij komentarz

Skomentuj!