From b3fa3e626765fb3b53ade86e54838d352063054c Mon Sep 17 00:00:00 2001 From: Rene Greiner Date: Sat, 1 May 2021 20:31:02 +0200 Subject: [PATCH 01/10] - better const correctness - reorder functions to context - check parameters for alarm --- .../hardware_rtc/include/hardware/rtc.h | 5 +-- src/rp2_common/hardware_rtc/rtc.c | 31 ++++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/rp2_common/hardware_rtc/include/hardware/rtc.h b/src/rp2_common/hardware_rtc/include/hardware/rtc.h index 8757e36e0..5f8b7a42f 100644 --- a/src/rp2_common/hardware_rtc/include/hardware/rtc.h +++ b/src/rp2_common/hardware_rtc/include/hardware/rtc.h @@ -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 @@ -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 diff --git a/src/rp2_common/hardware_rtc/rtc.c b/src/rp2_common/hardware_rtc/rtc.c index 5429acd4b..9cdb2a6ae 100644 --- a/src/rp2_common/hardware_rtc/rtc.c +++ b/src/rp2_common/hardware_rtc/rtc.c @@ -39,7 +39,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; @@ -52,7 +52,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; } @@ -114,6 +114,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. @@ -131,7 +139,7 @@ static void rtc_irq_handler(void) { } } -static bool rtc_alarm_repeats(datetime_t *t) { +static bool 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 < 0) return true; @@ -144,9 +152,15 @@ static bool rtc_alarm_repeats(datetime_t *t) { return false; } -void rtc_set_alarm(datetime_t *t, rtc_callback_t user_callback) { +bool rtc_set_alarm(datetime_t *t, rtc_callback_t user_callback) { rtc_disable_alarm(); + // 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; + // 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)) | @@ -180,12 +194,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; } From 61309a6a656475ab34ccd7e15d4770e6643850f4 Mon Sep 17 00:00:00 2001 From: Rene Greiner Date: Sat, 1 May 2021 20:59:18 +0200 Subject: [PATCH 02/10] new feature, alarm on every second --- src/rp2_common/hardware_rtc/rtc.c | 69 +++++++++++++++++-------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/rp2_common/hardware_rtc/rtc.c b/src/rp2_common/hardware_rtc/rtc.c index 9cdb2a6ae..d2b18ee2c 100644 --- a/src/rp2_common/hardware_rtc/rtc.c +++ b/src/rp2_common/hardware_rtc/rtc.c @@ -13,7 +13,7 @@ // Set this when setting an alarm static rtc_callback_t _callback = NULL; -static bool _alarm_repeats = false; +static int8_t _alarm_repeats = 0; bool rtc_running(void) { return (rtc_hw->ctrl & RTC_CTRL_RTC_ACTIVE_BITS); @@ -130,6 +130,11 @@ static void rtc_irq_handler(void) { if (_alarm_repeats) { // If it is a repeatable alarm, re enable the alarm. + if(_alarm_repeats == -1) { + datetime_t tNew; + rtc_get_datetime(&tNew); + rtc_hw->irq_setup_1 = RTC_IRQ_SETUP_1_SEC_ENA_BITS | ((((uint)tNew.sec + 1) % 60) << RTC_IRQ_SETUP_1_SEC_LSB); + } rtc_enable_alarm(); } @@ -139,48 +144,50 @@ static void rtc_irq_handler(void) { } } -static bool 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 < 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 - 1; + return ((t->year | t->month | t->day | t->dotw | t->hour | t->min | t->sec) < 0); } -bool 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(); + 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; - // 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); + 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 ); + 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 == -1) // repeatable every second! All entries are -1 + { + datetime_t tNew; + rtc_get_datetime(&tNew); + s1 = RTC_IRQ_SETUP_1_SEC_ENA_BITS | ((((uint)tNew.sec + 1) % 60) << RTC_IRQ_SETUP_1_SEC_LSB); + } - // Does it repeat? I.e. do we not match on any of the bits - _alarm_repeats = rtc_alarm_repeats(t); + rtc_hw->irq_setup_0 = s0; + rtc_hw->irq_setup_1 = s1; // Store function pointer we can call later _callback = user_callback; From f945ab8e2f8ef61faef3e01b87029ed3500fdf67 Mon Sep 17 00:00:00 2001 From: Rene Greiner Date: Sat, 1 May 2021 22:38:26 +0200 Subject: [PATCH 03/10] Update rtc.c - use enum for multi-state value - code formatting --- src/rp2_common/hardware_rtc/rtc.c | 58 ++++++++++++++++--------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/rp2_common/hardware_rtc/rtc.c b/src/rp2_common/hardware_rtc/rtc.c index d2b18ee2c..f5025ee1b 100644 --- a/src/rp2_common/hardware_rtc/rtc.c +++ b/src/rp2_common/hardware_rtc/rtc.c @@ -13,7 +13,14 @@ // Set this when setting an alarm static rtc_callback_t _callback = NULL; -static int8_t _alarm_repeats = 0; + +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); @@ -115,11 +122,11 @@ 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(); - } + // 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) { @@ -131,9 +138,9 @@ static void rtc_irq_handler(void) { if (_alarm_repeats) { // If it is a repeatable alarm, re enable the alarm. if(_alarm_repeats == -1) { - datetime_t tNew; - rtc_get_datetime(&tNew); - rtc_hw->irq_setup_1 = RTC_IRQ_SETUP_1_SEC_ENA_BITS | ((((uint)tNew.sec + 1) % 60) << RTC_IRQ_SETUP_1_SEC_LSB); + 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(); } @@ -148,9 +155,11 @@ static void rtc_irq_handler(void) { 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 - 1; - return ((t->year | t->month | t->day | t->dotw | t->hour | t->min | t->sec) < 0); + 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; } bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback) { @@ -165,22 +174,15 @@ bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback) { 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 ); - 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 == -1) // repeatable every second! All entries are -1 - { + 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 ); + 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 tNew; rtc_get_datetime(&tNew); s1 = RTC_IRQ_SETUP_1_SEC_ENA_BITS | ((((uint)tNew.sec + 1) % 60) << RTC_IRQ_SETUP_1_SEC_LSB); From 66ceabdcfd9cf859edb22c6158462e8d1c0d3dde Mon Sep 17 00:00:00 2001 From: Rene Greiner Date: Sat, 1 May 2021 22:43:40 +0200 Subject: [PATCH 04/10] removed hungarian rename tNew -> new_dt --- src/rp2_common/hardware_rtc/rtc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rp2_common/hardware_rtc/rtc.c b/src/rp2_common/hardware_rtc/rtc.c index f5025ee1b..352ee4ff6 100644 --- a/src/rp2_common/hardware_rtc/rtc.c +++ b/src/rp2_common/hardware_rtc/rtc.c @@ -183,9 +183,9 @@ bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback) { 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 tNew; - rtc_get_datetime(&tNew); - s1 = RTC_IRQ_SETUP_1_SEC_ENA_BITS | ((((uint)tNew.sec + 1) % 60) << RTC_IRQ_SETUP_1_SEC_LSB); + 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; From eddfce40b73d721034cc4e20aa17b26672022679 Mon Sep 17 00:00:00 2001 From: Rene Greiner Date: Sat, 1 May 2021 23:01:40 +0200 Subject: [PATCH 05/10] overseen one comparison -1 replaced with enum value --- src/rp2_common/hardware_rtc/rtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rp2_common/hardware_rtc/rtc.c b/src/rp2_common/hardware_rtc/rtc.c index 352ee4ff6..dff28c04c 100644 --- a/src/rp2_common/hardware_rtc/rtc.c +++ b/src/rp2_common/hardware_rtc/rtc.c @@ -137,7 +137,7 @@ static void rtc_irq_handler(void) { if (_alarm_repeats) { // If it is a repeatable alarm, re enable the alarm. - if(_alarm_repeats == -1) { + 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); From 3deda9855b2d6ef11a060a9c8edc6ab3d5570c2a Mon Sep 17 00:00:00 2001 From: Rene Greiner Date: Sun, 2 May 2021 12:16:15 +0200 Subject: [PATCH 06/10] added rtc_delete_alarm -with rtc_delete_alarm() not only the the alarm is disabled but also the according exclusive interrupt is removed and everything else cleaned up - renamed CONTINUOUS_REPEAT_ON_SEC to CONTINUOUS_REPEAT_EVERY_SEC -added some helper macros for RANGE_CHECK - added macro to modify irq_setup_1 with incremented seconds and wrap at 60 - removed _callback check in ISR because this is handled now in setup alarm function - increased readability by changing bitwise and to logical and --- .../hardware_rtc/include/hardware/rtc.h | 9 ++ src/rp2_common/hardware_rtc/rtc.c | 142 ++++++++++++------ 2 files changed, 103 insertions(+), 48 deletions(-) diff --git a/src/rp2_common/hardware_rtc/include/hardware/rtc.h b/src/rp2_common/hardware_rtc/include/hardware/rtc.h index 5f8b7a42f..faf3d820b 100644 --- a/src/rp2_common/hardware_rtc/include/hardware/rtc.h +++ b/src/rp2_common/hardware_rtc/include/hardware/rtc.h @@ -32,6 +32,10 @@ extern "C" { #endif +#ifndef PARAM_ASSERTIONS_ENABLED_RTC +#define PARAM_ASSERTIONS_ENABLED_RTC 0 +#endif + /*! Callback function type for RTC alarms * \ingroup hardware_rtc * @@ -85,6 +89,11 @@ void rtc_enable_alarm(void); */ void rtc_disable_alarm(void); +/*! \brief Deletes the alarm previously set with \see rtc_set_alarm + * \ingroup hardware_rtc + */ +void rtc_delete_alarm(void); + #ifdef __cplusplus } #endif diff --git a/src/rp2_common/hardware_rtc/rtc.c b/src/rp2_common/hardware_rtc/rtc.c index dff28c04c..85690ef86 100644 --- a/src/rp2_common/hardware_rtc/rtc.c +++ b/src/rp2_common/hardware_rtc/rtc.c @@ -11,13 +11,22 @@ #include "hardware/resets.h" #include "hardware/clocks.h" +#define RANGE_CHECK_YEAR(t) (t->year >= 0 && t->year <= 4095) +#define RANGE_CHECK_MONTH(t) (t->month >= 1 && t->month <= 12) +#define RANGE_CHECK_DAY(t) (t->day >= 1 && t->day <= 32) +#define RANGE_CHECK_DOTW(t) (t->dotw >= 0 && t->dotw <= 6) +#define RANGE_CHECK_HOUR(t) (t->hour >= 0 && t->hour <= 23) +#define RANGE_CHECK_MIN(t) (t->min >= 0 && t->min <= 59) +#define RANGE_CHECK_SEC(t) (t->sec >= 0 && t->sec <= 59) + + // Set this when setting an alarm static rtc_callback_t _callback = NULL; typedef enum { - NO_REPEAT = 0, - CONTINUOUS_REPEAT = 1, - CONTINUOUS_REPEAT_ON_SEC = 2, + NO_REPEAT = 0, + CONTINUOUS_REPEAT = 1, + CONTINUOUS_REPEAT_EVERY_SEC = 2, } repeat_type; static repeat_type _alarm_repeats = NO_REPEAT; @@ -46,21 +55,38 @@ void rtc_init(void) { rtc_hw->clkdiv_m1 = rtc_freq; } -static bool valid_datetime(const datetime_t *t) { +static bool is_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; - if (!(t->month >= 1 && t->month <= 12)) return false; - if (!(t->day >= 1 && t->day <= 31)) return false; - if (!(t->dotw >= 0 && t->dotw <= 6)) return false; - if (!(t->hour >= 0 && t->hour <= 23)) return false; - if (!(t->min >= 0 && t->min <= 59)) return false; - if (!(t->sec >= 0 && t->sec <= 59)) return false; - return true; + return RANGE_CHECK_YEAR(t) + && RANGE_CHECK_MONTH(t) + && RANGE_CHECK_DAY(t) + && RANGE_CHECK_DOTW(t) + && RANGE_CHECK_HOUR(t) + && RANGE_CHECK_MIN(t) + && RANGE_CHECK_SEC(t); +} + +// small helper without check for running rtc +static inline datetime_t _rtc_get_datetime(datetime_t *t) { + // Note: RTC_0 should be read before RTC_1 + uint32_t rtc_val = rtc_hw->rtc_0; + t->dotw = (rtc_val & RTC_RTC_0_DOTW_BITS) >> RTC_RTC_0_DOTW_LSB; + t->hour = (rtc_val & RTC_RTC_0_HOUR_BITS) >> RTC_RTC_0_HOUR_LSB; + t->min = (rtc_val & RTC_RTC_0_MIN_BITS) >> RTC_RTC_0_MIN_LSB; + t->sec = (rtc_val & RTC_RTC_0_SEC_BITS) >> RTC_RTC_0_SEC_LSB; + + rtc_val = rtc_hw->rtc_1; + t->year = (rtc_val & RTC_RTC_1_YEAR_BITS) >> RTC_RTC_1_YEAR_LSB; + t->month = (rtc_val & RTC_RTC_1_MONTH_BITS) >> RTC_RTC_1_MONTH_LSB; + t->day = (rtc_val & RTC_RTC_1_DAY_BITS) >> RTC_RTC_1_DAY_LSB; } bool rtc_set_datetime(const datetime_t *t) { - if (!valid_datetime(t)) { + bool check_params = is_valid_datetime(t); + valid_params_if(RTC, check_params); + + if (!check_params) { return false; } @@ -98,18 +124,7 @@ bool rtc_get_datetime(datetime_t *t) { return false; } - // Note: RTC_0 should be read before RTC_1 - uint32_t rtc_0 = rtc_hw->rtc_0; - uint32_t rtc_1 = rtc_hw->rtc_1; - - t->dotw = (rtc_0 & RTC_RTC_0_DOTW_BITS ) >> RTC_RTC_0_DOTW_LSB; - t->hour = (rtc_0 & RTC_RTC_0_HOUR_BITS ) >> RTC_RTC_0_HOUR_LSB; - t->min = (rtc_0 & RTC_RTC_0_MIN_BITS ) >> RTC_RTC_0_MIN_LSB; - t->sec = (rtc_0 & RTC_RTC_0_SEC_BITS ) >> RTC_RTC_0_SEC_LSB; - t->year = (rtc_1 & RTC_RTC_1_YEAR_BITS ) >> RTC_RTC_1_YEAR_LSB; - t->month = (rtc_1 & RTC_RTC_1_MONTH_BITS) >> RTC_RTC_1_MONTH_LSB; - t->day = (rtc_1 & RTC_RTC_1_DAY_BITS ) >> RTC_RTC_1_DAY_LSB; - + _rtc_get_datetime(t); return true; } @@ -129,33 +144,37 @@ void rtc_disable_alarm(void) { } } -static void rtc_irq_handler(void) { +#define ADD_AND_ENABLE_REPEATABLE_SECOND(s) (RTC_IRQ_SETUP_1_SEC_ENA_BITS | ((((uint)s + 1) % 60) << RTC_IRQ_SETUP_1_SEC_LSB)) + +static void __no_inline_not_in_flash_func(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. // If it matches on a second it can keep firing for that second. rtc_disable_alarm(); if (_alarm_repeats) { - // If it is a repeatable alarm, re enable the alarm. - if(_alarm_repeats == CONTINUOUS_REPEAT_ON_SEC) { + if(_alarm_repeats == CONTINUOUS_REPEAT_EVERY_SEC) { + // we need to modify the sec entry with the next valid RTC change and store it into irq_setup_1 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_hw->irq_setup_1 = ADD_AND_ENABLE_REPEATABLE_SECOND(t.sec); } - rtc_enable_alarm(); } // Call user callback function - if (_callback) { - _callback(); + _callback(); + + // If it is a repeatable alarm, re enable the alarm. + if(_alarm_repeats) { + rtc_enable_alarm(); } } -// return != 0 on repeat, -1 on repeat every second -static int8_t rtc_alarm_repeats(const datetime_t *t) { +static repeat_type 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; + if (t->year < 0 && t->month < 0 && t->day < 0 && t->dotw < 0 + && t->hour < 0 && t->min < 0 && t->sec < 0) return CONTINUOUS_REPEAT_EVERY_SEC; return (t->year < 0 || t->month < 0 || t->day < 0 || t->dotw < 0 || t->hour < 0 || t->min < 0 || t->sec < 0) @@ -163,6 +182,9 @@ static int8_t rtc_alarm_repeats(const datetime_t *t) { } bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback) { + if (!rtc_running()) + return false; + rtc_disable_alarm(); uint32_t s0 = 0, s1 = 0; @@ -170,22 +192,27 @@ bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback) { // 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 + bool check_params = (is_valid_datetime(t) || _alarm_repeats != NO_REPEAT) && user_callback; + valid_params_if(RTC, check_params); + if(!check_params) // 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 ); - 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); + if (RANGE_CHECK_YEAR(t)) s0 |= RTC_IRQ_SETUP_0_YEAR_ENA_BITS | (((uint)t->year) << RTC_IRQ_SETUP_0_YEAR_LSB ); + if (RANGE_CHECK_MONTH(t)) s0 |= RTC_IRQ_SETUP_0_MONTH_ENA_BITS | (((uint)t->month) << RTC_IRQ_SETUP_0_MONTH_LSB); + if (RANGE_CHECK_DAY(t)) s0 |= RTC_IRQ_SETUP_0_DAY_ENA_BITS | (((uint)t->day) << RTC_IRQ_SETUP_0_DAY_LSB ); + if (RANGE_CHECK_DOTW(t)) s1 |= RTC_IRQ_SETUP_1_DOTW_ENA_BITS | (((uint)t->dotw) << RTC_IRQ_SETUP_1_DOTW_LSB); + if (RANGE_CHECK_HOUR(t)) s1 |= RTC_IRQ_SETUP_1_HOUR_ENA_BITS | (((uint)t->hour) << RTC_IRQ_SETUP_1_HOUR_LSB); + if (RANGE_CHECK_MIN(t)) s1 |= RTC_IRQ_SETUP_1_MIN_ENA_BITS | (((uint)t->min) << RTC_IRQ_SETUP_1_MIN_LSB); + if (RANGE_CHECK_SEC(t)) s1 |= RTC_IRQ_SETUP_1_SEC_ENA_BITS | (((uint)t->sec) << RTC_IRQ_SETUP_1_SEC_LSB); + else if (_alarm_repeats == CONTINUOUS_REPEAT_EVERY_SEC) { + // repeatable every second! All entries are -1 + datetime_t new_dt; + _rtc_get_datetime(&new_dt); + s1 = ADD_AND_ENABLE_REPEATABLE_SECOND(new_dt.sec); + } + else { + return false; // out of range datetime_t input } rtc_hw->irq_setup_0 = s0; @@ -205,3 +232,22 @@ bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback) { rtc_enable_alarm(); return true; } + +void rtc_delete_alarm(void) +{ + if (_callback == NULL) { + return; // rtc_set_alarm was not called or not successful + } + + // first disable the alarm + rtc_disable_alarm(); + + // don't receive interrupts anymore + rtc_hw->inte = RTC_INTE_RESET; + + // disable IRQ and remove handler + irq_set_enabled(RTC_IRQ, false); + irq_remove_handler(RTC_IRQ, _callback); + + _callback = NULL; +} From 82a468178542131ca79530b859be28d26097fd0c Mon Sep 17 00:00:00 2001 From: Rene Greiner Date: Sun, 2 May 2021 12:26:14 +0200 Subject: [PATCH 07/10] check (_alarm_repeats == CONTINUOUS_REPEAT_EVERY_SEC) first it is superfluous to check all other conditions if _alarm_repeats is already set to every second --- src/rp2_common/hardware_rtc/rtc.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/rp2_common/hardware_rtc/rtc.c b/src/rp2_common/hardware_rtc/rtc.c index 85690ef86..05ed52efd 100644 --- a/src/rp2_common/hardware_rtc/rtc.c +++ b/src/rp2_common/hardware_rtc/rtc.c @@ -198,21 +198,22 @@ bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback) { return false; // Set the match enable bits for things we care about - if (RANGE_CHECK_YEAR(t)) s0 |= RTC_IRQ_SETUP_0_YEAR_ENA_BITS | (((uint)t->year) << RTC_IRQ_SETUP_0_YEAR_LSB ); - if (RANGE_CHECK_MONTH(t)) s0 |= RTC_IRQ_SETUP_0_MONTH_ENA_BITS | (((uint)t->month) << RTC_IRQ_SETUP_0_MONTH_LSB); - if (RANGE_CHECK_DAY(t)) s0 |= RTC_IRQ_SETUP_0_DAY_ENA_BITS | (((uint)t->day) << RTC_IRQ_SETUP_0_DAY_LSB ); - if (RANGE_CHECK_DOTW(t)) s1 |= RTC_IRQ_SETUP_1_DOTW_ENA_BITS | (((uint)t->dotw) << RTC_IRQ_SETUP_1_DOTW_LSB); - if (RANGE_CHECK_HOUR(t)) s1 |= RTC_IRQ_SETUP_1_HOUR_ENA_BITS | (((uint)t->hour) << RTC_IRQ_SETUP_1_HOUR_LSB); - if (RANGE_CHECK_MIN(t)) s1 |= RTC_IRQ_SETUP_1_MIN_ENA_BITS | (((uint)t->min) << RTC_IRQ_SETUP_1_MIN_LSB); - if (RANGE_CHECK_SEC(t)) s1 |= RTC_IRQ_SETUP_1_SEC_ENA_BITS | (((uint)t->sec) << RTC_IRQ_SETUP_1_SEC_LSB); - else if (_alarm_repeats == CONTINUOUS_REPEAT_EVERY_SEC) { - // repeatable every second! All entries are -1 - datetime_t new_dt; - _rtc_get_datetime(&new_dt); - s1 = ADD_AND_ENABLE_REPEATABLE_SECOND(new_dt.sec); + if(_alarm_repeats == CONTINUOUS_REPEAT_EVERY_SEC) { + // repeatable every second! All entries are -1 + datetime_t new_dt; + _rtc_get_datetime(&new_dt); + s1 = ADD_AND_ENABLE_REPEATABLE_SECOND(new_dt.sec); } else { - return false; // out of range datetime_t input + if (RANGE_CHECK_YEAR(t)) s0 |= RTC_IRQ_SETUP_0_YEAR_ENA_BITS | (((uint)t->year) << RTC_IRQ_SETUP_0_YEAR_LSB); + if (RANGE_CHECK_MONTH(t)) s0 |= RTC_IRQ_SETUP_0_MONTH_ENA_BITS | (((uint)t->month) << RTC_IRQ_SETUP_0_MONTH_LSB); + if (RANGE_CHECK_DAY(t)) s0 |= RTC_IRQ_SETUP_0_DAY_ENA_BITS | (((uint)t->day) << RTC_IRQ_SETUP_0_DAY_LSB); + if (RANGE_CHECK_DOTW(t)) s1 |= RTC_IRQ_SETUP_1_DOTW_ENA_BITS | (((uint)t->dotw) << RTC_IRQ_SETUP_1_DOTW_LSB); + if (RANGE_CHECK_HOUR(t)) s1 |= RTC_IRQ_SETUP_1_HOUR_ENA_BITS | (((uint)t->hour) << RTC_IRQ_SETUP_1_HOUR_LSB); + if (RANGE_CHECK_MIN(t)) s1 |= RTC_IRQ_SETUP_1_MIN_ENA_BITS | (((uint)t->min) << RTC_IRQ_SETUP_1_MIN_LSB); + if (RANGE_CHECK_SEC(t)) s1 |= RTC_IRQ_SETUP_1_SEC_ENA_BITS | (((uint)t->sec) << RTC_IRQ_SETUP_1_SEC_LSB); + + if(!s0 && !s1) return false; // out of range datetime_t input } rtc_hw->irq_setup_0 = s0; From 27cb80efc5ccd1a0cd3aa87d23eae6dbc926f323 Mon Sep 17 00:00:00 2001 From: Rene Greiner Date: Sun, 2 May 2021 12:45:14 +0200 Subject: [PATCH 08/10] improved with seconds increment given as negative value now the trigger interval can be given with a negative sec value. Also updated docu for rtc_set_alarm() --- src/rp2_common/hardware_rtc/include/hardware/rtc.h | 3 ++- src/rp2_common/hardware_rtc/rtc.c | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/rp2_common/hardware_rtc/include/hardware/rtc.h b/src/rp2_common/hardware_rtc/include/hardware/rtc.h index faf3d820b..33d208d8e 100644 --- a/src/rp2_common/hardware_rtc/include/hardware/rtc.h +++ b/src/rp2_common/hardware_rtc/include/hardware/rtc.h @@ -73,7 +73,8 @@ bool rtc_running(void); /*! \brief Set a time in the future for the RTC to call a user provided callback * \ingroup hardware_rtc * - * \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 t Pointer to a \ref datetime_t structure containing a time in the future to fire the alarm. Any values set to a negative value will not be matched on. + * With one exception: If all values are negative, it will be matched on every step of abs(datetime_t::sec). * \param user_callback pointer to a \ref rtc_callback_t to call when the alarm fires * \return false if parameters aren't valid */ diff --git a/src/rp2_common/hardware_rtc/rtc.c b/src/rp2_common/hardware_rtc/rtc.c index 05ed52efd..3f9b9673c 100644 --- a/src/rp2_common/hardware_rtc/rtc.c +++ b/src/rp2_common/hardware_rtc/rtc.c @@ -22,6 +22,9 @@ // Set this when setting an alarm static rtc_callback_t _callback = NULL; +static uint8_t _seconds_increment = 1; + +#define ADD_AND_ENABLE_REPEATABLE_SECOND(s) (RTC_IRQ_SETUP_1_SEC_ENA_BITS | ((((uint)s + _seconds_increment) % 60) << RTC_IRQ_SETUP_1_SEC_LSB)) typedef enum { NO_REPEAT = 0, @@ -144,8 +147,6 @@ void rtc_disable_alarm(void) { } } -#define ADD_AND_ENABLE_REPEATABLE_SECOND(s) (RTC_IRQ_SETUP_1_SEC_ENA_BITS | ((((uint)s + 1) % 60) << RTC_IRQ_SETUP_1_SEC_LSB)) - static void __no_inline_not_in_flash_func(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. @@ -202,6 +203,7 @@ bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback) { // repeatable every second! All entries are -1 datetime_t new_dt; _rtc_get_datetime(&new_dt); + _seconds_increment = (-t->sec) % 60; s1 = ADD_AND_ENABLE_REPEATABLE_SECOND(new_dt.sec); } else { From ecedcbed86c80553b28bea2270d3a491d404f3f5 Mon Sep 17 00:00:00 2001 From: Rene Greiner Date: Sun, 2 May 2021 16:50:28 +0200 Subject: [PATCH 09/10] removed user_callback check the function rtc_set_alarm() is used to wakeup the system. There a user_callback might not exist. Therefore removed the NULL comparison => it must be checked in ISR. - also fixed RANGE_CHECK_DAY - added a PICO_CONFIG entry --- src/rp2_common/hardware_rtc/include/hardware/rtc.h | 1 + src/rp2_common/hardware_rtc/rtc.c | 11 ++++------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/rp2_common/hardware_rtc/include/hardware/rtc.h b/src/rp2_common/hardware_rtc/include/hardware/rtc.h index 33d208d8e..4a335384e 100644 --- a/src/rp2_common/hardware_rtc/include/hardware/rtc.h +++ b/src/rp2_common/hardware_rtc/include/hardware/rtc.h @@ -32,6 +32,7 @@ extern "C" { #endif +// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_RTC, Enable/disable assertions in the RTC module, type=bool, default=0, group=hardware_rtc #ifndef PARAM_ASSERTIONS_ENABLED_RTC #define PARAM_ASSERTIONS_ENABLED_RTC 0 #endif diff --git a/src/rp2_common/hardware_rtc/rtc.c b/src/rp2_common/hardware_rtc/rtc.c index 3f9b9673c..fccea944e 100644 --- a/src/rp2_common/hardware_rtc/rtc.c +++ b/src/rp2_common/hardware_rtc/rtc.c @@ -13,7 +13,7 @@ #define RANGE_CHECK_YEAR(t) (t->year >= 0 && t->year <= 4095) #define RANGE_CHECK_MONTH(t) (t->month >= 1 && t->month <= 12) -#define RANGE_CHECK_DAY(t) (t->day >= 1 && t->day <= 32) +#define RANGE_CHECK_DAY(t) (t->day >= 1 && t->day <= 31) #define RANGE_CHECK_DOTW(t) (t->dotw >= 0 && t->dotw <= 6) #define RANGE_CHECK_HOUR(t) (t->hour >= 0 && t->hour <= 23) #define RANGE_CHECK_MIN(t) (t->min >= 0 && t->min <= 59) @@ -163,7 +163,8 @@ static void __no_inline_not_in_flash_func(rtc_irq_handler)(void) { } // Call user callback function - _callback(); + if (_callback) + _callback(); // If it is a repeatable alarm, re enable the alarm. if(_alarm_repeats) { @@ -193,7 +194,7 @@ bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback) { // Does it repeat? I.e. do we not match on any of the bits _alarm_repeats = rtc_alarm_repeats(t); - bool check_params = (is_valid_datetime(t) || _alarm_repeats != NO_REPEAT) && user_callback; + bool check_params = (is_valid_datetime(t) || _alarm_repeats != NO_REPEAT); valid_params_if(RTC, check_params); if(!check_params) // none of the parameters is valid return false; @@ -238,10 +239,6 @@ bool rtc_set_alarm(const datetime_t *t, rtc_callback_t user_callback) { void rtc_delete_alarm(void) { - if (_callback == NULL) { - return; // rtc_set_alarm was not called or not successful - } - // first disable the alarm rtc_disable_alarm(); From 1a6884b2d845104c22883d5eddcd72249c88baaf Mon Sep 17 00:00:00 2001 From: Rene Greiner Date: Sun, 2 May 2021 18:21:58 +0200 Subject: [PATCH 10/10] fixed remove the correct IRQ handler --- src/rp2_common/hardware_rtc/rtc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rp2_common/hardware_rtc/rtc.c b/src/rp2_common/hardware_rtc/rtc.c index fccea944e..0e6a3ac9c 100644 --- a/src/rp2_common/hardware_rtc/rtc.c +++ b/src/rp2_common/hardware_rtc/rtc.c @@ -246,8 +246,7 @@ void rtc_delete_alarm(void) rtc_hw->inte = RTC_INTE_RESET; // disable IRQ and remove handler - irq_set_enabled(RTC_IRQ, false); - irq_remove_handler(RTC_IRQ, _callback); + irq_remove_handler(RTC_IRQ, rtc_irq_handler); _callback = NULL; }