From 614b005a548821dcbf4657cefdeaff3c06fd38a4 Mon Sep 17 00:00:00 2001 From: GuruSR Date: Sun, 21 Nov 2021 22:51:43 -0500 Subject: [PATCH] Beta PCF8563 RTC library. This is a rework of SQFMI's port of the original PCF8563 library with (hopefully) direct operational functionality for all Watchy watchfaces. --- Library/PCF8563.cpp | 681 ++++++++++++++++++++++++++++++++++++++++++++ Library/PCF8563.h | 330 +++++++++++++++++++++ 2 files changed, 1011 insertions(+) create mode 100644 Library/PCF8563.cpp diff --git a/Library/PCF8563.cpp b/Library/PCF8563.cpp new file mode 100644 index 0000000..f8055fa --- /dev/null +++ b/Library/PCF8563.cpp @@ -0,0 +1,681 @@ +/***** + * 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 "PCF8563.h" + +PCF8563::PCF8563(bool initI2C) +{ + Wire.begin(); + Rtcc_Addr = RTCC_R>>1; +} + +/* Private internal functions, but useful to look at if you need a similar func. */ +byte PCF8563::decToBcd(byte val) +{ + return ( (val/10*16) + (val%10) ); +} + +byte PCF8563::bcdToDec(byte val) +{ + return ( (val/16*10) + (val%16) ); +} + +void 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 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 PCF8563::readStatus2() +{ + getDateTime(); + return getStatus2(); +} + +void 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 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 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 PCF8563::getAlarm() +{ + getDateTime(); +} + +/* +* Returns true if AIE is on +* +*/ +bool PCF8563::alarmEnabled() +{ + return getStatus2() & RTCC_ALARM_AIE; +} + +/* +* Returns true if AF is on +* +*/ +bool 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 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 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(); + + PCF8563::enableAlarm(); +} + +void 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 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 PCF8563::timerEnabled() +{ + if (getStatus2() & RTCC_TIMER_TIE) + if (_timer_control & RTCC_TIMER_TE) + return true; + return false; +} + + +// true if timer is active +bool PCF8563::timerActive() +{ + return getStatus2() & RTCC_TIMER_TF; +} + + +// enable timer and interrupt +void 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 PCF8563::setTimer(byte value, byte frequency, bool is_pulsed) +{ + getDateTime(); + if (is_pulsed) + _status2 |= is_pulsed << 4; + else + _status2 &= ~(is_pulsed << 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 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 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 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 PCF8563::clearSquareWave() +{ + PCF8563::squareWave(SQWAVE_NONE); +} + +void 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 PCF8563::setTime(byte hour, byte minute, byte sec) +{ + getDateTime(); + setDateTime(getDay(), getWeekday(), getMonth(), + getCentury(), getYear(), hour, minute, sec); +} + +void PCF8563::setDate(byte day, byte weekday, byte month, bool century, byte year) +{ + getDateTime(); + setDateTime(day, weekday, month, century, year, + getHour(), getMinute(), getSecond()); +} + +void PCF8563::getDate() +{ + getDateTime(); +} + +void PCF8563::getTime() +{ + getDateTime(); +} + +bool PCF8563::getVoltLow(void) +{ + return _volt_low; +} + +byte PCF8563::getSecond() { + return _sec; +} + +byte PCF8563::getMinute() { + return _minute; +} + +byte PCF8563::getHour() { + return _hour; +} + +byte PCF8563::getAlarmMinute() { + return _alarm_minute; +} + +byte PCF8563::getAlarmHour() { + return _alarm_hour; +} + +byte PCF8563::getAlarmDay() { + return _alarm_day; +} + +byte PCF8563::getAlarmWeekday() { + return _alarm_weekday; +} + +byte PCF8563::getTimerControl() { + return _timer_control; +} + +byte 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 PCF8563::getDay() { + return _day; +} + +byte PCF8563::getMonth() { + return _month; +} + +byte PCF8563::getYear() { + return _year; +} + +bool PCF8563::getCentury() { + return _century; +} + +byte PCF8563::getWeekday() { + return _weekday; +} + +byte PCF8563::getStatus1() { + return _status1; +} + +byte PCF8563::getStatus2() { + return _status2; +} + +// Functions below "Mimic" the DS3232RTC code, so this library can be a drop in replacement for most Watch faces. + + // Read the current time from the RTC and return it as a time_t +// value. Returns a zero value if an I2C error occurred (e.g. RTC +// not present). +time_t PCF8563::get() +{ + tmElements_t tm; + + if ( read(tm) ) return 0; + return( makeTime(tm) ); +} + +// Set the RTC to the given time_t value and clear the +// oscillator stop flag (OSF) in the Control/Status register. +// Returns the I2C status (zero if successful). +byte PCF8563::set(time_t t) +{ + tmElements_t tm; + + breakTime(t, tm); + return ( write(tm) ); +} + +// Read the current time from the RTC and return it in a tmElements_t +// structure. Returns the I2C status (zero if successful). +byte PCF8563::read(tmElements_t &tm) +{ + getDateTime(); + tm.Year = _year; + tm.Month = _month; + tm.Day = _day; + tm.Wday = _weekday; + tm.Hour = _hour; + tm.Minute = _minute; + tm.Second = _sec; + tm.Year += TIME_H_DIFF; // Add the extra 30 years on when using this function. +// tm.Year = y2kYearToTm(T.Year); + return 0; +} + +// Set the RTC time from a tmElements_t structure and clear the +// oscillator stop flag (OSF) in the Control/Status register. +// Returns the I2C status (zero if successful). +byte PCF8563::write(tmElements_t &tm) +{ + tmElements_t T = tm; + T.Year -= TIME_H_DIFF; // Take the extra 30 years off when using this function. + setDateTime(T.Day, T.Wday, T.Month, 0, T.Year, T.Hour, T.Minute, T.Second); + return 0; +} + +void PCF8563::setAlarm(ALARM_TYPES_t alarmType, byte seconds, byte minutes, byte hours, byte daydate) { setAlarm(minutes, hours, daydate, 0); } +void PCF8563::setAlarm(ALARM_TYPES_t alarmType, byte minutes, byte hours, byte daydate) { setAlarm(minutes, hours, daydate, 0); } +void PCF8563::alarmInterrupt(byte alarmNumber, bool alarmEnabled) { if (alarmEnabled) enableAlarm(); else resetAlarm(); } +bool PCF8563::alarm(byte alarmNumber) +{ + tmElements_t tm; + time_t t; + + if (alarmNumber == ALARM_2) + { + clearAlarm(); + getDateTime(); + tm.Year = _year; + tm.Month = _month; + tm.Day = _day; + tm.Wday = _weekday; + tm.Hour = _hour; + tm.Minute = _minute; + tm.Second = _sec; + tm.Year += TIME_H_DIFF; // Add the extra 30 years on when using this function. + t = makeTime(tm) + (60 - _sec); + breakTime(t, tm); + setAlarm(tm.Minute, tm.Hour, tm.Day, tm.Wday); + enableAlarm(); + } + return (_status2 & RTCC_ALARM_AF); +} +bool PCF8563::checkAlarm(byte alarmNumber) { return alarm(alarmNumber); } +bool PCF8563::clearAlarm(byte alarmNumber) { clearAlarm(); } +void PCF8563::squareWave(SQWAVE_FREQS_t freq) { setSquareWave((byte)freq); } +bool PCF8563::oscStopped(bool clearOSF) { return false; } // Not sure this works. +int16_t PCF8563::temperature() { return 32767; } // 0x7FFF returns to prove it is the PCF8563 not the DS3232. diff --git a/Library/PCF8563.h b/Library/PCF8563.h index 8b13789..66a5f1c 100644 --- a/Library/PCF8563.h +++ b/Library/PCF8563.h @@ -1 +1,331 @@ +/***** + * 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 + */ +#ifndef PCF8563_H +#define 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 + +// Alarm masks +enum ALARM_TYPES_t { + ALM1_EVERY_SECOND = 0x0F, + ALM1_MATCH_SECONDS = 0x0E, + ALM1_MATCH_MINUTES = 0x0C, // match minutes *and* seconds + ALM1_MATCH_HOURS = 0x08, // match hours *and* minutes, seconds + ALM1_MATCH_DATE = 0x00, // match date *and* hours, minutes, seconds + ALM1_MATCH_DAY = 0x10, // match day *and* hours, minutes, seconds + ALM2_EVERY_MINUTE = 0x8E, + ALM2_MATCH_MINUTES = 0x8C, // match minutes + ALM2_MATCH_HOURS = 0x88, // match hours *and* minutes + ALM2_MATCH_DATE = 0x80, // match date *and* hours, minutes + ALM2_MATCH_DAY = 0x90, // match day *and* hours, minutes +}; + +// Square-wave output frequency (TS2, RS1 bits) +#define SQW_32KHZ B10000000 +enum SQWAVE_FREQS_t { + SQWAVE_1_HZ = B10000011, + SQWAVE_1024_HZ = B10000001, + SQWAVE_4096_HZ = 0, + SQWAVE_8192_HZ = 0, + SQWAVE_NONE = B00000000 +}; + +#define ALARM_1 1 // constants for alarm functions +#define ALARM_2 2 + +#define TIME_H_DIFF 30 // 30 years difference between 2000 and 1970 when using TimeLib functions. + +/* DS3232RTC compatibility Ends */ + +#include +#include +#include + +extern TwoWire Wire; + +/* arduino class */ +class PCF8563 { + public: + PCF8563(bool initI2C = true); + + 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(); + + /* 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(); + + // 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 + + // DS3232RTC compat + time_t get(); + byte set(time_t t); + byte read(tmElements_t &tm); + byte write(tmElements_t &tm); + void setAlarm(ALARM_TYPES_t alarmType, byte seconds, byte minutes, byte hours, byte daydate); + void setAlarm(ALARM_TYPES_t alarmType, byte minutes, byte hours, byte daydate); + void alarmInterrupt(byte alarmNumber, bool alarmEnabled); + bool alarm(byte alarmNumber); + bool checkAlarm(byte alarmNumber); + bool clearAlarm(byte alarmNumber); + void squareWave(SQWAVE_FREQS_t freq); + bool oscStopped(bool clearOSF = false); + int16_t temperature(); + // DS3232RTC compat End + static byte errCode; + + 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; + + int Rtcc_Addr; +}; + + +inline int 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 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 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 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 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 \ No newline at end of file