niedziela, 15 kwietnia 2018

Moduł RTC DS3231 i EEPROM AT24C32

Moduł RTC, inaczej zegar czasu rzeczywistego, służy do precyzyjnego odmierzania czasu. Znajdzie więc zastosowanie w rozmaitych zegarach, budzikach, rejestratorach i wszędzie tam, gdzie dodatkowo zechcemy pokazywać bieżący czas. Opisywany moduł dodatkowo oferuje pomiar temperatury, pamięć EEPROM i baterię podtrzymującą zasilanie.

Moduł RTC DS3231 i EEPROM AT24C32

Specyfikacja
  • zasilanie 3,3 - 5,5V
  • interfejs komunikacji: I2C
  • układ zegara DS3231
    • dokładność:
      • ±2ppm od 0°C to +40°C 
      • ±3.5ppm od -40°C to +85°C
    • pomiar temperatury z dokładnością ±3°C
    • adres I2C: 0x68
    • podtrzymanie pamięci (wymienialną) baterią 2032
  • pamięć EEPROM
    • pojemność 32kb
    • adres I2C 0x57
  • dodatkowe wyjścia: SDA, SCL, VCC i GND do kaskadowego podłączenia kolejnego modułu pracującego na magistrali I2C
  • wymiary: 38x22mm

Piny
  • Vcc, GND - zasilanie
  • SCL, SDA - piny magistrali I2C
  • SQW - wyjście sygnału prostokątnego lub źródło przerwań dla alarmów
  • 32K - sygnał prostokątny o częstotliwości 32kHz

Funkcje
  • zegar RTC z podtrzymaniem pamięci (wbudowana bateria)
    • odmierza sekundy, minuty, godziny, dni miesiąca, miesiące, dni tygodnia, lata
    • dwa możliwe alarmy do ustawienia (z wieloma możliwymi interwałami do wyboru)
    • wyznacza lata przestępne do roku 2100
    • pomiar temperatury (niezbyt dokładny)
  • pamięć EEPROM (konfigurowalny adres na magistrali I2C za pomocą zworek A0, A1 i A2)

Uruchomienie

W użytej w bibliotece DS3232RTC (3) (korzystającej z biblioteki Time (4)) jest wiele przykładów pokazujących możliwości pracy z opisywanym modułem RTC. Wybrałem trzy podstawowe warianty pracy z modułem, które mogą się przydać. Są to kolejno ustawianie czasu, odczyt czasu  oraz ustawianie alarmu.Kod został uruchomiony na Arduino Uno.

Układ uruchomieniowy

Ustawianie czasu

W funkcji setRtcTime() ustawiony jest najpierw czas systemowy, potem modułu RTC na podstawie bieżącego czasu systemowego.

#include <TimeLib.h>
#include <Time.h>
#include <DS3232RTC.h>

void setup() {
    Serial.begin(9600);
    setSyncProvider(RTC.get);   // the function to get the time from the RTC
    if (timeStatus() != timeSet) {
        Serial.println("Unable to sync with the RTC");
    } else {
        Serial.println("RTC has set the system time");
    }

    setRtcTime();
}

void loop() {

}

void setRtcTime() {
    setTime(15, 24, 30, 7, 4, 2018);   //set the system time to 15h24m30s on 7 kwietnia 2018
    RTC.set(now());                    //set the RTC from the system time
}


Wyniki na terminalu

Pokazywanie czasu

Co jedną sekundę odczytywany jest bieżący czas i data, formatowany i przesyłany na terminal komputera. Dla minut i sekund poniżej 10-ki dostawiane są wiodące zera.

#include <TimeLib.h>
#include <Time.h>
#include <DS3232RTC.h>

void setup() {
    Serial.begin(9600);
    setSyncProvider(RTC.get);   // the function to get the time from the RTC
    if (timeStatus() != timeSet) {
        Serial.println("Unable to sync with the RTC");
    } else {
        Serial.println("RTC has set the system time");
    }
}

void loop() {
    digitalClockDisplay();
    delay(1000);
}

void digitalClockDisplay() {
    // digital clock display of the time
    Serial.print(hour());
    printDigits(minute());
    printDigits(second());
    Serial.print(' ');
    Serial.print(day());
    Serial.print(' ');
    Serial.print(month());
    Serial.print(' ');
    Serial.print(year());
    Serial.println();
}

void printDigits(int digits) {
    // utility function for digital clock display: prints preceding colon and leading 0
    Serial.print(':');
    if (digits < 10) {
        Serial.print('0');
    }
    Serial.print(digits);
}

Wyniki na terminalu

Ustawianie alarmu

Przykład pokazuje ustawianie i sprawdzanie alarmu 1. W f-cji setup() następuje inicjalizacja i określenia działania alarmu 1 - jest uruchamiany co jedną sekundę (wskazuje na to stała ALM1_EVERY_SECOND). Z kolei w f-cji loop() przy każdym jej przejściu następuje sprawdzanie, czy wystąpił alarm 1.

