Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
5 changes: 3 additions & 2 deletions src/rp2_common/hardware_rtc/include/hardware/rtc.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void rtc_init(void);
* \param t Pointer to a \ref datetime_t structure contains time to set
* \return true if set, false if the passed in datetime was invalid.
*/
bool rtc_set_datetime(datetime_t *t);
bool rtc_set_datetime(const datetime_t *t);

/*! \brief Get the current time from the RTC
* \ingroup hardware_rtc
Expand All @@ -71,8 +71,9 @@ bool rtc_running(void);
*
* \param t Pointer to a \ref datetime_t structure containing a time in the future to fire the alarm. Any values set to -1 will not be matched on.
* \param user_callback pointer to a \ref rtc_callback_t to call when the alarm fires
* \return false if parameters aren't valid
*/
void rtc_set_alarm(datetime_t *t, rtc_callback_t user_callback);
bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback);

/*! \brief Enable the RTC alarm (if inactive)
* \ingroup hardware_rtc
Expand Down
96 changes: 56 additions & 40 deletions src/rp2_common/hardware_rtc/rtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@

// Set this when setting an alarm
static rtc_callback_t _callback = NULL;
static bool _alarm_repeats = false;

typedef enum {
NO_REPEAT = 0,
CONTINUOUS_REPEAT = 1,
CONTINUOUS_REPEAT_ON_SEC = 2,
} repeat_type;

static repeat_type _alarm_repeats = NO_REPEAT;

bool rtc_running(void) {
return (rtc_hw->ctrl & RTC_CTRL_RTC_ACTIVE_BITS);
Expand All @@ -39,7 +46,7 @@ void rtc_init(void) {
rtc_hw->clkdiv_m1 = rtc_freq;
}

static bool valid_datetime(datetime_t *t) {
static bool valid_datetime(const datetime_t *t) {
// Valid ranges taken from RTC doc. Note when setting an RTC alarm
// these values are allowed to be -1 to say "don't match this value"
if (!(t->year >= 0 && t->year <= 4095)) return false;
Expand All @@ -52,7 +59,7 @@ static bool valid_datetime(datetime_t *t) {
return true;
}

bool rtc_set_datetime(datetime_t *t) {
bool rtc_set_datetime(const datetime_t *t) {
if (!valid_datetime(t)) {
return false;
}
Expand Down Expand Up @@ -114,6 +121,14 @@ void rtc_enable_alarm(void) {
}
}

void rtc_disable_alarm(void) {
// Disable matching and wait for it to stop being active
hw_clear_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_MATCH_ENA_BITS);
while (rtc_hw->irq_setup_0 & RTC_IRQ_SETUP_0_MATCH_ACTIVE_BITS) {
tight_loop_contents();
}
}

static void rtc_irq_handler(void) {
// Always disable the alarm to clear the current IRQ.
// Even if it is a repeatable alarm, we don't want it to keep firing.
Expand All @@ -122,6 +137,11 @@ static void rtc_irq_handler(void) {

if (_alarm_repeats) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should all the if (_alarm_repeats) checks actually be if (_alarm_repeats != NO_REPEAT) ? 🤷

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a question of coding guidelines. Technically it's the same

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, that's a Graham-decision then 🙂

// If it is a repeatable alarm, re enable the alarm.
if(_alarm_repeats == CONTINUOUS_REPEAT_ON_SEC) {
datetime_t t;
rtc_get_datetime(&t);
rtc_hw->irq_setup_1 = RTC_IRQ_SETUP_1_SEC_ENA_BITS | ((((uint)t.sec + 1) % 60) << RTC_IRQ_SETUP_1_SEC_LSB);
}
rtc_enable_alarm();
}

Expand All @@ -131,43 +151,46 @@ static void rtc_irq_handler(void) {
}
}

static bool rtc_alarm_repeats(datetime_t *t) {
// If any value is set to -1 then we don't match on that value
// hence the alarm will eventually repeat
if (t->year < 0) return true;
if (t->month < 0) return true;
if (t->day < 0) return true;
if (t->dotw < 0) return true;
if (t->hour < 0) return true;
if (t->min < 0) return true;
if (t->sec < 0) return true;
return false;
// return != 0 on repeat, -1 on repeat every second
static int8_t rtc_alarm_repeats(const datetime_t *t) {
// If any value is set to -1 then we don't match on that value
// hence the alarm will eventually repeat
if((t->year & t->month & t->day & t->dotw & t->hour & t->min & t->sec) < 0) return CONTINUOUS_REPEAT_ON_SEC;

return (t->year < 0 || t->month < 0 || t->day < 0 || t->dotw < 0
|| t->hour < 0 || t->min < 0 || t->sec < 0)
? CONTINUOUS_REPEAT : NO_REPEAT;
}

void rtc_set_alarm(datetime_t *t, rtc_callback_t user_callback) {
bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback) {
rtc_disable_alarm();

// Only add to setup if it isn't -1
rtc_hw->irq_setup_0 = ((t->year < 0) ? 0 : (((uint)t->year) << RTC_IRQ_SETUP_0_YEAR_LSB )) |
((t->month < 0) ? 0 : (((uint)t->month) << RTC_IRQ_SETUP_0_MONTH_LSB)) |
((t->day < 0) ? 0 : (((uint)t->day) << RTC_IRQ_SETUP_0_DAY_LSB ));
rtc_hw->irq_setup_1 = ((t->dotw < 0) ? 0 : (((uint)t->dotw) << RTC_IRQ_SETUP_1_DOTW_LSB)) |
((t->hour < 0) ? 0 : (((uint)t->hour) << RTC_IRQ_SETUP_1_HOUR_LSB)) |
((t->min < 0) ? 0 : (((uint)t->min) << RTC_IRQ_SETUP_1_MIN_LSB )) |
((t->sec < 0) ? 0 : (((uint)t->sec) << RTC_IRQ_SETUP_1_SEC_LSB ));

// Set the match enable bits for things we care about
if (t->year >= 0) hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_YEAR_ENA_BITS);
if (t->month >= 0) hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_MONTH_ENA_BITS);
if (t->day >= 0) hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_DAY_ENA_BITS);
if (t->dotw >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_DOTW_ENA_BITS);
if (t->hour >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_HOUR_ENA_BITS);
if (t->min >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_MIN_ENA_BITS);
if (t->sec >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_SEC_ENA_BITS);
uint32_t s0 = 0, s1 = 0;

// Does it repeat? I.e. do we not match on any of the bits
_alarm_repeats = rtc_alarm_repeats(t);

if( (!valid_datetime(t) && !_alarm_repeats) || !user_callback) // none of the parameters is valid
return false;

// Set the match enable bits for things we care about
if (t->year >= 0) s0 |= RTC_IRQ_SETUP_0_YEAR_ENA_BITS | (((uint)t->year) << RTC_IRQ_SETUP_0_YEAR_LSB );
if (t->month >= 1) s0 |= RTC_IRQ_SETUP_0_MONTH_ENA_BITS | (((uint)t->month) << RTC_IRQ_SETUP_0_MONTH_LSB);
if (t->day >= 1) s0 |= RTC_IRQ_SETUP_0_DAY_ENA_BITS | (((uint)t->day) << RTC_IRQ_SETUP_0_DAY_LSB );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if changing the month and day checks to 1 instead of 0 is wanted here? 🤷

Copy link
Contributor Author

@Reneg973 Reneg973 May 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is a strange inconsistency in the original code. The RTC reports months from 1-12, but the input allowed a month of 0. If I assume the RTC follows the KISS principle, input and output will have the same ranges. Anyway I am not 100% sure

if (t->dotw >= 0) s1 |= RTC_IRQ_SETUP_1_DOTW_ENA_BITS | (((uint)t->dotw) << RTC_IRQ_SETUP_1_DOTW_LSB);
if (t->hour >= 0) s1 |= RTC_IRQ_SETUP_1_HOUR_ENA_BITS | (((uint)t->hour) << RTC_IRQ_SETUP_1_HOUR_LSB);
if (t->min >= 0) s1 |= RTC_IRQ_SETUP_1_MIN_ENA_BITS | (((uint)t->min) << RTC_IRQ_SETUP_1_MIN_LSB);
if (t->sec >= 0) s1 |= RTC_IRQ_SETUP_1_SEC_ENA_BITS | (((uint)t->sec) << RTC_IRQ_SETUP_1_SEC_LSB);
else if (_alarm_repeats == CONTINUOUS_REPEAT_ON_SEC) {
// repeatable every second! All entries are -1
datetime_t new_dt;
rtc_get_datetime(&new_dt);
s1 = RTC_IRQ_SETUP_1_SEC_ENA_BITS | ((((uint)new_dt.sec + 1) % 60) << RTC_IRQ_SETUP_1_SEC_LSB);
}

rtc_hw->irq_setup_0 = s0;
rtc_hw->irq_setup_1 = s1;

// Store function pointer we can call later
_callback = user_callback;

Expand All @@ -180,12 +203,5 @@ void rtc_set_alarm(datetime_t *t, rtc_callback_t user_callback) {
irq_set_enabled(RTC_IRQ, true);

rtc_enable_alarm();
}

void rtc_disable_alarm(void) {
// Disable matching and wait for it to stop being active
hw_clear_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_MATCH_ENA_BITS);
while(rtc_hw->irq_setup_0 & RTC_IRQ_SETUP_0_MATCH_ACTIVE_BITS) {
tight_loop_contents();
}
return true;
}