Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some functions to PCF8523 (Alarm) #291

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 250 additions & 2 deletions src/RTC_PCF8523.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#define PCF8523_CONTROL_1 0x00 ///< Control and status register 1
#define PCF8523_CONTROL_2 0x01 ///< Control and status register 2
#define PCF8523_CONTROL_3 0x02 ///< Control and status register 3
#define PCF8523_TIMER_A_FRCTL 0x10 ///< Timer A source clock frequency control
#define PCF8523_TIMER_A_VALUE 0x11 ///< Timer A value (number clock periods)
#define PCF8523_TIMER_B_FRCTL 0x12 ///< Timer B source clock frequency control
#define PCF8523_TIMER_B_VALUE 0x13 ///< Timer B value (number clock periods)
#define PCF8523_OFFSET 0x0E ///< Offset register
Expand All @@ -18,6 +20,8 @@
*/
/**************************************************************************/
bool RTC_PCF8523::begin(TwoWire *wireInstance) {
wdgTimeoutUnitySet = PCF8523_WDG_DISABLED;
wdgTimeoutValueSet = 0;
if (i2c_dev)
delete i2c_dev;
i2c_dev = new Adafruit_I2CDevice(PCF8523_ADDRESS, wireInstance);
Expand Down Expand Up @@ -45,13 +49,168 @@ bool RTC_PCF8523::lostPower(void) {
/*!
@brief Check control register 3 to see if we've run adjust() yet (setting
the date/time and battery switchover mode)
@return True if the PCF8523 has been set up, false if not
@return 1 if the PCF8523 has been set up, false if not
*/
/**************************************************************************/
bool RTC_PCF8523::initialized(void) {
return (read_register(PCF8523_CONTROL_3) & 0xE0) != 0xE0;
}

/**************************************************************************/
/*!
@brief Set RTC alarm value for a specific register
@param the_register The Alarm register concerned
@param the_value The Alarm register value
*/
/**************************************************************************/

void RTC_PCF8523::setAlarmValueForRegister(Pcf8563AlarmRegister the_register,
uint8_t the_value) {
// set the value and enable the AEN_X (enabled with 0 value)
switch (the_register) {
case PCF8523_ALARM_MINUTE:
write_register(the_register, bin2bcd((the_value % 60) & 0x7F));
break;
case PCF8523_ALARM_HOUR:
write_register(the_register, bin2bcd((the_value % 24) & 0x7F));
break;
case PCF8523_ALARM_DAY:
write_register(the_register, bin2bcd((the_value % 32) & 0x7F));
break;
case PCF8523_ALARM_WEEKDAY:
write_register(the_register, bin2bcd((the_value % 8) & 0x7F));
break;
default:
break;
}
}

/**************************************************************************/
/*!
@brief Get RTC alarm value for a specific register
@param the_register The Alarm register concerned
@return The value for the requested Alarm register
*/
/**************************************************************************/

uint8_t
RTC_PCF8523::getAlarmValueForRegister(Pcf8563AlarmRegister the_register) {
// set the value and enable the AEN_X (enabled with 0 value)
switch (the_register) {
case PCF8523_ALARM_MINUTE:
return bcd2bin(read_register(the_register));
break;
case PCF8523_ALARM_HOUR:
return bcd2bin(read_register(the_register));
break;
case PCF8523_ALARM_DAY:
return bcd2bin(read_register(the_register));
break;
case PCF8523_ALARM_WEEKDAY:
return bcd2bin(read_register(the_register));
break;
default:
break;
}
return 0xFF;
}

/**************************************************************************/
/*!
@brief RTC Upgrade oscillator capacitor From 7pF to 12.5pF
*/
/**************************************************************************/

void RTC_PCF8523::upgradeOsciCapaTo12pf5() {
write_register(PCF8523_CONTROL_1,
read_register(PCF8523_CONTROL_1) | (1 << 7)); // Enable CAP_SEL
}

/**************************************************************************/
/*!
@brief Enable Alarm based on previous setAlarmValueForRegister call
*/
/**************************************************************************/

void RTC_PCF8523::enableAlarm() {
write_register(PCF8523_CONTROL_1,
read_register(PCF8523_CONTROL_1) | (1 << 1)); // Enable AIE
}

/**************************************************************************/
/*!
@brief Check Watchdog TimerA fired, isWatchdogFired must be read at the
very beginning, because flag is read-only and cleared by reading
register Control_2
@return 1 if a Watchdog TimerA fired
*/
/**************************************************************************/

bool RTC_PCF8523::isWatchdogFired(void) {
return (read_register(PCF8523_CONTROL_2) & (1 << 7)); // Check WTAF
}

/**************************************************************************/
/*!
@brief Check Alarm fired
@return 1 if an alarm fired
*/
/**************************************************************************/

bool RTC_PCF8523::isAlarmFired(void) {
return (read_register(PCF8523_CONTROL_2) & (1 << 3)); // Check AF
}

/**************************************************************************/
/*!
@brief Clear Alarm
*/
/**************************************************************************/

void RTC_PCF8523::clearAlarm() {
write_register(PCF8523_CONTROL_2,
read_register(PCF8523_CONTROL_2) & ~(1 << 3)); // Clear AF
}

/**************************************************************************/
/*!
@brief Disable Alarm
*/
/**************************************************************************/

void RTC_PCF8523::disableAlarm() {
write_register(PCF8523_CONTROL_1,
read_register(PCF8523_CONTROL_1) & ~(1 << 1)); // Disable AIE
write_register(PCF8523_ALARM_MINUTE, 0x80);
write_register(PCF8523_ALARM_HOUR, 0x80);
write_register(PCF8523_ALARM_DAY, 0x80);
write_register(PCF8523_ALARM_WEEKDAY, 0x80);
}

/**************************************************************************/
/*!
@brief Ask if any Alarm is setup
@return true if an alarm is setup
*/
/**************************************************************************/

bool RTC_PCF8523::isAnyAlarmSetup(void) {
if (read_register(PCF8523_CONTROL_1) & (1 << 1)) {
return true;
}
return false;
}

/**************************************************************************/
/*!
@brief Set RTC battery switch-over mode in register Control_3
@param battery_switch_over_value The value for battery switch over register
*/
/**************************************************************************/
void RTC_PCF8523::setBatterySwitchOver(uint8_t battery_switch_over_value) {
write_register(PCF8523_CONTROL_3, battery_switch_over_value);
}

/**************************************************************************/
/*!
@brief Set the date and time, set battery switchover mode
Expand All @@ -70,7 +229,7 @@ void RTC_PCF8523::adjust(const DateTime &dt) {
i2c_dev->write(buffer, 8);

// set to battery switchover mode
write_register(PCF8523_CONTROL_3, 0x00);
setBatterySwitchOver(0x00);
}

/**************************************************************************/
Expand Down Expand Up @@ -110,6 +269,17 @@ void RTC_PCF8523::stop(void) {
read_register(PCF8523_CONTROL_1) | (1 << 5));
}

/**************************************************************************/
/*!
@brief Reset RTC sending 0x58 in register Control_1
*/
/**************************************************************************/
void RTC_PCF8523::reset(void) {
wdgTimeoutUnitySet = PCF8523_WDG_DISABLED;
wdgTimeoutValueSet = 0;
write_register(PCF8523_CONTROL_1, 0x58);
}

/**************************************************************************/
/*!
@brief Is the PCF8523 running? Check the STOP bit in register Control_1
Expand Down Expand Up @@ -168,6 +338,84 @@ void RTC_PCF8523::disableSecondTimer() {
read_register(PCF8523_CONTROL_1) & ~(1 << 2));
}

/**************************************************************************/
/*!
@brief Enable Timer A as Watchdog permanent Interrupt on the PCF8523.
How :
TAC[2:1] = 10 : {Tmr_CLKOUT_ctrl(0x0F)} : set as watchdog timer
TAQ[2:0] : {Tmr_A_freq_ctrl(0x10)} : determine source clock frequency
WTAIE(2) : {Control_2(0x01)} : watchdog timer A interrupt enabled

TAM(7) : {Tmr_CLKOUT_ctrl(0x0F)} : control the interrupt generation mode(permanent) force to 0

T_A : {Tmr_A_reg(0x11)} : determines the watchdog timer-period

The watchdog timer counts down from value T_A in register Tmr_A_reg (11h). When the
counter reaches 1, the watchdog timer flag WTAF (register Control_2) is set logic 1 on the
next rising edge of the timer clock

The counter does not automatically reload

When loading the counter with any valid value of T_A except 0, (interrupt is cleared, and watchdog timer starts)
When loading the counter with 0, The watchdog timer stops

A read of the register Control_2 (01h) automatically resets WTAF (WTAF = 0) and clears the interrupt
*/
/**************************************************************************/
void RTC_PCF8523::enableWatchdog(PCF8523WatchdogTimeoutUnity wdgTimeoutUnity, uint8_t wdgTimeoutValue) {
uint8_t ctlreg = read_register(PCF8523_CONTROL_2);
if(wdgTimeoutUnitySet || wdgTimeoutValueSet){
disableWatchdog();
}
if((!wdgTimeoutUnity)||(!wdgTimeoutValue)){
return;
}
wdgTimeoutUnitySet = wdgTimeoutUnity;
wdgTimeoutValueSet = wdgTimeoutValue;
write_register(PCF8523_TIMER_A_VALUE,0);//Stop the watchdog timer
//TAC[2:1] = 10 : {Tmr_CLKOUT_ctrl(0x0F)} : set as watchdog timer
write_register(PCF8523_CLKOUTCONTROL,
(read_register(PCF8523_CLKOUTCONTROL) & 0xFC) | (0x02 << 1));
//TAQ[2:0] : {Tmr_A_freq_ctrl(0x10)} : determine source clock frequency
write_register(PCF8523_TIMER_A_FRCTL,
(read_register(PCF8523_TIMER_A_FRCTL) & 0xF8) | wdgTimeoutUnitySet);
//WTAIE(2) : {Control_2(0x01)} : watchdog timer A interrupt enabled
write_register(PCF8523_CONTROL_2, ctlreg | (1 << 2));
//TAM(7) : {Tmr_CLKOUT_ctrl(0x0F)} : control the interrupt generation mode(permanent) force to 0
write_register(PCF8523_CLKOUTCONTROL,
read_register(PCF8523_CLKOUTCONTROL) & ~(1<<7));

//Start the watchdog timer
write_register(PCF8523_TIMER_A_VALUE, wdgTimeoutValueSet);
}

/**************************************************************************/
/*!
@brief Reset Timer A Watchdog on the PCF8523.
*/
/**************************************************************************/

void RTC_PCF8523::resetWatchdog(void) {
if((!wdgTimeoutUnitySet)||(!wdgTimeoutValueSet)){
return;
}
write_register(PCF8523_TIMER_A_VALUE, wdgTimeoutValueSet);
}

/**************************************************************************/
/*!
@brief Disable Timer A Watchdog on the PCF8523.
*/
/**************************************************************************/

void RTC_PCF8523::disableWatchdog(void) {
uint8_t ctlreg = read_register(PCF8523_CONTROL_2);
// WTAIE set to 0
write_register(PCF8523_CONTROL_2, ctlreg & ~(1 << 2));
wdgTimeoutUnitySet = PCF8523_WDG_DISABLED;
wdgTimeoutValueSet = 0;
}

/**************************************************************************/
/*!
@brief Enable the Countdown Timer Interrupt on the PCF8523.
Expand Down
34 changes: 34 additions & 0 deletions src/RTClib.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ enum PCF8523TimerClockFreq {
PCF8523_FrequencyHour = 4, /**< 1 hour, max 255 hours = 10.625 days */
};

