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.
This commit is contained in:
parent
38c801d3b6
commit
614b005a54
681
Library/PCF8563.cpp
Normal file
681
Library/PCF8563.cpp
Normal file
@ -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 <Arduino.h>
|
||||
#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.
|
||||
@ -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 <Arduino.h>
|
||||
#include <TimeLib.h>
|
||||
#include <Wire.h>
|
||||
|
||||
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
|
||||
Loading…
x
Reference in New Issue
Block a user