Skip to content

Commit e70ae4d

Browse files
author
Jack Christensen
committed
Alpha version v0.82
1 parent 816b313 commit e70ae4d

File tree

3 files changed

+201
-17
lines changed

3 files changed

+201
-17
lines changed

MCP79412RTC.cpp

Lines changed: 157 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ void MCP79412RTC::read(tmElements_t &tm)
6969
tm.Second = bcd2dec(i2cRead() & ~_BV(ST));
7070
tm.Minute = bcd2dec(i2cRead());
7171
tm.Hour = bcd2dec(i2cRead() & ~_BV(HR1224)); //assumes 24hr clock
72-
tm.Wday = bcd2dec(i2cRead() & ~(_BV(OSCON) | _BV(VBAT) | _BV(VBATEN)) ); //mask off OSCON, VBAT, VBATEN bits
72+
tm.Wday = i2cRead() & ~(_BV(OSCON) | _BV(VBAT) | _BV(VBATEN)); //mask off OSCON, VBAT, VBATEN bits
7373
tm.Day = bcd2dec(i2cRead());
7474
tm.Month = bcd2dec(i2cRead() & ~_BV(LP)); //mask off the leap year bit
7575
tm.Year = y2kYearToTm(bcd2dec(i2cRead()));
@@ -85,7 +85,7 @@ void MCP79412RTC::write(tmElements_t &tm)
8585
i2cWrite(0x00); //stops the oscillator (Bit 7, ST == 0)
8686
i2cWrite(dec2bcd(tm.Minute));
8787
i2cWrite(dec2bcd(tm.Hour)); //sets 24 hour format (Bit 6 == 0)
88-
i2cWrite(dec2bcd(tm.Wday) | _BV(VBATEN)); //enable battery backup operation
88+
i2cWrite(tm.Wday | _BV(VBATEN)); //enable battery backup operation
8989
i2cWrite(dec2bcd(tm.Day));
9090
i2cWrite(dec2bcd(tm.Month));
9191
i2cWrite(dec2bcd(tmYearToY2k(tm.Year)));
@@ -328,6 +328,161 @@ void MCP79412RTC::idRead(byte *uniqueID)
328328
for (byte i=0; i<UNIQUE_ID_SIZE; i++) uniqueID[i] = i2cRead();
329329
}
330330

