niedziela, 10 września 2017

[32] Arduino - RTS DS3231

W tym poście chciałbym opisać podłączenie oraz programowanie zegara czasu rzeczywistego DS3231 za pomocą Arduino.

[Źródło: http://www.hobbytronics.co.uk/arduino-uno-r3, http://www.banggood.com]



Dużą zaletą tego układu, oprócz dużej precyzji jest brak konieczności wyposażenia układu w zewnętrzny kwarc. Ponieważ układ posiada taki w obudowie. Dodatkowym atutem jest układ kompensacji temperaturowej. 

Układ posiada wyjście z kwarcu 32,768kHz oraz dwa alarmy, które można zaprogramować. Dodatkowo z wbudowanego termometru można odczytać zmierzoną temperaturę.

Dokładność układu wynosi +/- 2ppm dla zakresu temperatur od 0 st. C do 40 st. C. Natomiast dla zakresy od -40 do 85 st. C może dochodzić do większych rozrzutów wynoszących +/-3ppm. Takie wartości pozwalają na uzyskanie dokładności nie przekraczające rozrzutu do 60 sekund w roku.

Biblioteką można pobrać przez menadżer bibliotek.

Program:


Program pobierający dane z układu wygląda następująco:

  1. #include <DS3231.h>
  2. #include <Wire.h>
  3. #include <stdint.h>
  4. /* Pin for SDA in Arduino uno is Analog pin 4
  5.    Pin for SCL in Arduino uno is Analog pin 5 */
  6. DS3231 Clock;
  7. bool SetCentury=false;
  8. bool AMPM_Indicat;
  9. bool PM;
  10. void setup() {
  11.   Wire.begin();         /* Start I2C */
  12.   Serial.begin(9600);   /* Enable UART transmission */
  13. }
  14. void loop()
  15. {
  16.   byte A1Day, A1Hour, A1Minute, A1Second;
  17.   bool A1_Dy, A1_12h, A1_pm;
  18.   byte A2Day, A2Hour, A2Minute, A2Second;
  19.   bool A2_Dy, A2_12h, A2_pm;
  20.   byte alarmBits;
  21.   /* Get date and hour from DS3231 then send it through UART */
  22.   uint8_t ds_year = Clock.getYear();
  23.   uint8_t ds_month = Clock.getMonth(SetCentury);
  24.   uint8_t ds_dayOfMonth = Clock.getDate();
  25.   uint8_t ds_dayOfWeek = Clock.getDoW();
  26.   uint8_t ds_hour = Clock.getHour(AMPM_Indicat, PM);
  27.   uint8_t ds_minutes = Clock.getMinute();
  28.   uint8_t ds_seconds = Clock.getSecond();
  29.   uint8_t ds_temperature = Clock.getTemperature();
  30.   /*  Print data */
  31.   Serial.print("20");
  32.   Serial.print(ds_year, DEC);       /* Print year */
  33.   Serial.print('-');
  34.   Serial.print(ds_month, DEC);      /* Print month */
  35.   Serial.print('-');
  36.   Serial.print(ds_dayOfMonth, DEC); /* Print day in month */
  37.   Serial.print(' ');
  38.   Serial.print(ds_dayOfWeek, DEC);  /* Day of the week */
  39.   Serial.print(';');
  40.   /* Print time */
  41.   Serial.print(ds_hour, DEC);
  42.   Serial.print(':');
  43.   Serial.print(ds_minutes, DEC);
  44.   Serial.print(':');
  45.   Serial.print(ds_seconds, DEC);
  46.   if(AMPM_Indicat)
  47.   {
  48.     if(PM) { Serial.print(" PM "); }
  49.     else   { Serial.print(" AM "); }
  50.   }
  51.   else
  52.   {
  53.     Serial.print(" 24H ");
  54.   }
  55.   /* Display temperature */
  56.   Serial.print("Temp=");
  57.   Serial.print(ds_temperature, 2);
  58.   /* Check oscillator, if time is correct */
  59.   if (Clock.oscillatorCheck() == true)  { Serial.print("; Osc Time OK; ");    }
  60.   else                                  { Serial.print("; Osc Time ERROR; "); }
  61.   Serial.print('\n');
  62.   /* Check alarms */
  63.   if(Clock.checkIfAlarm(1) == true){  Serial.print("Alarm 1 GO !; ");   }
  64.   if(Clock.checkIfAlarm(2) == true){  Serial.print("Alarm 2 GO !; ");   }
  65.   /* Check alarm 1 information */
  66.   Serial.print('\n');
  67.   Serial.print("Alarm 1 data: ");
  68.   Clock.getA1Time(A1Day, A1Hour, A1Minute, A1Second, alarmBits, A1_Dy, A1_12h, A1_pm);
  69.   Serial.print(A1Day, DEC);
  70.   if (A1_Dy) {
  71.     Serial.print(" Day of week");
  72.   } else {
  73.     Serial.print(" Date");
  74.   }
  75.   Serial.print(';');
  76.   Serial.print(A1Hour, DEC);
  77.   Serial.print(':');
  78.   Serial.print(A1Minute, DEC);
  79.   Serial.print(':');
  80.   Serial.print(A1Second, DEC);
  81.   Serial.print(':');
  82.   if (A1_12h)
  83.   {
  84.     if (A1_pm) { Serial.print('pm '); }
  85.     else       { Serial.print('am '); }
  86.   }
  87.   if (Clock.checkAlarmEnabled(1)) { Serial.print("Alarm 1: enabled;"); }
  88.   /* Check alarm 2 information */
  89.   Serial.print('\n');
  90.   Serial.print("Alarm 2 data: ");
  91.   Clock.getA1Time(A2Day, A2Hour, A2Minute, A2Second, alarmBits, A2_Dy, A2_12h, A2_pm);
  92.   Serial.print(A2Day, DEC);
  93.   if (A2_Dy) { Serial.print(" day of week"); }
  94.   else       { Serial.print(" Date");        }
  95.   Serial.print(';');
  96.   Serial.print(A2Hour, DEC);
  97.   Serial.print(':');
  98.   Serial.print(A2Minute, DEC);
  99.   Serial.print(':');
  100.   Serial.print(A2Second, DEC);
  101.   Serial.print(':');
  102.   if (A2_12h)
  103.   {
  104.     if (A2_pm) { Serial.print('pm '); }
  105.     else       { Serial.print('am '); }
  106.   }
  107.   if (Clock.checkAlarmEnabled(2)) { Serial.print("Alarm 2: enabled;"); }
  108.   Serial.print('\n');
  109.   Serial.print("Alarm bits: ");
  110.   Serial.print(alarmBits, BIN);
  111.   Serial.print('\n');
  112.   delay(1000);
  113. }

Słów kilka o niektórych funkcjach.

Funkcja pobierająca godzinę:

  1. byte DS3231::getHour(bool& h12, bool& PM) {
  2.     byte temp_buffer;
  3.     byte hour;
  4.     Wire.beginTransmission(CLOCK_ADDRESS);
  5.     Wire.write(0x02);
  6.     Wire.endTransmission();
  7.     Wire.requestFrom(CLOCK_ADDRESS, 1);
  8.     temp_buffer = Wire.read();
  9.     h12 = temp_buffer & 0b01000000;
  10.     if (h12) {
  11.         PM = temp_buffer & 0b00100000;
  12.         hour = bcdToDec(temp_buffer & 0b00011111);
  13.     } else {
  14.         hour = bcdToDec(temp_buffer & 0b00111111);
  15.     }
  16.     return hour;
  17. }

Funkcja podobnie jak pozostałe związane z datą oraz godziną zwracają aktualną wartość. Dodatkowo ta funkcja pobiera dwa wskaźniki do zmiennych logicznych. Jedna z nich określa format w jakim dane są pobrane czyli czy 12 godzinny czy 24 godziny. Druga zmienna określa czy godzina jest AM czy PM. Czyli czy od północy do 12 czy od 12 do północy. Ta część tyczy się trybu 12 godzinnego.

Kolejna funkcja sprawdza stan oscylatora:

  1. bool DS3231::oscillatorCheck() {
  2.     byte temp_buffer = readControlByte(1);
  3.     bool result = true;
  4.     if (temp_buffer & 0b10000000) {
  5.         // Oscillator Stop Flag (OSF) is set, so return false.
  6.         result = false;
  7.     }
  8.     return result;
  9. }

Sprawdza ona status flagi OSF (Oscilltor Stop Flag). Jeśli funkcja zwróci false, będzie to oznaczać że coś jest nie tak z oscylatorem, czyli prawdopodobnie podawany czas jest zły. Flaga OSF jest czyszczona przez funkcje setSecond().

Kolejna funkcja sprawdza czy alarm został odpalony:

  1. bool DS3231::checkIfAlarm(byte Alarm) {
  2.     byte result;
  3.     byte temp_buffer = readControlByte(1);
  4.     if (Alarm == 1) {
  5.         // Did alarm 1 go off?
  6.         result = temp_buffer & 0b00000001;
  7.         // clear flag
  8.         temp_buffer = temp_buffer & 0b11111110;
  9.     } else {
  10.         // Did alarm 2 go off?
  11.         result = temp_buffer & 0b00000010;
  12.         // clear flag
  13.         temp_buffer = temp_buffer & 0b11111101;
  14.     }
  15.     writeControlByte(temp_buffer, 1);
  16.     return result;
  17. }

Funkcja zwraca true lub false w zależności od stanu alarmu. Jako argument podawany jest numer alarmu.

Uruchamiani alarmów odbywa się za pomocą następujących funkcji:

  1. /* set alarms */
  2. void DS3231::setA1Time(byte A1Day, byte A1Hour, byte A1Minute, byte A1Second, byte AlarmBits, bool A1Dy, bool A1h12, bool A1PM);
  3. void DS3231::setA2Time(byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits, bool A2Dy, bool A2h12, bool A2PM);
  4. /* Check alarm flags */
  5. void DS3231::getA1Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM);
  6. void DS3231::getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM);
  7. /* turn on selected alarm */
  8. void DS3231::turnOnAlarm(byte Alarm);
  9. /* turn off alarm */
  10. void DS3231::turnOffAlarm(byte Alarm);