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 PCF8523 alarm functionality #264

Open
wants to merge 3 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
73 changes: 73 additions & 0 deletions examples/pcf8523Alarm/pcf8523Alarm.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "RTClib.h"

// Change this to reflect the interrupt pin you would like to use
#define INTERRUPT_PIN 3

RTC_PCF8523 rtc;

volatile bool alarm_triggered = false;

void setup() {
Serial.begin(57600);

// Ensure RTC initializes
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1)
delay(10);
}

doAlarmExample();
}

void loop() {
if (alarm_triggered) {
alarm_triggered = false;
rtc.disableAlarm();
Serial.println("Alarm triggered.\n");
doAlarmExample();
} else {
Serial.println("Alarm not triggered. The time is " + rtc.now().timestamp());
delay(1000);
}
}

/*
* Sets alarm one minute later.
*/
void doAlarmExample() {
// Make alarm trigger one minute after current time, but not accounting for
// seconds as the PCF8523 alarm doesn't have an alarm seconds register. So for
// example if current_time is 12:00:50, and alarm_time is 12:01:50, it will
// trigger at the exact minute, e.g. 12:01:00, because the PCF8523 hardware
// does not allow for seconds to be set on the alarm and ignores them.
DateTime current_time = rtc.now();
TimeSpan alarm_offset(0, 0, 1, 0); // One minute
DateTime alarm_time(current_time + alarm_offset);

// Good to clear other timers and such.
rtc.deconfigureAllTimers();

rtc.enableAlarm(alarm_time, PCF8523_AlarmDate);

// Print the current time in ISO8601 format.
Serial.println(String("Current Time: ") + current_time.timestamp());
Serial.flush();

// Print when the alarm should trigger. Using a custom ISO8601 format
// because as stated above, PCF8523 can only trigger on the :00th second
// so seconds don't matter.
char alarm_time_buf[] = "YYYY-MM-DDThh:mm:00";
Serial.println(String("Alarm Time: ") + alarm_time.toString(alarm_time_buf));
Serial.flush();

// Set the interrupt.
pinMode(INTERRUPT_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), alarmISR, LOW);
}

void alarmISR() {
alarm_triggered = true;
detachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN));
}
84 changes: 78 additions & 6 deletions src/RTC_PCF8523.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
#include "RTClib.h"

#define PCF8523_ADDRESS 0x68 ///< I2C address for PCF8523
#define PCF8523_CLKOUTCONTROL 0x0F ///< Timer and CLKOUT control register
#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_ADDRESS 0x68 ///< I2C address for PCF8523
#define PCF8523_CLKOUTCONTROL 0x0F ///< Timer and CLKOUT control register
#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_ALARM_REG 0x0A ///< Alarm register
#define PCF8523_ALARM_WEEKDAY_REG 0x0D ///< Alarm Weekday register
#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
#define PCF8523_STATUSREG 0x03 ///< Status register

#define PCF8523_ALARM_MINUTE 0x01
#define PCF8523_ALARM_HOUR 0x02
#define PCF8523_ALARM_DATE 0x04
#define PCF8523_ALARM_WEEKDAY 0x08