331+
/*----------------------------------------------------------------------*
332+
* Check to see if a power failure has occurred. If so, returns TRUE *
333+
* as the function value, and returns the power down and power up *
334+
* timestamps. After returning the time stamps, the RTC's timestamp *
335+
* registers are cleared and the VBAT bit which indicates a power *
336+
* failure is reset. *
337+
* *
338+
* Note that the power down and power up timestamp registers do not *
339+
* contain values for seconds or for the year. The returned time stamps *
340+
* will therefore contain the current year from the RTC. However, there *
341+
* is a chance that a power outage spans from one year to the next. *
342+
* If we find the power down timestamp to be later (larger) than the *
343+
* power up timestamp, we will assume this has happened, and well *
344+
* subtract one year from the power down timestamp. *
345+
* *
346+
* Still, there is an assumption that the timestamps are being read *
347+
* in the same year as that when the power up occurred. *
348+
* *
349+
* Finally, note that once the RTC records a power outage, it must be *
350+
* cleared before another will be recorded. *
351+
*----------------------------------------------------------------------*/
352+
boolean MCP79412RTC::powerFail(time_t *powerDown, time_t *powerUp)
353+
{
354+
byte day, yr; //copies of the RTC Day and Year registers
355+
tmElements_t dn, up; //power down and power up times
356+
357+
ramRead(DAY_REG, &day, 1);
358+
ramRead(YEAR_REG, &yr, 1);
359+
yr = y2kYearToTm(bcd2dec(yr));
360+
if ( day & _BV(VBAT) ) {
361+
Wire.beginTransmission(RTC_ADDR);
362+
i2cWrite(PWRDWN_TS_REG);
363+
Wire.endTransmission();
364+
365+
Wire.requestFrom(RTC_ADDR, TIMESTAMP_SIZE); //read both timestamp registers, 8 bytes total
366+
dn.Second = 0;
367+
dn.Minute = bcd2dec(i2cRead());
368+
dn.Hour = bcd2dec(i2cRead() & ~_BV(HR1224)); //assumes 24hr clock
369+
dn.Day = bcd2dec(i2cRead());
370+
dn.Month = bcd2dec(i2cRead() & 0x1F); //mask off the day, we don't need it
371+
dn.Year = yr; //assume current year
372+
up.Second = 0;
373+
up.Minute = bcd2dec(i2cRead());
374+
up.Hour = bcd2dec(i2cRead() & ~_BV(HR1224)); //assumes 24hr clock
375+
up.Day = bcd2dec(i2cRead());
376+
up.Month = bcd2dec(i2cRead() & 0x1F); //mask off the day, we don't need it
377+
up.Year = yr; //assume current year
378+
379+
*powerDown = makeTime(dn);
380+
*powerUp = makeTime(up);
381+
382+
//clear the VBAT bit, which causes the RTC hardware to clear the timestamps too.
383+
//I suppose there is a risk here that the day has changed since we read it,
384+
//but the Day of Week is actually redundant data and the makeTime() function
385+
//does not use it. This could be an issue if someone is reading the RTC
386+
//registers directly, but as this library is meant to be used with the Time library,
387+
//and also because we don't provide a method to read the RTC clock/calendar
388+
//registers directly, we won't lose any sleep about it at this point unless
389+
//some issue is actually brought to our attention ;-)
390+
day &= ~_BV(VBAT);
391+
ramWrite(DAY_REG, &day , 1);
392+
393+
//adjust the powerDown timestamp if needed (see notes above)
394+
if (*powerDown > *powerUp) {
395+
--dn.Year;
396+
*powerDown = makeTime(dn);
397+
}
398+
}
399+
else
400+
return false;
401+
}
402+
403+
/*----------------------------------------------------------------------*
404+
* Enable or disable the square wave output. *
405+
*----------------------------------------------------------------------*/
406+
void MCP79412RTC::squareWave(uint8_t freq)
407+
{
408+
uint8_t ctrlReg;
409+
410+
ramRead(CTRL_REG, &ctrlReg, 1);
411+
if (freq > 3) {
412+
ctrlReg &= ~_BV(SQWE);
413+
}
414+
else {
415+
ctrlReg = (ctrlReg & 0xF8) | _BV(SQWE) | freq;
416+
}
417+
ramWrite(CTRL_REG, &ctrlReg, 1);
418+
}
419+
420+
/*----------------------------------------------------------------------*
421+
* Set an alarm time. Sets the alarm registers only, does not enable *
422+
* the alarm. See enableAlarm(). *
423+
*----------------------------------------------------------------------*/
424+
void MCP79412RTC::setAlarm(uint8_t alarmNumber, time_t alarmTime)
425+
{
426+
tmElements_t tm;
427+
uint8_t day; //need to preserve bits in the day (of week) register
428+
429+
alarmNumber &= 0x01; //ensure a valid alarm number
430+
ramRead( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG) , &day, 1);
431+
breakTime(alarmTime, tm);
432+
Wire.beginTransmission(RTC_ADDR);
433+
i2cWrite( ALM0_REG + alarmNumber * (ALM1_REG - ALM0_REG) );
434+
i2cWrite(dec2bcd(tm.Second));
435+
i2cWrite(dec2bcd(tm.Minute));
436+
i2cWrite(dec2bcd(tm.Hour)); //sets 24 hour format (Bit 6 == 0)
437+
i2cWrite( (day & 0xF8) + tm.Wday );
438+
i2cWrite(dec2bcd(tm.Day));
439+
i2cWrite(dec2bcd(tm.Month));
440+
Wire.endTransmission();
441+
}
442+
443+
/*----------------------------------------------------------------------*
444+
* Enable or disable an alarm, and set the trigger criteria, *
445+
* e.g. match only seconds, only minutes, entire time and date, etc. *
446+
*----------------------------------------------------------------------*/
447+
void MCP79412RTC::enableAlarm(uint8_t alarmNumber, uint8_t alarmType)
448+
{
449+
uint8_t day; //alarm day register has config & flag bits
450+
uint8_t ctrl; //control register has alarm enable bits
451+
452+
alarmNumber &= 0x01; //ensure a valid alarm number
453+
ramRead(CTRL_REG, &ctrl, 1);
454+
if (alarmType < ALM_DISABLE) {
455+
ramRead(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
456+
day = ( day & 0x07 ) | alarmType << 4; //reset interrupt flag, OR in the config bits
457+
ramWrite(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
458+
ctrl |= _BV(ALM0 + alarmNumber); //enable the alarm
459+
}
460+
else {
461+
ctrl &= ~(_BV(ALM0 + alarmNumber)); //disable the alarm
462+
}
463+
ramWrite(CTRL_REG, &ctrl, 1);
464+
}
465+
466+
/*----------------------------------------------------------------------*
467+
* Returns true or false depending on whether the given alarm has been *
468+
* triggered, and resets the alarm "interrupt" flag. This is not a real *
469+
* interrupt, just a bit that's set when an alarm is triggered. *
470+
*----------------------------------------------------------------------*/
471+
boolean MCP79412RTC::alarm(uint8_t alarmNumber)
472+
{
473+
uint8_t day; //alarm day register has config & flag bits
474+
475+
alarmNumber &= 0x01; //ensure a valid alarm number
476+
ramRead( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG) , &day, 1);
477+
if (day & _BV(ALMIF)) {
478+
day &= ~_BV(ALMIF); //turn off the alarm "interrupt" flag
479+
ramWrite( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG) , &day, 1);
480+
return true;
481+
}
482+
else
483+
return false;
484+
}
485+
331486
/*----------------------------------------------------------------------*
332487
* Check to see if the RTC's oscillator is started (ST bit in seconds *
333488
* register). Returns true if started. *

MCP79412RTC.h

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,17 @@
4646

4747
//MCP7941x Register Addresses
4848
#define TIME_REG 0x00 //7 registers, Seconds, Minutes, Hours, DOW, Date, Month, Year
49+
#define DAY_REG 0x03 //the RTC Day register contains the OSCON, VBAT, and VBATEN bits
50+
#define YEAR_REG 0x06 //RTC year register
4951
#define CTRL_REG 0x07 //control register
5052
#define CALIB_REG 0x08 //calibration register
5153
#define UNLOCK_ID_REG 0x09 //unlock ID register
5254
#define ALM0_REG 0x0A //alarm 0, 6 registers, Seconds, Minutes, Hours, DOW, Date, Month
5355
#define ALM1_REG 0x11 //alarm 1, 6 registers, Seconds, Minutes, Hours, DOW, Date, Month
56+
#define ALM0_DAY 0x0D //DOW register has alarm config/flag bits
5457
#define PWRDWN_TS_REG 0x18 //power-down timestamp, 4 registers, Minutes, Hours, Date, Month
5558
#define PWRUP_TS_REG 0x1C //power-up timestamp, 4 registers, Minutes, Hours, Date, Month
59+
#define TIMESTAMP_SIZE 8 //number of bytes in the two timestamp registers
5660
#define SRAM_START_ADDR 0x20 //first SRAM address
5761
#define SRAM_SIZE 64 //number of bytes of SRAM
5862
#define EEPROM_SIZE 128 //number of bytes of EEPROM
@@ -70,11 +74,7 @@
7074
#define RS2 2 //RS2:0 set square wave output frequency: 0==1Hz, 1==4096Hz, 2==8192Hz, 3=32768Hz
7175
#define RS1 1
7276
#define RS0 0
73-
#define SQWAVE_1_HZ 0
74-
#define SQWAVE_4096_HZ 1
75-
#define SQWAVE_8192_HZ 2
76-
#define SQWAVE_32768_HZ 3
77-
#define SQWAVE_NONE 4
77+
enum {SQWAVE_1_HZ, SQWAVE_4096_HZ, SQWAVE_8192_HZ, SQWAVE_32768_HZ, SQWAVE_NONE};
7878

7979
//Other Control Bits
8080
#define ST 7 //Seconds register (TIME_REG) oscillator start/stop bit, 1==Start, 0==Stop
@@ -92,22 +92,19 @@
9292
#define ALMC1 5
9393
#define ALMC0 4
9494
#define ALMIF 3 //Alarm Interrupt Flag: Set by hardware when an alarm was triggered, cleared by software.
95-
#define ALM_MATCH_SECONDS 0 //Match Seconds
96-
#define ALM_MATCH_MINUTES 1 //Match Minutes
97-
#define ALM_MATCH_HOURS 2 //Match Hours
98-
#define ALM_MATCH_DAY 3 //Match Day (alarm triggers at midnight)
99-
#define ALM_MATCH_DATE 4 //Match Date
100-
#define ALM_MATCH_DATETIME 5 //Match Second, Minute, Hour, Day, Date, Month
101-
95+
enum {ALM_MATCH_SECONDS, ALM_MATCH_MINUTES, ALM_MATCH_HOURS, ALM_MATCH_DAY, ALM_MATCH_DATE, ALM_RESERVED_5, ALM_RESERVED_6, ALM_MATCH_DATETIME, ALM_DISABLE};
96+
//Note ALM_MATCH_DAY triggers alarm at midnight
97+
#define ALARM_0 0 //constants for calling functions
98+
#define ALARM_1 1
10299

103100
class MCP79412RTC
104101
{
105102
public:
106103
MCP79412RTC();
107104
static time_t get(void);
108-
static void set(time_t t);
109-
static void read(tmElements_t &tm);
110-
static void write(tmElements_t &tm);
105+
static void set(time_t t);
106+
static void read(tmElements_t &tm);
107+
static void write(tmElements_t &tm);
111108
void sramWrite(byte addr, byte value);
112109
void sramWrite(byte addr, byte *values, byte nBytes);
113110
byte sramRead(byte addr);
@@ -119,6 +116,11 @@ class MCP79412RTC
119116
int calibRead(void);
120117
void calibWrite(int value);
121118
void idRead(byte *uniqueID);
119+
boolean powerFail(time_t *powerDown, time_t *powerUp);
120+
void squareWave(uint8_t freq);
121+
void setAlarm(uint8_t alarmNumber, time_t alarmTime);
122+
void enableAlarm(uint8_t alarmNumber, uint8_t alarmType);
123+
boolean alarm(uint8_t alarmNumber);
122124

123125
boolean oscStarted(void);
124126

ReadMe.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,30 @@ void calibWrite(int value);
8989

9090
Read the 64-bit unique ID from the RTC into the given 8-byte array.
9191
void idRead(byte *uniqueID);
92+
93+
Returns true or false to indicate whether a power failure has occurred.
94+
If one occurred, the power down and power up timestamps are returned in the variables
95+
given by the caller, the RTC's power fail flag is reset and the power up/down
96+
timestamps are cleared.
97+
boolean powerFail(time_t *powerDown, time_t *powerUp);
98+
99+
Enables or disables the square wave output on the multi-function pin (MFP).
100+
freq is one of the following:
101+
SQWAVE_1_HZ, SQWAVE_4096_HZ, SQWAVE_8192_HZ, SQWAVE_32768_HZ, SQWAVE_NONE
102+
void squareWave(uint8_t freq);
103+
104+
Set an alarm time. Sets the alarm registers only, does not enable
105+
the alarm, use enableAlarm() for that. alarmNumber is 0 or 1, but is
106+
ruthlessly masked to ensure this is so.
107+
void setAlarm(uint8_t alarmNumber, time_t alarmTime);
108+
109+
Enable or disable the given alarm (0 or 1).
110+
alarmNumber is masked to ensure a value of 0 or 1.
111+
alarmType is one of the following:
112+
ALM_MATCH_SECONDS, ALM_MATCH_MINUTES, ALM_MATCH_HOURS, ALM_MATCH_DAY, ALM_MATCH_DATE, ALM_MATCH_DATETIME, ALM_DISABLE
113+
void enableAlarm(uint8_t alarmNumber, uint8_t alarmType);
114+
115+
Tests whether the given alarm (0 or 1) has been triggered, returns
116+
true if triggered, else false. Clears the alarm flag to trap the next
117+
trigger event can be trapped.
118+
boolean alarm(uint8_t alarmNumber);

0 commit comments

Comments
 (0)