764 lines
20 KiB
C++
764 lines
20 KiB
C++
// Code by JeeLabs http://news.jeelabs.org/code/
|
|
// Released to the public domain! Enjoy!
|
|
|
|
#include <Wire.h>
|
|
#include "RTClib.h"
|
|
|
|
#define PROGMEM
|
|
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
|
|
#define WIRE Wire
|
|
|
|
#define DS1307_ADDRESS 0x68
|
|
#define PCF8563_ADDRESS 0xA3
|
|
#define PCF8583_ADDRESS 0xA0
|
|
|
|
#define PCF8563_ALARM_REG_OFF 0x80
|
|
#define PCF8563_ALARM_AIE 0x02
|
|
#define PCF8563_ALARM_AF 0x08 // 0x08 : not 0x04!!!!
|
|
/* optional val for no alarm setting */
|
|
#define PCF8563_NO_ALARM 0x99
|
|
|
|
// i2c slave address of the DS3231 chip
|
|
#define DS3231_ADDRESS 0x68
|
|
|
|
// registers
|
|
#define DS3231_TIME_ADDR 0x00
|
|
#define DS3231_ALARM_1_ADDR 0x07
|
|
#define DS3231_ALARM_2_ADDR 0x0B
|
|
#define DS3231_CONTROL_ADDR 0x0E
|
|
#define DS3231_STATUS_ADDR 0x0F
|
|
#define DS3231_AGING_OFFSET_ADDR 0x10
|
|
#define DS3231_TEMPERATURE_ADDR 0x11
|
|
|
|
// control bits
|
|
#define DS3231_ALARM_1_IE 0x1
|
|
#define DS3231_ALARM_2_IE 0x2
|
|
#define DS3231_INTC 0x4
|
|
|
|
// status bits
|
|
#define DS3231_ALARM_1_AF 0x1
|
|
#define DS3231_ALARM_2_AF 0x2
|
|
#define DS3231_OSF 0x80
|
|
|
|
#define SECONDS_PER_DAY 86400L
|
|
|
|
#define SECONDS_FROM_1970_TO_2000 946684800
|
|
|
|
#if (ARDUINO >= 100)
|
|
#include <Arduino.h> // capital A so it is error prone on case-sensitive filesystems
|
|
#else
|
|
#include <WProgram.h>
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// utility code, some of this could be exposed in the DateTime API if needed
|
|
static uint8_t bcd2bin (uint8_t val) { return val - 6 * (val >> 4); }
|
|
static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); }
|
|
|
|
const uint8_t daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };
|
|
|
|
// number of days since 2000/01/01, valid for 2001..2099
|
|
static uint16_t date2days(uint16_t y, uint8_t m, uint8_t d) {
|
|
if (y >= 2000)
|
|
y -= 2000;
|
|
uint16_t days = d;
|
|
for (uint8_t i = 1; i < m; ++i)
|
|
days += pgm_read_byte(daysInMonth + i - 1);
|
|
if (m > 2 && y % 4 == 0)
|
|
++days;
|
|
return days + 365 * y + (y + 3) / 4 - 1;
|
|
}
|
|
|
|
static long time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s) {
|
|
return ((days * 24L + h) * 60 + m) * 60 + s;
|
|
}
|
|
|
|
static uint8_t conv2d(const char* p) {
|
|
uint8_t v = 0;
|
|
if ('0' <= *p && *p <= '9')
|
|
v = *p - '0';
|
|
return 10 * v + *++p - '0';
|
|
}
|
|
|
|
uint8_t _read(const int dev, const uint8_t addr) {
|
|
WIRE.beginTransmission(dev);
|
|
WIRE.write(addr);
|
|
WIRE.endTransmission();
|
|
WIRE.requestFrom(dev, 1);
|
|
uint8_t s = WIRE.read();
|
|
return s;
|
|
}
|
|
void _write(const int dev, const uint8_t addr, const uint8_t val) {
|
|
WIRE.beginTransmission(dev);
|
|
WIRE.write(addr);
|
|
WIRE.write(val);
|
|
WIRE.endTransmission();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// DateTime implementation - ignores time zones and DST changes
|
|
// NOTE: also ignores leap seconds, see http://en.wikipedia.org/wiki/Leap_second
|
|
|
|
DateTime::DateTime (uint32_t t) {
|
|
t -= SECONDS_FROM_1970_TO_2000; // bring to 2000 timestamp from 1970
|
|
|
|
ss = t % 60;
|
|
t /= 60;
|
|
mm = t % 60;
|
|
t /= 60;
|
|
hh = t % 24;
|
|
uint16_t days = t / 24;
|
|
uint8_t leap;
|
|
for (yOff = 0; ; ++yOff) {
|
|
leap = yOff % 4 == 0;
|
|
if (days < 365 + leap)
|
|
break;
|
|
days -= 365 + leap;
|
|
}
|
|
for (m = 1; ; ++m) {
|
|
uint8_t daysPerMonth = pgm_read_byte(daysInMonth + m - 1);
|
|
if (leap && m == 2)
|
|
++daysPerMonth;
|
|
if (days < daysPerMonth)
|
|
break;
|
|
days -= daysPerMonth;
|
|
}
|
|
d = days + 1;
|
|
}
|
|
|
|
DateTime::DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) {
|
|
if (year >= 2000)
|
|
year -= 2000;
|
|
yOff = year;
|
|
m = month;
|
|
d = day;
|
|
hh = hour;
|
|
mm = min;
|
|
ss = sec;
|
|
}
|
|
|
|
// A convenient constructor for using "the compiler's time":
|
|
// DateTime now (__DATE__, __TIME__);
|
|
// NOTE: using PSTR would further reduce the RAM footprint
|
|
DateTime::DateTime (const char* date, const char* time) {
|
|
// sample input: date = "Dec 26 2009", time = "12:34:56"
|
|
// or date="26-12-2009"
|
|
yOff = conv2d(date + 9);
|
|
// Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
|
|
d = conv2d(date + 4);
|
|
switch (date[0]) {
|
|
case 'J': m = date[1] == 'a' ? 1 : m = date[2] == 'n' ? 6 : 7; break;
|
|
case 'F': m = 2; break;
|
|
case 'A': m = date[2] == 'r' ? 4 : 8; break;
|
|
case 'M': m = date[2] == 'r' ? 3 : 5; break;
|
|
case 'S': m = 9; break;
|
|
case 'O': m = 10; break;
|
|
case 'N': m = 11; break;
|
|
case 'D': m = 12; break;
|
|
default:
|
|
yOff = conv2d(date + 8);
|
|
d = conv2d(date);
|
|
m = conv2d(date + 3);
|
|
}
|
|
hh = conv2d(time);
|
|
mm = conv2d(time + 3);
|
|
ss = conv2d(time + 6);
|
|
}
|
|
|
|
// A convenient constructor for using "the compiler's time":
|
|
// This version will save RAM by using PROGMEM to store it by using the F macro.
|
|
// DateTime now (F(__DATE__), F(__TIME__));
|
|
DateTime::DateTime (const __FlashStringHelper* date, const __FlashStringHelper* time) {
|
|
// sample input: date = "Dec 26 2009", time = "12:34:56"
|
|
char _date[11];
|
|
char _time[8];
|
|
memcpy_P(_date, date, 11);
|
|
memcpy_P(_time, time, 8);
|
|
DateTime(date, time);
|
|
}
|
|
|
|
DateTime::DateTime(const char* sdate) {
|
|
DateTime(sdate, sdate+11);
|
|
}
|
|
|
|
/*********************************************/
|
|
/* Comparison */
|
|
/*********************************************/
|
|
|
|
unsigned char DateTime::equals(const char* sdate) const
|
|
{
|
|
return equals(DateTime(sdate));
|
|
}
|
|
unsigned char DateTime::equals(const DateTime &date) const
|
|
{
|
|
return yOff == date.year()-2000 && m == date.month() && d == date.day() && hh == date.hour() && mm == date.minute() && ss == date.second();
|
|
}
|
|
unsigned char DateTime::operator<(const DateTime &date) const
|
|
{
|
|
return unixtime() < date.unixtime();
|
|
}
|
|
unsigned char DateTime::operator>(const DateTime &date) const
|
|
{
|
|
return unixtime() > date.unixtime();
|
|
}
|
|
unsigned char DateTime::operator<=(const DateTime &date) const
|
|
{
|
|
return unixtime() <= date.unixtime();
|
|
}
|
|
unsigned char DateTime::operator>=(const DateTime &date) const
|
|
{
|
|
return unixtime() >= date.unixtime();
|
|
}
|
|
|
|
uint8_t DateTime::dayOfWeek() const {
|
|
uint16_t day = date2days(yOff, m, d);
|
|
return (day + 6) % 7; // Jan 1, 2000 is a Saturday, i.e. returns 6
|
|
}
|
|
|
|
uint32_t DateTime::unixtime(void) const {
|
|
uint32_t t;
|
|
uint16_t days = date2days(yOff, m, d);
|
|
t = time2long(days, hh, mm, ss);
|
|
t += SECONDS_FROM_1970_TO_2000; // seconds from 1970 to 2000
|
|
|
|
return t;
|
|
}
|
|
|
|
DateTime DateTime::operator + (const TimeDelta& delta) {
|
|
return DateTime(unixtime() + delta.totalseconds());
|
|
}
|
|
DateTime DateTime::operator - (const TimeDelta& delta) {
|
|
return DateTime(unixtime() - delta.totalseconds());
|
|
}
|
|
TimeDelta DateTime::operator - (const DateTime& right) {
|
|
return TimeDelta(unixtime() - right.unixtime());
|
|
}
|
|
|
|
char* DateTime::format(char* ret){
|
|
for(int i=0;i<strlen(ret)-1;i++){
|
|
if(ret[i] == 'h' && ret[i+1] == 'h'){
|
|
ret[i] = '0'+hh/10;
|
|
ret[i+1] = '0'+hh%10;
|
|
}
|
|
if(ret[i] == 'm' && ret[i+1] == 'm'){
|
|
ret[i] = '0'+mm/10;
|
|
ret[i+1] = '0'+mm%10;
|
|
}
|
|
if(ret[i] == 's' && ret[i+1] == 's'){
|
|
ret[i] = '0'+ss/10;
|
|
ret[i+1] = '0'+ss%10;
|
|
}
|
|
if(ret[i] == 'D' && ret[i+1] == 'D'){
|
|
ret[i] = '0'+d/10;
|
|
ret[i+1] = '0'+d%10;
|
|
}
|
|
if(ret[i] == 'M' && ret[i+1] == 'M'){
|
|
ret[i] = '0'+m/10;
|
|
ret[i+1] = '0'+m%10;
|
|
}
|
|
if(ret[i] == 'Y'&& ret[i+3] == 'Y'){
|
|
ret[i] = '2';
|
|
ret[i+1] = '0';
|
|
ret[i+2] = '0'+yOff/10;
|
|
ret[i+3] = '0'+yOff%10;
|
|
}else
|
|
if(ret[i] == 'Y'&& ret[i+1] == 'Y'){
|
|
ret[i] = '0'+yOff/10;
|
|
ret[i+1] = '0'+yOff%10;
|
|
}
|
|
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// TimeDelta implementation
|
|
TimeDelta::TimeDelta (uint32_t seconds):
|
|
_sec(seconds)
|
|
{}
|
|
TimeDelta::TimeDelta (uint16_t days, uint8_t hours, uint8_t minutes, uint8_t seconds):
|
|
_sec(days*86400L + hours*3600 + minutes*60 + seconds){}
|
|
TimeDelta::TimeDelta (const TimeDelta& copy):
|
|
_sec(copy._sec){}
|
|
TimeDelta TimeDelta::operator + (const TimeDelta& right) {
|
|
return TimeDelta(_sec + right._sec);
|
|
}
|
|
TimeDelta TimeDelta::operator-(const TimeDelta& right) {
|
|
return TimeDelta(_sec - right._sec);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// RTC_DS1302 implementation
|
|
DS1302:: DS1302 (uint8_t ce_pin, uint8_t sck_pin, uint8_t io_pin) {
|
|
ce = ce_pin;
|
|
sck = sck_pin;
|
|
io = io_pin;
|
|
}
|
|
uint8_t DS1302::begin(void) {
|
|
pinMode(ce, OUTPUT);
|
|
pinMode(sck, OUTPUT);
|
|
pinMode(io, INPUT);
|
|
write(7,0);
|
|
uint8_t sec = read(0);
|
|
sec |= (1 << 7);
|
|
write(0, sec);
|
|
return 1;
|
|
}
|
|
uint8_t DS1302::read() {
|
|
pinMode(io, INPUT);
|
|
uint8_t value = 0;
|
|
for (uint8_t i = 0; i < 8; ++i) {
|
|
uint8_t bit = digitalRead(io);
|
|
value |= (bit << i); // Bits are read LSB first.
|
|
digitalWrite(sck, HIGH);
|
|
digitalWrite(sck, LOW);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
void DS1302::write(const uint8_t val) {
|
|
pinMode(io, OUTPUT);
|
|
shiftOut(io, sck, LSBFIRST, val);
|
|
}
|
|
uint8_t DS1302::read(const uint8_t addr) {
|
|
const uint8_t cmd = (0x81 | (addr << 1));
|
|
write(cmd);
|
|
return read();
|
|
}
|
|
|
|
void DS1302::write(const uint8_t addr, const uint8_t val) {
|
|
const uint8_t cmd = (0x80 | (addr << 1));
|
|
write(cmd);
|
|
write(val);
|
|
}
|
|
|
|
uint8_t DS1302::isrunning(void) {
|
|
uint8_t ss = read(0);
|
|
return !(ss>>7);
|
|
}
|
|
DateTime DS1302::now() {
|
|
write(0xBF);
|
|
uint8_t ss = bcd2bin(read() & 0x7F);
|
|
uint8_t mm = bcd2bin(read());
|
|
uint8_t hh = bcd2bin(read());
|
|
uint8_t d = bcd2bin(read());
|
|
uint8_t m = bcd2bin(read());
|
|
read();
|
|
uint16_t y = bcd2bin(read()) + 2000;
|
|
return DateTime (y, m, d, hh, mm, ss);
|
|
}
|
|
void DS1302::adjust(const DateTime& dt) {
|
|
write(0xBE);
|
|
write(bin2bcd(dt.second()));
|
|
write(bin2bcd(dt.minute()));
|
|
write(bin2bcd(dt.hour()));
|
|
write(bin2bcd(dt.day()));
|
|
write(bin2bcd(dt.month()));
|
|
write(bin2bcd(dt.dayOfWeek()));
|
|
write(bin2bcd(dt.year() - 2000));
|
|
write(0);
|
|
}
|
|
// RTC_DS1307 implementation
|
|
|
|
uint8_t DS1307::begin(void) {
|
|
return 1;
|
|
}
|
|
|
|
uint8_t DS1307::read(const uint8_t addr) {
|
|
return _read(DS1307_ADDRESS,addr);
|
|
}
|
|
void DS1307::write(const uint8_t addr, const uint8_t val) {
|
|
_write(DS1307_ADDRESS,addr,val);
|
|
}
|
|
|
|
uint8_t DS1307::isrunning(void) {
|
|
uint8_t ss = read(0);
|
|
return !(ss>>7);
|
|
}
|
|
|
|
void DS1307::adjust(const DateTime& dt) {
|
|
WIRE.beginTransmission(DS1307_ADDRESS);
|
|
WIRE.write(0);
|
|
WIRE.write(bin2bcd(dt.second()));
|
|
WIRE.write(bin2bcd(dt.minute()));
|
|
WIRE.write(bin2bcd(dt.hour()));
|
|
WIRE.write(bin2bcd(0));
|
|
WIRE.write(bin2bcd(dt.day()));
|
|
WIRE.write(bin2bcd(dt.month()));
|
|
WIRE.write(bin2bcd(dt.year() - 2000));
|
|
WIRE.write(0);
|
|
WIRE.endTransmission();
|
|
}
|
|
|
|
DateTime DS1307::now() {
|
|
WIRE.beginTransmission(DS1307_ADDRESS);
|
|
WIRE.write(0);
|
|
WIRE.endTransmission();
|
|
|
|
WIRE.requestFrom(DS1307_ADDRESS, 7);
|
|
uint8_t ss = bcd2bin(WIRE.read() & 0x7F);
|
|
uint8_t mm = bcd2bin(WIRE.read());
|
|
uint8_t hh = bcd2bin(WIRE.read());
|
|
WIRE.read();
|
|
uint8_t d = bcd2bin(WIRE.read());
|
|
uint8_t m = bcd2bin(WIRE.read());
|
|
uint16_t y = bcd2bin(WIRE.read()) + 2000;
|
|
|
|
return DateTime (y, m, d, hh, mm, ss);
|
|
}
|
|
|
|
uint8_t DS3231::begin(void) {
|
|
write(DS3231_CONTROL_ADDR, DS3231_INTC);
|
|
}
|
|
|
|
uint8_t DS3231::read(const uint8_t addr) {
|
|
return _read(DS3231_ADDRESS,addr);
|
|
}
|
|
void DS3231::write(const uint8_t addr, const uint8_t val) {
|
|
_write(DS3231_ADDRESS,addr,val);
|
|
}
|
|
uint8_t DS3231::isrunning(void) {
|
|
uint8_t ss = read(0);
|
|
return !(ss>>7);
|
|
}
|
|
|
|
void DS3231::adjust(const DateTime& dt) {
|
|
uint8_t year,cen;
|
|
if(dt.year() > 2000){
|
|
year = dt.year() - 2000;
|
|
cen = 0x80;
|
|
}else{
|
|
year = dt.year() - 1900;
|
|
cen = 0;
|
|
}
|
|
WIRE.beginTransmission(DS3231_ADDRESS);
|
|
WIRE.write(DS3231_TIME_ADDR);
|
|
WIRE.write(bin2bcd(dt.second()));
|
|
WIRE.write(bin2bcd(dt.minute()));
|
|
WIRE.write(bin2bcd(dt.hour()));
|
|
WIRE.write(bin2bcd(0));
|
|
WIRE.write(bin2bcd(dt.day()));
|
|
WIRE.write(bin2bcd(dt.month())+ cen);
|
|
WIRE.write(bin2bcd(year));
|
|
WIRE.endTransmission();
|
|
}
|
|
|
|
DateTime DS3231::now() {
|
|
WIRE.beginTransmission(DS3231_ADDRESS);
|
|
WIRE.write(DS3231_TIME_ADDR);
|
|
WIRE.endTransmission();
|
|
WIRE.requestFrom(DS3231_ADDRESS, 7);
|
|
uint8_t ss = bcd2bin(WIRE.read() & 0x7F);
|
|
uint8_t mm = bcd2bin(WIRE.read());
|
|
uint8_t hh = bcd2bin(WIRE.read());
|
|
WIRE.read();
|
|
uint8_t d = bcd2bin(WIRE.read());
|
|
uint8_t cen = WIRE.read();
|
|
uint8_t m = bcd2bin(cen & 0x1F);
|
|
uint16_t y = bcd2bin(WIRE.read());
|
|
if((cen & 0x80) >> 7 == 1){
|
|
y += 2000;
|
|
}else{
|
|
y += 1900;
|
|
}
|
|
return DateTime (y, m, d, hh, mm, ss);
|
|
}
|
|
|
|
double DS3231::getTemp() {
|
|
double temp;
|
|
WIRE.beginTransmission(DS3231_ADDRESS);
|
|
WIRE.write(DS3231_TEMPERATURE_ADDR);
|
|
WIRE.endTransmission();
|
|
WIRE.requestFrom(DS3231_ADDRESS, 2);
|
|
temp = (double)WIRE.read();
|
|
temp += (double)0.25*(WIRE.read() >> 6);
|
|
return temp;
|
|
}
|
|
|
|
// provide device address as a full 8 bit address (like the datasheet)
|
|
PCF8583::PCF8583(int device_address) {
|
|
address = device_address >> 1; // convert to 7 bit so Wire doesn't choke
|
|
}
|
|
PCF8583::PCF8583() {
|
|
address = PCF8583_ADDRESS >> 1; // convert to 7 bit so Wire doesn't choke
|
|
}
|
|
|
|
// initialization
|
|
uint8_t PCF8583::begin()
|
|
{
|
|
_write(address,0x00,0x04);// Set alarm on int\ will turn to vcc
|
|
return 1;
|
|
}
|
|
|
|
DateTime PCF8583::now()
|
|
{
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0xC0); // stop counting, don't mask
|
|
Wire.endTransmission();
|
|
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x02);
|
|
Wire.endTransmission();
|
|
Wire.requestFrom(address, 5);
|
|
|
|
uint8_t second = bcd2bin(Wire.read());
|
|
uint8_t minute = bcd2bin(Wire.read());
|
|
uint8_t hour = bcd2bin(Wire.read());
|
|
uint8_t incoming = Wire.read(); // year/date counter
|
|
uint8_t day = bcd2bin(incoming & 0x3f);
|
|
uint8_t year = (int)((incoming >> 6) & 0x03); // it will only hold 4 years...
|
|
incoming = Wire.read();
|
|
uint8_t month = bcd2bin(incoming & 0x1f);
|
|
uint8_t dow = incoming >> 5;
|
|
|
|
// but that's not all - we need to find out what the base year is
|
|
// so we can add the 2 bits we got above and find the real year
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x10);
|
|
Wire.endTransmission();
|
|
Wire.requestFrom(address, 2);
|
|
uint8_t year_base = 0;
|
|
year_base = Wire.read();
|
|
year_base = year_base << 8;
|
|
year_base = year_base | Wire.read();
|
|
year = year + year_base;
|
|
return DateTime (year, month, day, hour, minute, second);
|
|
}
|
|
|
|
|
|
void PCF8583::adjust(const DateTime& dt)
|
|
{
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0xC0); // stop counting, don't mask
|
|
Wire.endTransmission();
|
|
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x02);
|
|
Wire.write(bin2bcd(dt.second()));
|
|
Wire.write(bin2bcd(dt.minute()));
|
|
Wire.write(bin2bcd(dt.hour()));
|
|
Wire.write(((byte)(dt.year() - 2000) << 6) | bin2bcd(dt.day()));
|
|
Wire.write((dt.dayOfWeek() << 5) | (bin2bcd(dt.month()) & 0x1f));
|
|
Wire.endTransmission();
|
|
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x10);
|
|
Wire.write(2000 >> 8);
|
|
Wire.write(2000 & 0x00ff);
|
|
Wire.endTransmission();
|
|
|
|
begin(); // re set the control/status register to 0x04
|
|
|
|
}
|
|
uint8_t PCF8583::isrunning(void){
|
|
uint8_t ss = _read(address,0);
|
|
return !(ss>>7);
|
|
}
|
|
|
|
//Get the alarm at 0x09 adress
|
|
DateTime PCF8583::get_alarm()
|
|
{
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x0A); // Set the register pointer to (0x0A)
|
|
Wire.endTransmission();
|
|
|
|
Wire.requestFrom(address, 4); // Read 4 values
|
|
|
|
uint8_t second = bcd2bin(Wire.read());
|
|
uint8_t minute = bcd2bin(Wire.read());
|
|
uint8_t hour = bcd2bin(Wire.read());
|
|
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x0E);
|
|
Wire.endTransmission();
|
|
|
|
Wire.requestFrom(address, 1); // Read weekday value
|
|
|
|
uint8_t day = bcd2bin(Wire.read());
|
|
return DateTime (2000, 01, day, hour, minute, second);
|
|
}
|
|
|
|
//Set a daily alarm
|
|
void PCF8583::set_alarm(const DateTime& dt)
|
|
{
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x08);
|
|
Wire.write(0x90); // daily alarm set
|
|
Wire.endTransmission();
|
|
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x09); // Set the register pointer to (0x09)
|
|
Wire.write(0x00); // Set 00 at milisec
|
|
Wire.write(bin2bcd(dt.second()));
|
|
Wire.write(bin2bcd(dt.minute()));
|
|
Wire.write(bin2bcd(dt.hour()));
|
|
Wire.write(0x00); // Set 00 at day
|
|
Wire.endTransmission();
|
|
}
|
|
void PCF8583::off_alarm()
|
|
{
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x08);
|
|
Wire.write(0x00); // off alarm set
|
|
Wire.endTransmission();
|
|
}
|
|
|
|
// provide device address as a full 8 bit address (like the datasheet)
|
|
PCF8563::PCF8563(int device_address) {
|
|
address = device_address >> 1; // convert to 7 bit so Wire doesn't choke
|
|
}
|
|
PCF8563::PCF8563() {
|
|
address = PCF8563_ADDRESS >> 1; // convert to 7 bit so Wire doesn't choke
|
|
}
|
|
|
|
// initialization
|
|
uint8_t PCF8563::begin()
|
|
{
|
|
Wire.begin();
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x00); // Start address
|
|
Wire.write(0); // Control and status 1
|
|
Wire.write(0); // Control and status 2
|
|
return Wire.endTransmission();
|
|
}
|
|
|
|
DateTime PCF8563::now()
|
|
{
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x00);
|
|
Wire.endTransmission();
|
|
Wire.requestFrom(address, 9);
|
|
|
|
status1 = Wire.read();
|
|
status2 = Wire.read();
|
|
uint8_t second = bcd2bin(Wire.read() & 0x7F);
|
|
uint8_t minute = bcd2bin(Wire.read() & 0x7F);
|
|
uint8_t hour = bcd2bin(Wire.read() & 0x3F);
|
|
uint8_t day = bcd2bin(Wire.read() & 0x3F);
|
|
uint8_t wekday = Wire.read() & 0x07; // year/date counter
|
|
uint8_t month = Wire.read();
|
|
uint8_t century = month >> 7;
|
|
month = bcd2bin(month & 0x1F);
|
|
uint8_t year = bcd2bin(Wire.read()); // it will only hold 4 years...
|
|
return DateTime (year, month, day, hour, minute, second);
|
|
}
|
|
|
|
void PCF8563::adjust(const DateTime& dt)
|
|
{
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x02); // Start address
|
|
Wire.write(bin2bcd(dt.second())); // Second (0-59)
|
|
Wire.write(bin2bcd(dt.minute())); // Minute (0-59)
|
|
Wire.write(bin2bcd(dt.hour())); // Hour (0-23)
|
|
Wire.write(bin2bcd(dt.day())); // Day (1-31)
|
|
Wire.write(bin2bcd(dt.dayOfWeek())); // Weekday (0-6 = Sunday-Saturday)
|
|
Wire.write(bin2bcd(dt.month()) | 0x80); // Month (1-12, century bit (bit 7) = 1)
|
|
Wire.write(bin2bcd(dt.year() % 100)); // Year (00-99)
|
|
Wire.endTransmission();
|
|
|
|
begin(); // re set the control/status register to 0x04
|
|
}
|
|
uint8_t PCF8563::isrunning(void){
|
|
WIRE.beginTransmission(address);
|
|
WIRE.write(0);
|
|
WIRE.endTransmission();
|
|
|
|
WIRE.requestFrom(address, 2);
|
|
|
|
status1 = Wire.read();
|
|
status2 = Wire.read();
|
|
return !(bitRead(status1,5));
|
|
}
|
|
|
|
//Get the alarm at 0x09 adress
|
|
DateTime PCF8563::get_alarm()
|
|
{
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x09); // Set the register pointer to (0x0A)
|
|
Wire.endTransmission();
|
|
|
|
Wire.requestFrom(address, 4); // Read 4 values
|
|
|
|
uint8_t minute = bcd2bin(Wire.read());
|
|
uint8_t hour = bcd2bin(Wire.read());
|
|
uint8_t day = bcd2bin(Wire.read());
|
|
uint8_t wday = bcd2bin(Wire.read());
|
|
return DateTime (0, wday, day, hour, minute, 0);
|
|
}
|
|
|
|
//Set a daily alarm
|
|
void PCF8563::set_alarm(const DateTime &dt, struct alarm_flags flags)
|
|
{
|
|
byte minute = bin2bcd(dt.minute());
|
|
byte hour = bin2bcd(dt.hour());
|
|
byte day = bin2bcd(dt.day());
|
|
byte dayOfWeek = bin2bcd(dt.month());
|
|
|
|
if( !flags.minute)
|
|
minute |= PCF8563_ALARM_REG_OFF;
|
|
|
|
if( !flags.hour)
|
|
hour |= PCF8563_ALARM_REG_OFF;
|
|
|
|
if( !flags.day)
|
|
day |= PCF8563_ALARM_REG_OFF;
|
|
|
|
if( !flags.wday)
|
|
dayOfWeek |= PCF8563_ALARM_REG_OFF;
|
|
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x09); // Set the register pointer to (0x09)
|
|
Wire.write(minute);
|
|
Wire.write(hour);
|
|
Wire.write(day);
|
|
Wire.write(dayOfWeek);
|
|
Wire.endTransmission();
|
|
}
|
|
|
|
void PCF8563::off_alarm()
|
|
{
|
|
//set status2 AF val to zero to reset alarm
|
|
status2 &= ~PCF8563_ALARM_AF;
|
|
Wire.beginTransmission(address);
|
|
Wire.write(0x01);
|
|
Wire.write(status2);
|
|
Wire.endTransmission();
|
|
}
|
|
|
|
void PCF8563::on_alarm()
|
|
{
|
|
//set status2 AF val to zero
|
|
status2 &= ~PCF8563_ALARM_AF;
|
|
//enable the interrupt
|
|
status2 |= PCF8563_ALARM_AIE;
|
|
Wire.beginTransmission(address); // Issue I2C start signal
|
|
Wire.write(0x01);
|
|
Wire.write(status2);
|
|
Wire.endTransmission();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// RTC_Millis implementation
|
|
|
|
long RTC_Millis::offset = 0;
|
|
|
|
void RTC_Millis::adjust(const DateTime& dt) {
|
|
offset = dt.unixtime() - millis() / 1000;
|
|
ok = 1;
|
|
}
|
|
|
|
DateTime RTC_Millis::now() {
|
|
return DateTime((uint32_t)(offset + millis() / 1000));
|
|
}
|
|
|
|
uint8_t RTC_Millis::isrunning(void){
|
|
return ok;
|
|
}
|
|
uint8_t RTC_Millis::begin()
|
|
{
|
|
ok = 0;
|
|
return 1;
|
|
}
|
|
////////////////////////////////////////////////////////////////////////////////
|