#include "RealTimeClock_DCF.h" /********************************************************** Konstruktor **********************************************************/ #if RTC_DCF_COMMUNICATION == 0x00 RealTimeClock_DCF::RealTimeClock_DCF() : softSerial(2, 3) {} #else RealTimeClock_DCF::RealTimeClock_DCF() {} #endif /********************************************************** öffentliche Methoden **********************************************************/ /*--------------------------------------------------------- Name: begin Beschreibung: initialisiert den gewählten Kommunikations- weg Eingänge: RTCCommunication_t rtcCommunication Kommunikationsweg zum RTC-DCF Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::begin(void) { #if RTC_DCF_COMMUNICATION == 0x00 softSerial.begin(RTC_DCF_UART_BAUDRATE); #endif #if RTC_DCF_COMMUNICATION == 0x01 SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setClockDivider(SPI_CLOCK_DIV32); SPI.setDataMode(SPI_MODE1); #endif #if RTC_DCF_COMMUNICATION == 0x02 Wire.begin(); #endif } /*--------------------------------------------------------- Name: setTime Beschreibung: setzt die Uhrzeit des RTC-DCF Eingänge: DateTime *dateTime Struktur mit Datum und Uhrzeit, die im RTC-DCF gesetzt werden soll. Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::setDateTime(DateTime *dateTime) { uint8_t dateTimeBCD[7]; dateTimeBCD[0] = decimalToBCD(dateTime->getSecond()); dateTimeBCD[1] = decimalToBCD(dateTime->getMinute()); dateTimeBCD[2] = decimalToBCD(dateTime->getHour()); dateTimeBCD[3] = decimalToBCD(dateTime->getWeekday() + 1); dateTimeBCD[4] = decimalToBCD(dateTime->getDay()); dateTimeBCD[5] = decimalToBCD(dateTime->getMonth()); dateTimeBCD[6] = decimalToBCD(dateTime->getYear()); writeRegister(RTC_DCF_REG_SECOND, dateTimeBCD, 7); } /*--------------------------------------------------------- Name: getTime Beschreibung: liest die Uhrzeit des RTC-DCF Eingänge: DateTime *dateTime Struktur mit Datum und Uhrzeit, in die Daten aus dem RTC-DCF gespeichert werden Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::getDateTime(DateTime *dateTime) { uint8_t dateTimeBCD[7]; readRegister(RTC_DCF_REG_SECOND, 7, dateTimeBCD); dateTime->setSecond(BCDToDecimal(dateTimeBCD[0])); dateTime->setMinute(BCDToDecimal(dateTimeBCD[1])); dateTime->setHour(BCDToDecimal(dateTimeBCD[2])); dateTime->setWeekday(BCDToDecimal(dateTimeBCD[3]) - 1); dateTime->setDay(BCDToDecimal(dateTimeBCD[4])); dateTime->setMonth(BCDToDecimal(dateTimeBCD[5])); dateTime->setYear(BCDToDecimal(dateTimeBCD[6])); } /*--------------------------------------------------------- Name: setAlarmTime Beschreibung: setzt einen der beiden Alarmuhrzeiten im RTC-Chip Eingänge: RTCAlarmWeekdays_t weekdays Wochentage für den der Alarm gelten soll mögliche Werte: RTC_ALARM_WEEKDAYS_NONE, RTC_ALARM_WEEKDAYS_MON, RTC_ALARM_WEEKDAYS_TUE, RTC_ALARM_WEEKDAYS_WED, RTC_ALARM_WEEKDAYS_THU, RTC_ALARM_WEEKDAYS_FRI, RTC_ALARM_WEEKDAYS_SAT, RTC_ALARM_WEEKDAYS_SUN, RTC_ALARM_WEEKDAYS_MON_TO_FRI, RTC_ALARM_WEEKDAYS_SAT_TO_SUN, RTC_ALARM_WEEKDAYS_ALL die Wochentage sind über das bitweise Oder (|) verknüpfbar. Beispiel: Für einen Alarm an den Wochentagen Montag und Donnerstag wird der Funktion folgendes übergeben: (RTC_ALARM_WEEKDAYS_MON | RTC_ALARM_WEEKDAYS_THU) uint8_t hour Stunde zu der der Alarm auslösen soll uint8_t minute Minute zu der der Alarm auslösen soll Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::setAlarmTime(RTCAlarmWeekdays_t weekdays, uint8_t hour, uint8_t minute) { uint8_t alarmTimeBCD[] = { decimalToBCD(minute), decimalToBCD(hour), (uint8_t)weekdays }; writeRegister(RTC_DCF_REG_ALARM_MINUTE, alarmTimeBCD, 3); } /*--------------------------------------------------------- Name: getAlarmTime Beschreibung: liest einen der beiden Alarmuhrzeiten aus dem RTC-Chip Eingänge: RTCAlarmWeekdays_t *weekdays ausgelesener Wochentag an dem Alarm aktiv ist (Bitkodierung siehe setAlarmTime) uint8_t *hour ausgelesene Stunde des Alarms uint8_t *minute ausgelesene Minute des Alarms Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::getAlarmTime(RTCAlarmWeekdays_t *weekdays, uint8_t *hour, uint8_t *minute) { uint8_t alarmTimeBCD[3]; readRegister(RTC_DCF_REG_ALARM_MINUTE, 7, alarmTimeBCD); *minute = BCDToDecimal(alarmTimeBCD[0]); *hour = BCDToDecimal(alarmTimeBCD[1]); *weekdays = (RTCAlarmWeekdays_t)alarmTimeBCD[2]; } /*--------------------------------------------------------- Name: enableAlarmInterrupt Beschreibung: aktiviert den eingestellten Alarm Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::enableAlarmInterrupt(void) { uint8_t alarmRegister; readRegister(RTC_DCF_REG_ALARM_CONFIG, 1, &alarmRegister); alarmRegister |= RTC_ALARM_CONFIG_REGISTER_AIE; writeRegister(RTC_DCF_REG_ALARM_CONFIG, &alarmRegister, 1); } /*--------------------------------------------------------- Name: disableAlarmInterrupt Beschreibung: deaktiviert den eingestellten Alarm Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::disableAlarmInterrupt(void) { uint8_t alarmRegister; readRegister(RTC_DCF_REG_ALARM_CONFIG, 1, &alarmRegister); alarmRegister &= ~RTC_ALARM_CONFIG_REGISTER_AIE; writeRegister(RTC_DCF_REG_ALARM_CONFIG, &alarmRegister, 1); } /*--------------------------------------------------------- Name: resetAlarmInterrupt Beschreibung: schaltet den Alarm wieder scharf Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::resetAlarmInterrupt(void) { uint8_t statusRegister = RTC_STATUS_AIF; writeRegister(RTC_DCF_REG_STATUS, &statusRegister, 1); } /*--------------------------------------------------------- Name: enableAlarmLED Beschreibung: aktiviert, dass die LED bei einem Alarm ein- bzw. nach ausschalten des Alarms wieder ausgeschaltet wird. Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::enableAlarmLED(void) { uint8_t alarmRegister; readRegister(RTC_DCF_REG_ALARM_CONFIG, 1, &alarmRegister); alarmRegister |= RTC_ALARM_CONFIG_REGISTER_AILED; writeRegister(RTC_DCF_REG_ALARM_CONFIG, &alarmRegister, 1); } /*--------------------------------------------------------- Name: disableAlarmLED Beschreibung: deaktiviert, dass die LED bei einem Alarm ein- bzw. nach ausschalten des Alarms wieder ausgeschaltet wird. Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::disableAlarmLED(void) { uint8_t alarmRegister; readRegister(RTC_DCF_REG_ALARM_CONFIG, 1, &alarmRegister); alarmRegister &= ~RTC_ALARM_CONFIG_REGISTER_AILED; writeRegister(RTC_DCF_REG_ALARM_CONFIG, &alarmRegister, 1); } /*--------------------------------------------------------- Name: setPeriodicInterruptMode Beschreibung: stellt den periodischen Interruptbetrieb ein Eingänge: PeriodicInterruptSelection_t periodicInterruptSelection Art des periodischen Betriebs: RTC_PERIODIC_INT_PULSE_1_HZ Pulsmodus mit 1 Hz RTC_PERIODIC_INT_PULSE_2_HZ Pulsmodus mit 2 Hz RTC_PERIODIC_INT_LEVEL_EVERY_SECOND Interrupt jede Sekunde (synchron mit Sekundenzähler) RTC_PERIODIC_INT_LEVEL_EVERY_MINUTE Interrupt jede Minute (bei Sekunde = 0) RTC_PERIODIC_INT_LEVEL_EVERY_HOUR Interrupt jede Stunde (bei Minute und Sekunde = 0) RTC_PERIODIC_INT_LEVEL_EVERY_DAY Interrupt jeden Tag (bei Stunde, Minute und Sekunde = 0) RTC_PERIODIC_INT_LEVEL_EVERY_MONTH Interrupt jeden Monat (jeweils am 1. um 0:00:00) Hinweis: Die Interrupts mit der Bezeichnung LEVEL werden nicht automatisch zurückgesetzt, sondern müssen durch den Aufruf der Methode resetPeriodicInterrupt wieder scharf geschaltet werden. Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::setPeriodicInterruptMode(RTCPeriodicInterruptMode_t periodicInterruptMode) { uint8_t mode = (uint8_t)periodicInterruptMode; writeRegister(RTC_DCF_REG_PERIODIC_INTERRUPT, &mode, 1); } /*--------------------------------------------------------- Name: enablePeriodicInterrupt Beschreibung: aktiviert den eingestellten periodischen Interrupt Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::enablePeriodicInterrupt(void) { uint8_t perIntRegister; readRegister(RTC_DCF_REG_PER_INT_CONFIG, 1, &perIntRegister); perIntRegister |= RTC_PER_INT_CONFIG_REGISTER_PIE; writeRegister(RTC_DCF_REG_PER_INT_CONFIG, &perIntRegister, 1); } /*--------------------------------------------------------- Name: disablePeriodicInterrupt Beschreibung: deaktiviert den eingestellten periodischen Interrupt Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::disablePeriodicInterrupt(void) { uint8_t perIntRegister; readRegister(RTC_DCF_REG_PER_INT_CONFIG, 1, &perIntRegister); perIntRegister &= ~RTC_PER_INT_CONFIG_REGISTER_PIE; writeRegister(RTC_DCF_REG_PER_INT_CONFIG, &perIntRegister, 1); } /*--------------------------------------------------------- Name: resetPeriodicInterrupt Beschreibung: schaltet den periodischen Interrupt bei Auswahl eines der Level-Modi wieder scharf (siehe setPeriodicInterrupt) Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::resetPeriodicInterrupt(void) { uint8_t statusRegister = RTC_STATUS_PIF; writeRegister(RTC_DCF_REG_STATUS, &statusRegister, 1); } /*--------------------------------------------------------- Name: enablePeriodicInterruptLED Beschreibung: aktiviert, dass die LED bei einem periodischen Interrupt ein- bzw. nach ausschalten des Interrupts wieder ausgeschaltet wird. Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::enablePeriodicInterruptLED(void) { uint8_t perIntRegister; readRegister(RTC_DCF_REG_PER_INT_CONFIG, 1, &perIntRegister); perIntRegister |= RTC_PER_INT_CONFIG_REGISTER_PILED; writeRegister(RTC_DCF_REG_PER_INT_CONFIG, &perIntRegister, 1); } /*--------------------------------------------------------- Name: disablePeriodicInterruptLED Beschreibung: deaktiviert, dass die LED bei einem periodischen Interrupt ein- bzw. nach ausschalten des Interrupts wieder ausgeschaltet wird. Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::disablePeriodicInterruptLED(void) { uint8_t perIntRegister; readRegister(RTC_DCF_REG_PER_INT_CONFIG, 1, &perIntRegister); perIntRegister &= ~RTC_PER_INT_CONFIG_REGISTER_PILED; writeRegister(RTC_DCF_REG_PER_INT_CONFIG, &perIntRegister, 1); } /*--------------------------------------------------------- Name: enableDCF77Reception Beschreibung: aktiviert den DCF77-Empfang Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::enableDCF77Reception(void) { uint8_t dcf77Register; readRegister(RTC_DCF_REG_DCF77_CONFIG, 1, &dcf77Register); dcf77Register |= RTC_DCF77_CONFIG_REGISTER_DCFE; writeRegister(RTC_DCF_REG_DCF77_CONFIG, &dcf77Register, 1); } /*--------------------------------------------------------- Name: disableDCF77Reception Beschreibung: deaktiviert den DCF77-Empfang Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::disableDCF77Reception(void) { uint8_t dcf77Register; readRegister(RTC_DCF_REG_DCF77_CONFIG, 1, &dcf77Register); dcf77Register &= ~RTC_DCF77_CONFIG_REGISTER_DCFE; writeRegister(RTC_DCF_REG_DCF77_CONFIG, &dcf77Register, 1); } /*--------------------------------------------------------- Name: enableDCF77Interrupt Beschreibung: aktiviert den Interrupt bei DCF77-Empfang Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::enableDCF77Interrupt(void) { uint8_t dcf77Register; readRegister(RTC_DCF_REG_DCF77_CONFIG, 1, &dcf77Register); dcf77Register |= RTC_DCF77_CONFIG_REGISTER_DCFIE; writeRegister(RTC_DCF_REG_DCF77_CONFIG, &dcf77Register, 1); } /*--------------------------------------------------------- Name: disableDCF77Interrupt Beschreibung: deaktiviert den Interrupt bei DCF77-Empfang Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::disableDCF77Interrupt(void) { uint8_t dcf77Register; readRegister(RTC_DCF_REG_DCF77_CONFIG, 1, &dcf77Register); dcf77Register &= ~RTC_DCF77_CONFIG_REGISTER_DCFIE; writeRegister(RTC_DCF_REG_DCF77_CONFIG, &dcf77Register, 1); } /*--------------------------------------------------------- Name: resetAlarmInterrupt Beschreibung: schaltet den Interrupt bei DCF77-Empfang wieder scharf Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::resetDCF77Interrupt(void) { uint8_t statusRegister = RTC_STATUS_DCFIF; writeRegister(RTC_DCF_REG_STATUS, &statusRegister, 1); } /*--------------------------------------------------------- Name: enableDCF77LED Beschreibung: aktiviert, dass die LED bei einem DCF77-Empfangs-Interrupt ein- bzw. nach ausschalten des Interrupts wieder ausgeschaltet wird. Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::enableDCF77LED(void) { uint8_t dcf77Register; readRegister(RTC_DCF_REG_DCF77_CONFIG, 1, &dcf77Register); dcf77Register |= RTC_DCF77_CONFIG_REGISTER_DCFLED; writeRegister(RTC_DCF_REG_DCF77_CONFIG, &dcf77Register, 1); } /*--------------------------------------------------------- Name: disableDCF77LED Beschreibung: deaktiviert, dass die LED bei einem DCF77-Empfangs-Interrupt ein- bzw. nach ausschalten des Interrupts wieder ausgeschaltet wird. Eingänge: void Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::disableDCF77LED(void) { uint8_t dcf77Register; readRegister(RTC_DCF_REG_DCF77_CONFIG, 1, &dcf77Register); dcf77Register &= ~RTC_DCF77_CONFIG_REGISTER_DCFLED; writeRegister(RTC_DCF_REG_DCF77_CONFIG, &dcf77Register, 1); } /********************************************************** private Methoden **********************************************************/ /*--------------------------------------------------------- Name: writeRegister Beschreibung: schreibt eine vorgegebene Anzahl Register des des RTC-DCF Eingänge: unsigned char regAddress Registeradresse ab der die Daten geschrieben werden sollen unsigned char *data zu schreibende Daten unsigned char datalength Anzahl der zu schreibenden Daten Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::writeRegister(RTCRegister_t regAddress, uint8_t *data, uint8_t datalength) { #if RTC_DCF_COMMUNICATION == 0x00 /* start byte */ softSerial.write(0x02); /* write direction */ softSerial.write((uint8_t)0x00); /* start address */ softSerial.write(regAddress); /* data */ while(datalength--) { /* use bytestuffing at data bytes equal to STX, ETX and DLE */ if( (*data == 0x02) || (*data == 0x03) || (*data == 0x10) ) { softSerial.write(0x10); softSerial.write(*data | 0x80); } else { softSerial.write(*data); } data++; } /* end byte */ softSerial.write(0x03); #endif #if RTC_DCF_COMMUNICATION == 0x01 digitalWrite(SPI_SLAVE_SELECT_PIN, LOW); delayMicroseconds(50); SPI.transfer(regAddress | SPI_BURST_WRITE); while(datalength--) { delayMicroseconds(50); SPI.transfer(*data); data++; } digitalWrite(SPI_SLAVE_SELECT_PIN, HIGH); delayMicroseconds(300); #endif #if RTC_DCF_COMMUNICATION == 0x02 Wire.beginTransmission(RTC_DCF_I2C_ADDRESS); Wire.write(regAddress); while(datalength--) { Wire.write(*data); data++; } Wire.endTransmission(); #endif } /*--------------------------------------------------------- Name: readRegister Beschreibung: liest eine vorgegebene Anzahl Register des des RTC-DCF Eingänge: unsigned char regAddress Registeradresse ab der die Daten gelesen werden sollen unsigned char length Anzahl der zu lesenden Daten unsigned char *data gelesene Daten Ausgang: void ---------------------------------------------------------*/ void RealTimeClock_DCF::readRegister(RTCRegister_t regAddress, uint8_t length, uint8_t *data) { #if RTC_DCF_COMMUNICATION == 0x00 uint8_t receivedByte; uint8_t nextByteStuffed; /* start byte */ softSerial.write(0x02); /* write direction */ softSerial.write(0x01); /* start address */ softSerial.write(regAddress); /* count of read register */ softSerial.write(length); /* end byte */ softSerial.write(0x03); /* wait for start byte of slave */ while(softSerial.read() != 0x02) {} /* receive data until length is reached */ while(length != 0) { /* wait until there are data to receive */ while(softSerial.available() == 0) {} receivedByte = softSerial.read(); /* check for stuffed bytes */ if(receivedByte == 0x10) { nextByteStuffed = 1; } else { if(nextByteStuffed == 1) { receivedByte &= 0x7f; nextByteStuffed = 0; } *data = receivedByte; data++; length--; } } #endif #if RTC_DCF_COMMUNICATION == 0x01 digitalWrite(SPI_SLAVE_SELECT_PIN, LOW); delayMicroseconds(50); SPI.transfer(regAddress | SPI_BURST_READ); while(length--) { delayMicroseconds(50); *data = SPI.transfer(0xff); data++; } digitalWrite(SPI_SLAVE_SELECT_PIN, HIGH); delayMicroseconds(300); #endif #if RTC_DCF_COMMUNICATION == 0x02 // set the register pointer to the given position, but do not stop the communication // after writing the pointer Wire.beginTransmission(RTC_DCF_I2C_ADDRESS); Wire.write(regAddress); Wire.endTransmission(false); // read out the given amount of data from the registers Wire.requestFrom((uint8_t)RTC_DCF_I2C_ADDRESS, length); while( Wire.available() ) { *data = Wire.read(); data++; } #endif } /*--------------------------------------------------------- Name: decimalToBCD Beschreibung: rechnet Dezimalwerte in das BCD-System um (der RTC-DCF arbeitet intern mit BCD-Daten) Eingänge: unsigned char decimal umzurechnender Dezimalwert Ausgang: unsigned char BCD-Wert der übergebenen Dezimalzahl ---------------------------------------------------------*/ uint8_t RealTimeClock_DCF::decimalToBCD(uint8_t decimal) { return (((decimal / 10) << 4) | (decimal % 10)); } /*--------------------------------------------------------- Name: BCDToDecimal Beschreibung: rechnet BCD-Werte in das Dezimal-System um (der RTC-DCF arbeitet intern mit BCD-Daten) Eingänge: unsigned char bcd umzurechnender BCD-Wert Ausgang: unsigned char Dezimalwert der übergebenen BCD-Zahl ---------------------------------------------------------*/ uint8_t RealTimeClock_DCF::BCDToDecimal(uint8_t bcd) { return (((((bcd)>>4) & 0x0F) * 10) + ((bcd) & 0x0F)); } /********************************************************** Vorinstantiiertes Objekt **********************************************************/ RealTimeClock_DCF RTC_DCF = RealTimeClock_DCF();