/**************************************************************************/
/*!
@brief Start I2C for the PCF8523 and test succesful connection
Expand Down Expand Up @@ -64,7 +71,7 @@ void RTC_PCF8523::adjust(const DateTime &dt) {
bin2bcd(dt.minute()),
bin2bcd(dt.hour()),
bin2bcd(dt.day()),
bin2bcd(0), // skip weekdays
bin2bcd(dt.dayOfTheWeek()),
bin2bcd(dt.month()),
bin2bcd(dt.year() - 2000U)};
i2c_dev->write(buffer, 8);
Expand Down Expand Up @@ -168,6 +175,71 @@ void RTC_PCF8523::disableSecondTimer() {
read_register(PCF8523_CONTROL_1) & ~(1 << 2));
}

/**************************************************************************/
/*!
@brief Enables alarm timer with current date/time and alarm mode.
@details The alarm will trigger at the specified date and time. The
INT/SQW pin will be pulled low when the specified time is reached. The
CLKOUT square wave will be disabled during the operation of the timer as
they interrupt on the same pin. Alarm modes are offered in the
Pcf8523AlarmMode enum.

If the alarm mode is set to PCF8523_AlarmWeekday, it will repeat on the
weekday of the original date/time setting.
@param dt Date/Time for the alarm.
@param alarmMode Sets the alarm mode from the Pcf8523AlarmMode enum.
*/
/**************************************************************************/
void RTC_PCF8523::enableAlarm(const DateTime &dt,
const Pcf8523AlarmMode alarmMode) {
// Disable square wave generation on SQW/INT pin, otherwise interrupt
// will trigger.
writeSqwPinMode(PCF8523_OFF);
// Clear alarm interrupt flag
write_register(PCF8523_CONTROL_2,
~(1 << 3) & read_register(PCF8523_CONTROL_2));

// Enable alarm interrupts
write_register(PCF8523_CONTROL_1,
(1 << 1) | read_register(PCF8523_CONTROL_1));

// Converts number to BCD then sets enable bit (bit 7) to 0 or 1
// based on the alarm mode.
uint8_t alarmMinute = (uint8_t)((bin2bcd(dt.minute()) | (1 << 7)) &
~((alarmMode & PCF8523_ALARM_MINUTE) << 7));
uint8_t alarmHour = (uint8_t)((bin2bcd(dt.hour()) | (1 << 7)) &
~((alarmMode & PCF8523_ALARM_HOUR) << 6));
uint8_t alarmDate = (uint8_t)((bin2bcd(dt.day()) | (1 << 7)) &
~((alarmMode & PCF8523_ALARM_DATE) << 5));
uint8_t alarmWeekday = (uint8_t)((bin2bcd(dt.dayOfTheWeek())) | (1 << 7)) &
~((alarmMode & PCF8523_ALARM_WEEKDAY) << 4);

uint8_t buffer[5] = {PCF8523_ALARM_REG, alarmMinute, alarmHour, alarmDate,
alarmWeekday};

i2c_dev->write(buffer, 5);
}

/**************************************************************************/
/*!
@brief Disables alarm timer and resets alarm interrupt.
@details Turns off alarm timer in Control Register 2 and clears
interrupt flag.

Note: enableAlarmTimer() disables CLKOUT square wave feature. CLKOUT
square wave remains disabled after using this function. Use
writeSqwPinMode() to reactivate if required.
*/
/**************************************************************************/
void RTC_PCF8523::disableAlarm() {
// Clear alarm interrupt bit
write_register(PCF8523_CONTROL_2,
~(1 << 3) & read_register(PCF8523_CONTROL_2));
// Disable alarm activation flag
write_register(PCF8523_CONTROL_1,
~(1 << 1) & read_register(PCF8523_CONTROL_1));
}

/**************************************************************************/
/*!
@brief Enable the Countdown Timer Interrupt on the PCF8523.
Expand Down
14 changes: 14 additions & 0 deletions src/RTClib.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ enum Pcf8523OffsetMode {
PCF8523_OneMinute = 0x80 /**< Offset made every minute */
};

/** PCF8523 alarm modes */
enum Pcf8523AlarmMode {
PCF8523_AlarmMinute = 0x01, /**< Alarm when minutes match */

PCF8523_AlarmHour = 0x03, /**< Alarm when hours and minutes match */

PCF8523_AlarmDate = 0x07, /**< Alarm when date (day of month), hours
and minutes match */
PCF8523_AlarmWeekday = 0x0B, /**< Alarm when day (day of week), hours
and minutes match */
};

/** PCF8563 CLKOUT pin mode settings */
enum Pcf8563SqwPinMode {
PCF8563_SquareWaveOFF = 0x00, /**< Off */
Expand Down Expand Up @@ -413,6 +425,8 @@ class RTC_PCF8523 : RTC_I2C {
void writeSqwPinMode(Pcf8523SqwPinMode mode);
void enableSecondTimer(void);
void disableSecondTimer(void);
void enableAlarm(const DateTime &dt, const Pcf8523AlarmMode alarmMode);
void disableAlarm();
void enableCountdownTimer(PCF8523TimerClockFreq clkFreq, uint8_t numPeriods,
uint8_t lowPulseWidth);
void enableCountdownTimer(PCF8523TimerClockFreq clkFreq, uint8_t numPeriods);
Expand Down