#include <TimeLib.h>
#include <Time.h>
#include <DS3232RTC.h>
void setup() {
    Serial.begin(9600);

    // initialize the alarms to known values, clear the alarm flags, clear the alarm interrupt flags
    RTC.setAlarm(ALM1_MATCH_DATE, 0, 0, 0, 1);
    RTC.setAlarm(ALM2_MATCH_DATE, 0, 0, 0, 1);
    RTC.alarm(ALARM_1);
    RTC.alarm(ALARM_2);
    RTC.alarmInterrupt(ALARM_1, false);
    RTC.alarmInterrupt(ALARM_2, false);
    RTC.squareWave(SQWAVE_NONE);

    // set Alarm 1 to occur once per second
    RTC.setAlarm(ALM1_EVERY_SECOND, 0, 0, 0, 0);
    // clear the alarm flag
    RTC.alarm(ALARM_1);

    Serial.print(" Start ");
    printDateTime(RTC.get());
    Serial.println();
}

void loop() {
    // check to see if the alarm flag is set (also resets the flag if set)
    if ( RTC.alarm(ALARM_1) ) {
        Serial.print(" ALARM_1 ");
        printDateTime(RTC.get());
    }
}

void printDateTime(time_t t) {
    Serial.print((day(t) < 10) ? "0" : "");
    Serial.print(day(t));
    Serial.print(" ");
    Serial.print(monthShortStr(month(t)));
    Serial.print(" ");
    Serial.print(year(t));
    Serial.print(" ");
    Serial.print((hour(t) < 10) ? "0" : "");
    Serial.print(hour(t));
    Serial.print(" ");
    Serial.print((minute(t) < 10) ? "0" : "");
    Serial.print(minute(t));
    Serial.print(" ");
    Serial.print((second(t) < 10) ? "0" : "");
    Serial.print(second(t));
    Serial.println();
}

Wyniki na terminalu

Tytułem wyjaśnienia, sam moduł i oczywiście biblioteka obsługuje dwa typy alarmów.
Alarm 1, który można wykorzystać do ustawiania różnych interwałów z dokładnością do jednej sekundy. W przykładzie powyżej była to wspomniana ALM1_EVERY_SECOND ustawiająca alarm 1 co jedną sekundę. Mamy do dyspozycji następujące możliwości (wywołania) dla tego alarmu:

  • ALM1_EVERY_SECOND - co jedna sekunda
  • ALM1_MATCH_SECONDS  - dla określonej sekundy (na minutę)
  • ALM1_MATCH_MINUTES - dla określonej sekundy i minuty
  • ALM1_MATCH_HOURS - dla określonej godziny, minuty i sekundy
  • ALM1_MATCH_DATE - dla określonego miesiąca, godziny, minuty i sekundy
  • ALM1_MATCH_DAY - dla określonego dnia tygodnia, godziny, minuty i sekundy

Alarm 2 także określa interwały, tyle że inne i z dokładnością do jednej minuty:

  • ALM2_EVERY_MINUTE - co jedna minuta
  • ALM2_MATCH_MINUTES - dla określonej minuty (na godzinę)
  • ALM2_MATCH_HOURS - dla określonej godziny i minuty
  • ALM2_MATCH_DATE - dla określonego miesiąca, godziny i minuty
  • ALM2_MATCH_DAY - dla określonego dnia tygodnia, godziny i minuty
Jak widać możliwości jest sporo, każdy alarm jest ustawiany f-cją:
RTC.setAlarm(alarmType, seconds, minutes, hours, dayOrDate);

Jest jeszcze jedna rzecz warta odnotowania. Mianowicie sposób sprawdzania, czy wystąpił alarm. 
W przykładzie powyżej było to ciągłe odpytywanie f-cją RTC.alarm(ALARM_1). Istnieją jednak jeszcze inne możliwości, które prezentują przykłady z biblioteki DS3232RTC (3). Chodzi o wykorzystanie pinu SQW. F-cją squareWave() za pomocą stałych można ustawiać następujące sygnały wyjściowe:
  • SQWAVE_NONE
  • SQWAVE_1_HZ
  • SQWAVE_1024_HZ
  • SQWAVE_4096_HZ
  • SQWAVE_8192_HZ

Czyli można wyłączyć generowanie sygnału prostokątnego przekazując stałą SQWAVE_NONE lub włączyć, przekazując dowolną inną stałą. W przypadku wyłączenia generowania sygnału, pin SQW można wykorzystać do informowania o alarmie. Stan niski na wyjściu SQW oznacza wystąpienie alarmu i można go identyfikować sprawdzając stan pinu (przykład  alarm_ex7.ino). Można także wykorzystać przerwania do sprawdzania stanu pinu SQW (przykład alarm_ex6.ino).

W specyfikacji wspomniałem o pomiarze temperatury, można ją odczytać f-cją RTC.temperature();


Schemat 

Schemat pochodzi ze artykułu Arduino and DS3231 RTC example (6). Schemat jak schemat, ale jedna rzecz jest w nim dyskusyjna. Chodzi o obecność elementów R5 (200) i dioda D2 (1N4148). Problem pojawia się, gdy zgodnie ze specyfikacją powyżej moduł zostanie zasilony napięciem 5V. W takim przypadku bateria będzie ładowana napięciem pomniejszonym o spadek na diodzie. A że nie jest to akumulator to mamy problem. Mi wprawdzie bateria nie wybuchła ani nie zmieniła wymiarów/objętości, ale dla spokoju ducha można pomyśleć o wylutowaniu albo rezystora, albo diody z tej dwójki.

Schemat elektryczny modułu
Źródła:

1 komentarz:

  1. Bardzo ciekawie napisane. Jestem pod wielkim wrażaniem.

    OdpowiedzUsuń