From 29fa5486cb33029db0758db12485704b49957ff5 Mon Sep 17 00:00:00 2001 From: Lewis Jackson <> Date: Wed, 31 May 2023 04:04:58 +0300 Subject: [PATCH] Use local bugfixed Rtc_Pcf8563 library --- lib/Rtc_Pcf8563/README.rst | 47 ++ lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp | 740 ++++++++++++++++++ lib/Rtc_Pcf8563/Rtc_Pcf8563.h | 307 ++++++++ .../examples/lcd_clock/lcd_clock.pde | 63 ++ .../examples/set_alarm/set_alarm.pde | 87 ++ .../examples/set_clock/set_clock.pde | 52 ++ lib/Rtc_Pcf8563/keywords.txt | 66 ++ lib/Rtc_Pcf8563/library.json | 16 + platformio.ini | 1 - 9 files changed, 1378 insertions(+), 1 deletion(-) create mode 100644 lib/Rtc_Pcf8563/README.rst create mode 100644 lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp create mode 100644 lib/Rtc_Pcf8563/Rtc_Pcf8563.h create mode 100644 lib/Rtc_Pcf8563/examples/lcd_clock/lcd_clock.pde create mode 100644 lib/Rtc_Pcf8563/examples/set_alarm/set_alarm.pde create mode 100644 lib/Rtc_Pcf8563/examples/set_clock/set_clock.pde create mode 100644 lib/Rtc_Pcf8563/keywords.txt create mode 100644 lib/Rtc_Pcf8563/library.json diff --git a/lib/Rtc_Pcf8563/README.rst b/lib/Rtc_Pcf8563/README.rst new file mode 100644 index 0000000..ab9acfb --- /dev/null +++ b/lib/Rtc_Pcf8563/README.rst @@ -0,0 +1,47 @@ + +This is my fixed/expanded version of the original Rtc_Pcf8563 library for Arduino. +All credits go to the original authors. + +My version differs for + +* a few more methods, mostly useful for debugging +* a single bug fix in RTCC_ALARM_AF + + +NAME +---- +Pcf8563 Real Time Clock support routines + +AUTHOR +------ +Joe Robertson, jmr +orbitalair@bellsouth.net +http://orbitalair.wikispaces.com/Arduino + +CREATION DATE +------------- +9/24/06, init - built off of usart demo. using mikroC + +NOTES +----- + +HISTORY +------- + +* 10/14/06 ported to CCS compiler, jmr +* 2/21/09 changed all return values to hex val and not bcd, jmr +* 1/10/10 ported to arduino, jmr +* 2/14/10 added 3 world date formats, jmr +* 28/02/2012 A. Pasotti + * fixed a bug in RTCC_ALARM_AF, + * added a few (not really useful) methods +* 24/01/2018 add getTimestamp method (return unix timestamp), gilou + + +TODO +---- + +Add Euro date format +Add short time (hh:mm) format +Add 24h/12h format +Add timer support \ No newline at end of file diff --git a/lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp b/lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp new file mode 100644 index 0000000..546b333 --- /dev/null +++ b/lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp @@ -0,0 +1,740 @@ +/***** + * NAME + * Pcf8563 Real Time Clock support routines + * AUTHOR + * Joe Robertson, jmr + * orbitalair@bellsouth.net + * http://orbitalair.wikispaces.com/Arduino + * CREATION DATE + * 9/24/06, init - built off of usart demo. using mikroC + * NOTES + * HISTORY + * 10/14/06 ported to CCS compiler, jmr + * 2/21/09 changed all return values to hex val and not bcd, jmr + * 1/10/10 ported to arduino, jmr + * 2/14/10 added 3 world date formats, jmr + * 28/02/2012 A. Pasotti + * fixed a bug in RTCC_ALARM_AF, + * added a few (not really useful) methods + * 22/10/2014 Fix whitespace, tabs, and newlines, cevich + * 22/10/2014 add voltLow get/set, cevich + * 22/10/2014 add century get, cevich + * 22/10/2014 Fix get/set date/time race condition, cevich + * 22/10/2014 Header/Code rearranging, alarm/timer flag masking + * extern Wire, cevich + * 26/11/2014 Add zeroClock(), initialize to lowest possible + * values, cevich + * 22/10/2014 add timer support, cevich + * + * TODO + * x Add Euro date format + * Add short time (hh:mm) format + * Add 24h/12h format + ****** + * Robodoc embedded documentation. + * http://www.xs4all.nl/~rfsber/Robo/robodoc.html + */ + +#include +#include "Rtc_Pcf8563.h" + +Rtc_Pcf8563::Rtc_Pcf8563(void) +{ + Wire.begin(); + Rtcc_Addr = RTCC_R>>1; +} + +Rtc_Pcf8563::Rtc_Pcf8563(int sdaPin, int sdlPin) +{ + Wire.begin(sdaPin, sdlPin); + Rtcc_Addr = RTCC_R>>1; +} + +/* Private internal functions, but useful to look at if you need a similar func. */ +byte Rtc_Pcf8563::decToBcd(byte val) +{ + return ( (val/10*16) + (val%10) ); +} + +byte Rtc_Pcf8563::bcdToDec(byte val) +{ + return ( (val/16*10) + (val%16) ); +} + +void Rtc_Pcf8563::zeroClock() +{ + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)0x0); // start address + + Wire.write((byte)0x0); //control/status1 + Wire.write((byte)0x0); //control/status2 + Wire.write((byte)0x00); //set seconds to 0 & VL to 0 + Wire.write((byte)0x00); //set minutes to 0 + Wire.write((byte)0x00); //set hour to 0 + Wire.write((byte)0x01); //set day to 1 + Wire.write((byte)0x00); //set weekday to 0 + Wire.write((byte)0x81); //set month to 1, century to 1900 + Wire.write((byte)0x00); //set year to 0 + Wire.write((byte)0x80); //minute alarm value reset to 00 + Wire.write((byte)0x80); //hour alarm value reset to 00 + Wire.write((byte)0x80); //day alarm value reset to 00 + Wire.write((byte)0x80); //weekday alarm value reset to 00 + Wire.write((byte)SQW_32KHZ); //set SQW to default, see: setSquareWave + Wire.write((byte)0x0); //timer off + Wire.endTransmission(); +} + +void Rtc_Pcf8563::clearStatus() +{ + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)0x0); + Wire.write((byte)0x0); //control/status1 + Wire.write((byte)0x0); //control/status2 + Wire.endTransmission(); +} + +/* +* Read status byte +*/ +byte Rtc_Pcf8563::readStatus2() +{ + getDateTime(); + return getStatus2(); +} + +void Rtc_Pcf8563::clearVoltLow(void) +{ + getDateTime(); + // Only clearing is possible on device (I tried) + setDateTime(getDay(), getWeekday(), getMonth(), + getCentury(), getYear(), getHour(), + getMinute(), getSecond()); +} + +/* +* Atomicly read all device registers in one operation +*/ +void Rtc_Pcf8563::getDateTime(void) +{ + /* Start at beginning, read entire memory in one go */ + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT1_ADDR); + Wire.endTransmission(); + + /* As per data sheet, have to read everything all in one operation */ + uint8_t readBuffer[16] = {0}; + Wire.requestFrom(Rtcc_Addr, 16); + for (uint8_t i=0; i < 16; i++) + readBuffer[i] = Wire.read(); + + // status bytes + status1 = readBuffer[0]; + status2 = readBuffer[1]; + + // time bytes + //0x7f = 0b01111111 + volt_low = readBuffer[2] & RTCC_VLSEC_MASK; //VL_Seconds + sec = bcdToDec(readBuffer[2] & ~RTCC_VLSEC_MASK); + minute = bcdToDec(readBuffer[3] & 0x7f); + //0x3f = 0b00111111 + hour = bcdToDec(readBuffer[4] & 0x3f); + + // date bytes + //0x3f = 0b00111111 + day = bcdToDec(readBuffer[5] & 0x3f); + //0x07 = 0b00000111 + weekday = bcdToDec(readBuffer[6] & 0x07); + //get raw month data byte and set month and century with it. + month = readBuffer[7]; + if (month & RTCC_CENTURY_MASK) + century = true; + else + century = false; + //0x1f = 0b00011111 + month = month & 0x1f; + month = bcdToDec(month); + year = bcdToDec(readBuffer[8]); + + // alarm bytes + alarm_minute = readBuffer[9]; + if(B10000000 & alarm_minute) + alarm_minute = RTCC_NO_ALARM; + else + alarm_minute = bcdToDec(alarm_minute & B01111111); + alarm_hour = readBuffer[10]; + if(B10000000 & alarm_hour) + alarm_hour = RTCC_NO_ALARM; + else + alarm_hour = bcdToDec(alarm_hour & B00111111); + alarm_day = readBuffer[11]; + if(B10000000 & alarm_day) + alarm_day = RTCC_NO_ALARM; + else + alarm_day = bcdToDec(alarm_day & B00111111); + alarm_weekday = readBuffer[12]; + if(B10000000 & alarm_weekday) + alarm_weekday = RTCC_NO_ALARM; + else + alarm_weekday = bcdToDec(alarm_weekday & B00000111); + + // CLKOUT_control 0x03 = 0b00000011 + squareWave = readBuffer[13] & 0x03; + + // timer bytes + timer_control = readBuffer[14] & 0x03; + timer_value = readBuffer[15]; // current value != set value when running +} + + +void Rtc_Pcf8563::setDateTime(byte day, byte weekday, byte month, + bool century, byte year, byte hour, + byte minute, byte sec) +{ + /* year val is 00 to 99, xx + with the highest bit of month = century + 0=20xx + 1=19xx + */ + month = decToBcd(month); + if (century) + month |= RTCC_CENTURY_MASK; + else + month &= ~RTCC_CENTURY_MASK; + + /* As per data sheet, have to set everything all in one operation */ + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write(RTCC_SEC_ADDR); // send addr low byte, req'd + Wire.write(decToBcd(sec) &~RTCC_VLSEC_MASK); //set sec, clear VL bit + Wire.write(decToBcd(minute)); //set minutes + Wire.write(decToBcd(hour)); //set hour + Wire.write(decToBcd(day)); //set day + Wire.write(decToBcd(weekday)); //set weekday + Wire.write(month); //set month, century to 1 + Wire.write(decToBcd(year)); //set year to 99 + Wire.endTransmission(); + // Keep values in-sync with device + getDateTime(); +} + +/** +* Get alarm, set values to RTCC_NO_ALARM (99) if alarm flag is not set +*/ +void Rtc_Pcf8563::getAlarm() +{ + getDateTime(); +} + +/* +* Returns true if AIE is on +* +*/ +bool Rtc_Pcf8563::alarmEnabled() +{ + return getStatus2() & RTCC_ALARM_AIE; +} + +/* +* Returns true if AF is on +* +*/ +bool Rtc_Pcf8563::alarmActive() +{ + return getStatus2() & RTCC_ALARM_AF; +} + +/* enable alarm interrupt + * whenever the clock matches these values an int will + * be sent out pin 3 of the Pcf8563 chip + */ +void Rtc_Pcf8563::enableAlarm() +{ + getDateTime(); // operate on current values + //set status2 AF val to zero + status2 &= ~RTCC_ALARM_AF; + //set TF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_TIMER_TF; + //enable the interrupt + status2 |= RTCC_ALARM_AIE; + + //enable the interrupt + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); +} + +/* set the alarm values + * whenever the clock matches these values an int will + * be sent out pin 3 of the Pcf8563 chip + */ +void Rtc_Pcf8563::setAlarm(byte min, byte hour, byte day, byte weekday) +{ + getDateTime(); // operate on current values + if (min <99) { + min = constrain(min, 0, 59); + min = decToBcd(min); + min &= ~RTCC_ALARM; + } else { + min = 0x0; min |= RTCC_ALARM; + } + + if (hour <99) { + hour = constrain(hour, 0, 23); + hour = decToBcd(hour); + hour &= ~RTCC_ALARM; + } else { + hour = 0x0; hour |= RTCC_ALARM; + } + + if (day <99) { + day = constrain(day, 1, 31); + day = decToBcd(day); day &= ~RTCC_ALARM; + } else { + day = 0x0; day |= RTCC_ALARM; + } + + if (weekday <99) { + weekday = constrain(weekday, 0, 6); + weekday = decToBcd(weekday); + weekday &= ~RTCC_ALARM; + } else { + weekday = 0x0; weekday |= RTCC_ALARM; + } + + alarm_hour = hour; + alarm_minute = min; + alarm_weekday = weekday; + alarm_day = day; + + // First set alarm values, then enable + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)RTCC_ALRM_MIN_ADDR); + Wire.write((byte)alarm_minute); + Wire.write((byte)alarm_hour); + Wire.write((byte)alarm_day); + Wire.write((byte)alarm_weekday); + Wire.endTransmission(); + + Rtc_Pcf8563::enableAlarm(); +} + +void Rtc_Pcf8563::clearAlarm() +{ + //set status2 AF val to zero to reset alarm + status2 &= ~RTCC_ALARM_AF; + //set TF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_TIMER_TF; + //turn off the interrupt + status2 &= ~RTCC_ALARM_AIE; + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); +} + +/** +* Reset the alarm leaving interrupt unchanged +*/ +void Rtc_Pcf8563::resetAlarm() +{ + //set status2 AF val to zero to reset alarm + status2 &= ~RTCC_ALARM_AF; + //set TF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_TIMER_TF; + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); +} + +// true if timer interrupt and control is enabled +bool Rtc_Pcf8563::timerEnabled() +{ + if (getStatus2() & RTCC_TIMER_TIE) + if (timer_control & RTCC_TIMER_TE) + return true; + return false; +} + + +// true if timer is active +bool Rtc_Pcf8563::timerActive() +{ + return getStatus2() & RTCC_TIMER_TF; +} + + +// enable timer and interrupt +void Rtc_Pcf8563::enableTimer(void) +{ + getDateTime(); + //set TE to 1 + timer_control |= RTCC_TIMER_TE; + //set status2 TF val to zero + status2 &= ~RTCC_TIMER_TF; + //set AF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_ALARM_AF; + //enable the interrupt + status2 |= RTCC_TIMER_TIE; + + // Enable interrupt first, then enable timer + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_TIMER1_ADDR); + Wire.write((byte)timer_control); // Timer starts ticking now! + Wire.endTransmission(); +} + + +// set count-down value and frequency +void Rtc_Pcf8563::setTimer(byte value, byte frequency, bool is_pulsed) +{ + getDateTime(); + if (is_pulsed) + status2 |= 0x01 << 4; + else + status2 &= ~(0x01 << 4); + timer_value = value; + // TE set to 1 in enableTimer(), leave 0 for now + timer_control |= (frequency & RTCC_TIMER_TD10); // use only last 2 bits + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_TIMER1_ADDR); + Wire.write((byte)timer_control); + Wire.write((byte)timer_value); + Wire.endTransmission(); + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); + + enableTimer(); +} + + +// clear timer flag and interrupt +void Rtc_Pcf8563::clearTimer(void) +{ + getDateTime(); + //set status2 TF val to zero + status2 &= ~RTCC_TIMER_TF; + //set AF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_ALARM_AF; + //turn off the interrupt + status2 &= ~RTCC_TIMER_TIE; + //turn off the timer + timer_control = 0; + + // Stop timer first + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_TIMER1_ADDR); + Wire.write((byte)timer_control); + Wire.endTransmission(); + + // clear flag and interrupt + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); +} + + +// clear timer flag but leave interrupt unchanged */ +void Rtc_Pcf8563::resetTimer(void) +{ + getDateTime(); + //set status2 TF val to zero to reset timer + status2 &= ~RTCC_TIMER_TF; + //set AF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_ALARM_AF; + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); +} + +/** +* Set the square wave pin output +*/ +void Rtc_Pcf8563::setSquareWave(byte frequency) +{ + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)RTCC_SQW_ADDR); + Wire.write((byte)frequency); + Wire.endTransmission(); +} + +void Rtc_Pcf8563::clearSquareWave() +{ + Rtc_Pcf8563::setSquareWave(SQW_DISABLE); +} + +const char *Rtc_Pcf8563::formatTime(byte style) +{ + getTime(); + switch (style) { + case RTCC_TIME_HM: + strOut[0] = '0' + (hour / 10); + strOut[1] = '0' + (hour % 10); + strOut[2] = ':'; + strOut[3] = '0' + (minute / 10); + strOut[4] = '0' + (minute % 10); + strOut[5] = '\0'; + break; + case RTCC_TIME_HMS: + default: + strOut[0] = '0' + (hour / 10); + strOut[1] = '0' + (hour % 10); + strOut[2] = ':'; + strOut[3] = '0' + (minute / 10); + strOut[4] = '0' + (minute % 10); + strOut[5] = ':'; + strOut[6] = '0' + (sec / 10); + strOut[7] = '0' + (sec % 10); + strOut[8] = '\0'; + break; + } + return strOut; +} + + +const char *Rtc_Pcf8563::formatDate(byte style) +{ + getDate(); + + switch (style) { + + case RTCC_DATE_ASIA: + //do the asian style, yyyy-mm-dd + if (century ){ + strDate[0] = '1'; + strDate[1] = '9'; + } + else { + strDate[0] = '2'; + strDate[1] = '0'; + } + strDate[2] = '0' + (year / 10 ); + strDate[3] = '0' + (year % 10); + strDate[4] = '-'; + strDate[5] = '0' + (month / 10); + strDate[6] = '0' + (month % 10); + strDate[7] = '-'; + strDate[8] = '0' + (day / 10); + strDate[9] = '0' + (day % 10); + strDate[10] = '\0'; + break; + case RTCC_DATE_US: + //the pitiful US style, mm/dd/yyyy + strDate[0] = '0' + (month / 10); + strDate[1] = '0' + (month % 10); + strDate[2] = '/'; + strDate[3] = '0' + (day / 10); + strDate[4] = '0' + (day % 10); + strDate[5] = '/'; + if (century){ + strDate[6] = '1'; + strDate[7] = '9'; + } + else { + strDate[6] = '2'; + strDate[7] = '0'; + } + strDate[8] = '0' + (year / 10 ); + strDate[9] = '0' + (year % 10); + strDate[10] = '\0'; + break; + case RTCC_DATE_WORLD: + default: + //do the world style, dd-mm-yyyy + strDate[0] = '0' + (day / 10); + strDate[1] = '0' + (day % 10); + strDate[2] = '-'; + strDate[3] = '0' + (month / 10); + strDate[4] = '0' + (month % 10); + strDate[5] = '-'; + + if (century){ + strDate[6] = '1'; + strDate[7] = '9'; + } + else { + strDate[6] = '2'; + strDate[7] = '0'; + } + strDate[8] = '0' + (year / 10 ); + strDate[9] = '0' + (year % 10); + strDate[10] = '\0'; + break; + + } + return strDate; +} + +void Rtc_Pcf8563::initClock() +{ + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)0x0); // start address + + Wire.write((byte)0x0); //control/status1 + Wire.write((byte)0x0); //control/status2 + Wire.write((byte)0x81); //set seconds & VL + Wire.write((byte)0x01); //set minutes + Wire.write((byte)0x01); //set hour + Wire.write((byte)0x01); //set day + Wire.write((byte)0x01); //set weekday + Wire.write((byte)0x01); //set month, century to 1 + Wire.write((byte)0x01); //set year to 99 + Wire.write((byte)0x80); //minute alarm value reset to 00 + Wire.write((byte)0x80); //hour alarm value reset to 00 + Wire.write((byte)0x80); //day alarm value reset to 00 + Wire.write((byte)0x80); //weekday alarm value reset to 00 + Wire.write((byte)0x0); //set SQW, see: setSquareWave + Wire.write((byte)0x0); //timer off + Wire.endTransmission(); +} + +void Rtc_Pcf8563::setTime(byte hour, byte minute, byte sec) +{ + getDateTime(); + setDateTime(getDay(), getWeekday(), getMonth(), + getCentury(), getYear(), hour, minute, sec); +} + +void Rtc_Pcf8563::setDate(byte day, byte weekday, byte month, bool century, byte year) +{ + getDateTime(); + setDateTime(day, weekday, month, century, year, + getHour(), getMinute(), getSecond()); +} + +void Rtc_Pcf8563::getDate() +{ + getDateTime(); +} + +void Rtc_Pcf8563::getTime() +{ + getDateTime(); +} + +bool Rtc_Pcf8563::getVoltLow(void) +{ + return volt_low; +} + +byte Rtc_Pcf8563::getSecond() { + return sec; +} + +byte Rtc_Pcf8563::getMinute() { + return minute; +} + +byte Rtc_Pcf8563::getHour() { + return hour; +} + +byte Rtc_Pcf8563::getAlarmMinute() { + return alarm_minute; +} + +byte Rtc_Pcf8563::getAlarmHour() { + return alarm_hour; +} + +byte Rtc_Pcf8563::getAlarmDay() { + return alarm_day; +} + +byte Rtc_Pcf8563::getAlarmWeekday() { + return alarm_weekday; +} + +byte Rtc_Pcf8563::getTimerControl() { + return timer_control; +} + +byte Rtc_Pcf8563::getTimerValue() { + // Impossible to freeze this value, it could + // be changing during read. Multiple reads + // required to check for consistency. + uint8_t last_value; + do { + last_value = timer_value; + getDateTime(); + } while (timer_value != last_value); + return timer_value; +} + +byte Rtc_Pcf8563::getDay() { + return day; +} + +byte Rtc_Pcf8563::getMonth() { + return month; +} + +byte Rtc_Pcf8563::getYear() { + return year; +} + +bool Rtc_Pcf8563::getCentury() { + return century; +} + +byte Rtc_Pcf8563::getWeekday() { + return weekday; +} + +byte Rtc_Pcf8563::getStatus1() { + return status1; +} + +byte Rtc_Pcf8563::getStatus2() { + return status2; +} + +unsigned long Rtc_Pcf8563::getTimestamp(){ + getDateTime(); // update date and time + unsigned long timestamp = 0; + + // Convert years in days + timestamp = (year-epoch_year)*365; // convert years in days + + if((year-epoch_year)>1) // add a dy when it's a leap year + { + for(unsigned char i = epoch_year; i2&&isLeapYear(century, year)) timestamp++; // test for the year's febuary + + // add months converted in days + if(month>1) timestamp += months_days[month-2]; + + // add days + timestamp += (day-epoch_day); + + timestamp*= 86400; // convert days in seconds + + // convert time to second and add it to timestamp + unsigned long timeTemp = hour*60+ minute; + timeTemp *=60; + timeTemp += sec; + + timestamp += timeTemp; // add hours +minutes + seconds + + timestamp += EPOCH_TIMESTAMP; // add epoch reference + + return timestamp; +} diff --git a/lib/Rtc_Pcf8563/Rtc_Pcf8563.h b/lib/Rtc_Pcf8563/Rtc_Pcf8563.h new file mode 100644 index 0000000..3096030 --- /dev/null +++ b/lib/Rtc_Pcf8563/Rtc_Pcf8563.h @@ -0,0 +1,307 @@ +/***** + * NAME + * Pcf8563 Real Time Clock support routines + * AUTHOR + * Joe Robertson, jmr + * orbitalair@bellsouth.net + * http://orbitalair.wikispaces.com/Arduino + * CREATION DATE + * 9/24/06, init - built off of usart demo. using mikroC + * NOTES + * HISTORY + * 10/14/06 ported to CCS compiler, jmr + * 2/21/09 changed all return values to hex val and not bcd, jmr + * 1/10/10 ported to arduino, jmr + * 2/14/10 added 3 world date formats, jmr + * 28/02/2012 A. Pasotti + * fixed a bug in RTCC_ALARM_AF, + * added a few (not really useful) methods + * 22/10/2014 Fix whitespace, tabs, and newlines, cevich + * 22/10/2014 add voltLow get/set, cevich + * 22/10/2014 add century get, cevich + * 22/10/2014 Fix get/set date/time race condition, cevich + * 22/10/2014 Header/Code rearranging, alarm/timer flag masking, + * extern Wire, cevich + * 26/11/2014 Add zeroClock(), initialize to lowest possible + * values, cevich + * 22/10/2014 add timer support, cevich + * 24/01/2018 add getTimestamp, gilou + * + * TODO + * x Add Euro date format + * Add short time (hh:mm) format + * Add 24h/12h format + ****** + * Robodoc embedded documentation. + * http://www.xs4all.nl/~rfsber/Robo/robodoc.html + */ + +#ifndef Rtc_Pcf8563_H +#define Rtc_Pcf8563_H + +/* the read and write values for pcf8563 rtcc */ +/* these are adjusted for arduino */ +#define RTCC_R 0xa3 +#define RTCC_W 0xa2 + +#define RTCC_SEC 1 +#define RTCC_MIN 2 +#define RTCC_HR 3 +#define RTCC_DAY 4 +#define RTCC_WEEKDAY 5 +#define RTCC_MONTH 6 +#define RTCC_YEAR 7 +#define RTCC_CENTURY 8 + +/* register addresses in the rtc */ +#define RTCC_STAT1_ADDR 0x0 +#define RTCC_STAT2_ADDR 0x01 +#define RTCC_SEC_ADDR 0x02 +#define RTCC_MIN_ADDR 0x03 +#define RTCC_HR_ADDR 0x04 +#define RTCC_DAY_ADDR 0x05 +#define RTCC_WEEKDAY_ADDR 0x06 +#define RTCC_MONTH_ADDR 0x07 +#define RTCC_YEAR_ADDR 0x08 +#define RTCC_ALRM_MIN_ADDR 0x09 +#define RTCC_SQW_ADDR 0x0D +#define RTCC_TIMER1_ADDR 0x0E +#define RTCC_TIMER2_ADDR 0x0F + +/* setting the alarm flag to 0 enables the alarm. + * set it to 1 to disable the alarm for that value. + */ +#define RTCC_ALARM 0x80 +#define RTCC_ALARM_AIE 0x02 +#define RTCC_ALARM_AF 0x08 +/* optional val for no alarm setting */ +#define RTCC_NO_ALARM 99 + +#define RTCC_TIMER_TIE 0x01 // Timer Interrupt Enable + +#define RTCC_TIMER_TF 0x04 // Timer Flag, read/write active state + // When clearing, be sure to set RTCC_TIMER_AF + // to 1 (see note above). +#define RTCC_TIMER_TI_TP 0x10 // 0: INT is active when TF is active + // (subject to the status of TIE) + // 1: INT pulses active + // (subject to the status of TIE); + // Note: TF stays active until cleared + // no matter what RTCC_TIMER_TI_TP is. +#define RTCC_TIMER_TD10 0x03 // Timer source clock, TMR_1MIN saves power +#define RTCC_TIMER_TE 0x80 // Timer 1:enable/0:disable + +/* Timer source-clock frequency constants */ +#define TMR_4096HZ B00000000 +#define TMR_64Hz B00000001 +#define TMR_1Hz B00000010 +#define TMR_1MIN B00000011 + +#define RTCC_CENTURY_MASK 0x80 +#define RTCC_VLSEC_MASK 0x80 + +/* date format flags */ +#define RTCC_DATE_WORLD 0x01 +#define RTCC_DATE_ASIA 0x02 +#define RTCC_DATE_US 0x04 +/* time format flags */ +#define RTCC_TIME_HMS 0x01 +#define RTCC_TIME_HM 0x02 + +/* square wave constants */ +#define SQW_DISABLE B00000000 +#define SQW_32KHZ B10000000 +#define SQW_1024HZ B10000001 +#define SQW_32HZ B10000010 +#define SQW_1HZ B10000011 + +/* epoch timestamp constants : 01/01/2016 à 00:00:00 : 1451599200 */ +#define epoch_day 1 +#define epoch_month 1 +#define epoch_year 16 +#define EPOCH_TIMESTAMP 1451606400 +const unsigned int months_days[]={31,59,90,120,151,181,212,243,273,304,334}; // days count for each month + +#include +#include + +extern TwoWire Wire; + +/* arduino class */ +class Rtc_Pcf8563 { + public: + Rtc_Pcf8563(); + Rtc_Pcf8563(int, int); /* Construct using different pins for sda & sdl */ + + void zeroClock(); /* Zero date/time, alarm / timer, default clkout */ + void clearStatus(); /* set both status bytes to zero */ + byte readStatus2(); + void clearVoltLow(void); /* Only clearing is possible */ + + void getDateTime(); /* get date and time vals to local vars */ + void setDateTime(byte day, byte weekday, byte month, bool century, byte year, + byte hour, byte minute, byte sec); + void getAlarm(); // same as getDateTime + bool alarmEnabled(); // true if alarm interrupt is enabled + bool alarmActive(); // true if alarm is active (going off) + void enableAlarm(); /* activate alarm flag and interrupt */ + /* set alarm vals, 99=ignore */ + void setAlarm(byte min, byte hour, byte day, byte weekday); + void clearAlarm(); /* clear alarm flag and interrupt */ + void resetAlarm(); /* clear alarm flag but leave interrupt unchanged */ + + bool timerEnabled(); // true if timer and interrupt is enabled + bool timerActive(); // true if timer is active (going off) + void enableTimer(void); // activate timer flag and interrupt + void setTimer(byte value, byte frequency, bool is_pulsed); // set value & frequency + void clearTimer(void); // clear timer flag, and interrupt, leave value unchanged + void resetTimer(void); // same as clearTimer() but leave interrupt unchanged */ + + void setSquareWave(byte frequency); + void clearSquareWave(); + + /* get a output string, these call getTime/getDate for latest vals */ + const char *formatTime(byte style=RTCC_TIME_HMS); + /* date supports 3 styles as listed in the wikipedia page about world date/time. */ + const char *formatDate(byte style=RTCC_DATE_US); + + /* Return leap-days between start (inclusive) and end (exclusive) */ + int leapDaysBetween(byte century_start, byte year_start, + byte century_end, byte year_end) const; + /* Return True if century (1: 1900, 0:2000) + decade is a leap year. */ + bool isLeapYear(byte century, int year) const; + /* Return number of days in any month of any decade of any year */ + byte daysInMonth(byte century, byte year, byte month) const; + /* Return the number of days since the beginning of a particular year*/ + byte daysInYear(byte century, byte year, byte month, byte day) const; + /* Return the weekday for any date after 1900 */ + byte whatWeekday(byte day, byte month, byte century, int year) const; + + bool getVoltLow(); + byte getSecond(); + byte getMinute(); + byte getHour(); + byte getDay(); + byte getMonth(); + byte getYear(); + bool getCentury(); + byte getWeekday(); + byte getStatus1(); + byte getStatus2(); + + byte getAlarmMinute(); + byte getAlarmHour(); + byte getAlarmDay(); + byte getAlarmWeekday(); + + byte getTimerControl(); + byte getTimerValue(); + + unsigned long getTimestamp(); // return unix timestamp + + // Sets date/time to static fixed values, disable all alarms + // use zeroClock() above to guarantee lowest possible values instead. + void initClock(); + // Slightly unsafe, don't use for new code, use above instead! + void setTime(byte hour, byte minute, byte sec); + void getTime(); // unsafe, don't use + void setDate(byte day, byte weekday, byte month, bool century, byte year); + void getDate(); // unsafe, don't use + + private: + /* methods */ + byte decToBcd(byte value); + byte bcdToDec(byte value); + /* time variables */ + byte hour; + byte minute; + bool volt_low; + byte sec; + byte day; + byte weekday; + byte month; + byte year; + /* alarm */ + byte alarm_hour; + byte alarm_minute; + byte alarm_weekday; + byte alarm_day; + /* CLKOUT */ + byte squareWave; + /* timer */ + byte timer_control; + byte timer_value; + /* support */ + byte status1; + byte status2; + bool century; + + char strOut[9]; + char strDate[11]; + + int Rtcc_Addr; +}; + + +inline int Rtc_Pcf8563::leapDaysBetween(byte century_start, byte year_start, + byte century_end, byte year_end) const { + // Credit: Victor Haydin via stackoverflow.com + int span_start = 2000 - (century_start * 100) + year_start; + int span_end = 2000 - (century_end * 100) + year_end - 1; // less year_end + // Subtract leap-years before span_start, from leap-years before span_end + return ((span_end / 4) - (span_end / 100) + (span_end / 400)) - + ((span_start / 4) - (span_start / 100) + (span_start / 400)); +} + + +inline bool Rtc_Pcf8563::isLeapYear(byte century, int year) const +{ + year = 2000 - (century * 100) + year; + if ((year % 4) != 0) + return false; + else if ((year % 100) != 0) + return true; + else if ((year % 400) != 0) + return false; + else + return true; +} + + +inline byte Rtc_Pcf8563::daysInMonth(byte century, + byte year, + byte month) const +{ + const int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + byte dim = days[month]; + if (month == 2 && isLeapYear(century, year)) + dim += 1; + return dim; +} + + +inline byte Rtc_Pcf8563::daysInYear(byte century, + byte year, + byte month, + byte day) const +{ + const int days[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + byte total = days[month - 1] + day; + if ((month > 2) and isLeapYear(century, year)) + total += 1; + return total; +} + + +inline byte Rtc_Pcf8563::whatWeekday(byte day, byte month, + byte century, int year) const +{ + year = 2000 - (century * 100) + year; + // Credit: Tomohiko Sakamoto + // http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week + year -= month < 3; + static int trans[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; + return (year + year / 4 - year / 100 + year / 400 + + trans[month - 1] + day) % 7; +} +#endif diff --git a/lib/Rtc_Pcf8563/examples/lcd_clock/lcd_clock.pde b/lib/Rtc_Pcf8563/examples/lcd_clock/lcd_clock.pde new file mode 100644 index 0000000..5299367 --- /dev/null +++ b/lib/Rtc_Pcf8563/examples/lcd_clock/lcd_clock.pde @@ -0,0 +1,63 @@ +/* Demonstration of Rtc_Pcf8563 Clock on LCD. + * + * I used a RBBB with Arduino IDE, the pins are mapped a + * bit differently. Change for your hw. + * SCK - A5, SDA - A4, INT - D3/INT1 + * + * This sketch connects a lcd to display the clock output. + * + * setup: see Pcf8563 data sheet. + * 1x 10Kohm pullup on Pin3 INT + * No pullups on Pin5 or Pin6 (I2C internals used) + * 1x 0.1pf on power + * 1x 32khz chrystal + * 1x h44780 LCD + * + * Joe Robertson, jmr + * orbitalair@bellsouth.net + */ + +#include +#include +/* add the lcd support */ +#include + +//init the real time clock +Rtc_Pcf8563 rtc; + +/* initialize the library objects */ +/* LiquidCrystal lcd(rs, en, d4, d5, d6, d7); */ +LiquidCrystal lcd(4 ,9 ,5 ,6 ,7 ,8); + +void setup() +{ + // set up the LCD's number of rows and columns: + lcd.begin(16, 2); + + //clear out all the registers + rtc.initClock(); + //set a time to start with. + //day, weekday, month, century, year + rtc.setDate(14, 6, 3, 0, 10); + //hr, min, sec + rtc.setTime(1, 15, 40); +} + +void loop() +{ + // set the cursor to column 0, line 1 + // (note: line 1 is the second row, since counting begins with 0): + lcd.setCursor(0, 0); + //lcd.print(rtc.formatTime(RTCC_TIME_HM)); + lcd.print(rtc.formatTime()); + lcd.setCursor(0, 1); + //lcd.print(rtc.formatDate(RTCC_DATE_ASIA)); + lcd.print(rtc.formatDate()); + + delay(1000); + +} + + + + diff --git a/lib/Rtc_Pcf8563/examples/set_alarm/set_alarm.pde b/lib/Rtc_Pcf8563/examples/set_alarm/set_alarm.pde new file mode 100644 index 0000000..a0bc0b8 --- /dev/null +++ b/lib/Rtc_Pcf8563/examples/set_alarm/set_alarm.pde @@ -0,0 +1,87 @@ +/* Demonstration of Rtc_Pcf8563 Alarms. + * + * The Pcf8563 has an interrupt output, Pin3. + * Pull Pin3 HIGH with a resistor, I used a 10kohm to 5v. + * I used a RBBB with Arduino IDE, the pins are mapped a + * bit differently. Change for your hw. + * SCK - A5, SDA - A4, INT - D3/INT1 + * + * After loading and starting the sketch, use the serial monitor + * to see the clock output. + * + * setup: see Pcf8563 data sheet. + * 1x 10Kohm pullup on Pin3 INT + * No pullups on Pin5 or Pin6 (I2C internals used) + * 1x 0.1pf on power + * 1x 32khz chrystal + * + * Joe Robertson, jmr + * orbitalair@bellsouth.net + */ +#include +#include + +/* get a real time clock object */ +Rtc_Pcf8563 rtc; + +/* a flag for the interrupt */ +volatile int alarm_flag=0; + +/* the interrupt service routine */ +void blink() +{ + alarm_flag=1; +} + +void setup() +{ + pinMode(3, INPUT); // set pin to input + digitalWrite(3, HIGH); // turn on pullup resistors + + Serial.begin(9600); + + /* setup int on pin 3 of arduino */ + attachInterrupt(1, blink, FALLING); + /* clear out all the registers */ + rtc.initClock(); + /* set a time to start with. + * day, weekday, month, century, year */ + rtc.setDate(14, 6, 3, 0, 10); + /* hr, min, sec */ + rtc.setTime(1, 15, 40); + /* set an alarm for 20 secs later... + * alarm pin goes low when match occurs + * this triggers the interrupt routine + * min, hr, day, weekday + * 99 = no alarm value to be set + */ + rtc.setAlarm(16, 99, 99, 99); +} + +void loop() +{ + /* each sec update the display */ + Serial.print(rtc.formatTime()); + Serial.print(" "); + Serial.print(rtc.formatDate()); + Serial.print(" 0x"); + Serial.print(rtc.getStatus2(), HEX); + Serial.print("\r\n"); + delay(1000); + if (alarm_flag==1){ + clr_alarm(); + } + +} + +void clr_alarm() +{ + detachInterrupt(1); + Serial.print("blink!\r\n"); + + rtc.clearAlarm(); + delay(1000); + alarm_flag=0; + attachInterrupt(1, blink, FALLING); +} + diff --git a/lib/Rtc_Pcf8563/examples/set_clock/set_clock.pde b/lib/Rtc_Pcf8563/examples/set_clock/set_clock.pde new file mode 100644 index 0000000..7b7b451 --- /dev/null +++ b/lib/Rtc_Pcf8563/examples/set_clock/set_clock.pde @@ -0,0 +1,52 @@ +/* Demonstration of Rtc_Pcf8563 Set Time. + * Set the clock to a time then loop over reading time and + * output the time and date to the serial console. + * + * I used a RBBB with Arduino IDE, the pins are mapped a + * bit differently. Change for your hw + * SCK - A5, SDA - A4, INT - D3/INT1 + * + * After loading and starting the sketch, use the serial monitor + * to see the clock output. + * + * setup: see Pcf8563 data sheet. + * 1x 10Kohm pullup on Pin3 INT + * No pullups on Pin5 or Pin6 (I2C internals used) + * 1x 0.1pf on power + * 1x 32khz chrystal + * + * Joe Robertson, jmr + * orbitalair@bellsouth.net + */ +#include +#include + +//init the real time clock +Rtc_Pcf8563 rtc; + +void setup() +{ + //clear out the registers + rtc.initClock(); + //set a time to start with. + //day, weekday, month, century(1=1900, 0=2000), year(0-99) + rtc.setDate(14, 6, 3, 1, 10); + //hr, min, sec + rtc.setTime(1, 15, 0); + Serial.begin(9600); +} + +void loop() +{ + //both format functions call the internal getTime() so that the + //formatted strings are at the current time/date. + Serial.print(rtc.formatTime()); + Serial.print("\r\n"); + Serial.print(rtc.formatDate()); + Serial.print("\r\n"); + delay(1000); +} + + + + diff --git a/lib/Rtc_Pcf8563/keywords.txt b/lib/Rtc_Pcf8563/keywords.txt new file mode 100644 index 0000000..95328e5 --- /dev/null +++ b/lib/Rtc_Pcf8563/keywords.txt @@ -0,0 +1,66 @@ +####################################### +# Syntax Coloring Map For Rtc_Pcf8563 +####################################### +# Datatypes (KEYWORD1) +####################################### + +Rtc_Pcf8563 KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +zeroClock KEYWORD2 +initClock KEYWORD2 +clearStatus KEYWORD2 +getDateTime KEYWORD2 +getDate KEYWORD2 +setDateTime KEYWORD2 +setDate KEYWORD2 +getTime KEYWORD2 +setTime KEYWORD2 +setAlarm KEYWORD2 +clearAlarm KEYWORD2 +getStatus1 KEYWORD2 +getStatus2 KEYWORD2 +getVoltLow KEYWORD2 +getSecond KEYWORD2 +getMinute KEYWORD2 +getHour KEYWORD2 +getDay KEYWORD2 +getWeekDay KEYWORD2 +getMonth KEYWORD2 +getYear KEYWORD2 +getCentury KEYWORD2 +formatTime KEYWORD2 +formatDate KEYWORD2 +timerEnabled KEYWORD2 +timerActive KEYWORD2 +enableTImer KEYWORD2 +setTimer KEYWORD2 +clearTimer KEYWORD2 +resetTimer KEYWORD2 +getTimerControl KEYWORD2 +getTimerValue KEYWORD2 + + +####################################### +# Constants (LITERAL1) +####################################### + +RTCC_DATE_WORLD LITERAL1 +RTCC_DATE_ASIA LITERAL1 +RTCC_DATE_US LITERAL1 +RTCC_TIME_HMS LITERAL1 +RTCC_TIME_HM LITERAL1 + +SQW_DISABLE LITERAL1 +SQW_32KHZ LITERAL1 +SQW_1024HZ LITERAL1 +SQW_32HZ LITERAL1 +SQW_1HZ LITERAL1 + +TMR_4096HZ LITERAL1 +TMR_64Hz LITERAL1 +TMR_1Hz LITERAL1 +TMR_1MIN LITERAL1 diff --git a/lib/Rtc_Pcf8563/library.json b/lib/Rtc_Pcf8563/library.json new file mode 100644 index 0000000..3e590a0 --- /dev/null +++ b/lib/Rtc_Pcf8563/library.json @@ -0,0 +1,16 @@ +{ + "name": "Rtc_Pcf8563", + "keywords": "rtc, clock, time, calendar", + "description": "Real-Time Clock (RTC) and calendar optimized for low power consumption", + "repository": + { + "type": "git", + "url": "https://github.com/elpaso/Rtc_Pcf8563.git" + }, + "authors": { + "name": "Joe Robertson", + "url": "http://orbitalair.wikispaces.com/Arduino" + }, + "frameworks": "arduino", + "platforms": "atmelavr" +} diff --git a/platformio.ini b/platformio.ini index 76172bd..8b9076c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,7 +16,6 @@ lib_deps = adafruit/Adafruit GFX Library@^1.11.5 GxEPD2 Time - Rtc_Pcf8563 NTPClient lib_ldf_mode = deep+