/** PCF8523 Watchdog Timeout */
enum PCF8523WatchdogTimeoutUnity {
PCF8523_WDG_DISABLED = 0, /** Watchog Timer disabled*/
PCF8523_WDG_SEC = 2, /** Watchog Timer unity will be seconds*/
PCF8523_WDG_MIN = 3, /** Watchog Timer unity will be minutes*/
PCF8523_WDG_HOUR = 4, /** Watchog Timer unity will be hours*/
};

/** PCF8523 Timer Interrupt Low Pulse Width options for Timer B only */
enum PCF8523TimerIntPulse {
PCF8523_LowPulse3x64Hz = 0, /**< 46.875 ms 3/64ths second */
Expand Down Expand Up @@ -124,6 +132,14 @@ enum Pcf8563SqwPinMode {
PCF8563_SquareWave32kHz = 0x80 /**< 32kHz square wave */
};

/** PCF8563 CLKOUT pin mode settings */
enum Pcf8563AlarmRegister {
PCF8523_ALARM_MINUTE = 0x0A, ///< Minute Alarm register
PCF8523_ALARM_HOUR = 0x0B, ///< Hour Alarm register
PCF8523_ALARM_DAY = 0x0C, ///< Day Alarm register
PCF8523_ALARM_WEEKDAY = 0x0D, ///< WeekDay Alarm register
};

/**************************************************************************/
/*!
@brief Simple general-purpose date/time class (no TZ / DST / leap
Expand Down Expand Up @@ -409,9 +425,21 @@ class RTC_PCF8523 : RTC_I2C {
void adjust(const DateTime &dt);
bool lostPower(void);
bool initialized(void);
bool isWatchdogFired(void);
bool isAlarmFired(void);
bool isAnyAlarmSetup(void);
DateTime now();
void upgradeOsciCapaTo12pf5();
void start(void);
void stop(void);
void reset(void);
void setBatterySwitchOver(uint8_t battery_switch_over_value);
void setAlarmValueForRegister(Pcf8563AlarmRegister the_register,
uint8_t the_value);
uint8_t getAlarmValueForRegister(Pcf8563AlarmRegister the_register);
void enableAlarm();
void clearAlarm();
void disableAlarm();
uint8_t isrunning();
Pcf8523SqwPinMode readSqwPinMode();
void writeSqwPinMode(Pcf8523SqwPinMode mode);
Expand All @@ -423,6 +451,12 @@ class RTC_PCF8523 : RTC_I2C {
void disableCountdownTimer(void);
void deconfigureAllTimers(void);
void calibrate(Pcf8523OffsetMode mode, int8_t offset);
void enableWatchdog(PCF8523WatchdogTimeoutUnity wdgTimeoutUnity, uint8_t wdgTimeoutValue);
void resetWatchdog(void);
void disableWatchdog(void);
private:
PCF8523WatchdogTimeoutUnity wdgTimeoutUnitySet;
uint8_t wdgTimeoutValueSet;
};

/**************************************************************************/
Expand Down
Loading