diff options
| -rw-r--r-- | watch-library/hpl/rtc/hpl_rtc.c | 47 | ||||
| -rw-r--r-- | watch-library/hw/driver_init.c | 11 | ||||
| -rw-r--r-- | watch-library/hw/driver_init.h | 20 | ||||
| -rw-r--r-- | watch-library/watch/watch_deepsleep.c | 36 | ||||
| -rw-r--r-- | watch-library/watch/watch_deepsleep.h | 5 | ||||
| -rw-r--r-- | watch-library/watch/watch_private.c | 3 | ||||
| -rw-r--r-- | watch-library/watch/watch_rtc.c | 150 | ||||
| -rw-r--r-- | watch-library/watch/watch_rtc.h | 74 | 
8 files changed, 226 insertions, 120 deletions
| diff --git a/watch-library/hpl/rtc/hpl_rtc.c b/watch-library/hpl/rtc/hpl_rtc.c index e580fa78..429feff7 100644 --- a/watch-library/hpl/rtc/hpl_rtc.c +++ b/watch-library/hpl/rtc/hpl_rtc.c @@ -376,55 +376,8 @@ int32_t _calendar_register_callback(struct calendar_dev *const dev, calendar_drv  	return ERR_NONE;  } -/** - * \brief RTC interrupt handler - * - * \param[in] dev The pointer to calendar device struct - */ -static void _rtc_interrupt_handler(struct calendar_dev *dev) -{ -	/* Read and mask interrupt flag register */ -	uint16_t interrupt_status  = hri_rtcmode0_read_INTFLAG_reg(dev->hw); -	uint16_t interrupt_enabled = hri_rtcmode0_read_INTEN_reg(dev->hw); - -	if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_ALARM0) { -		if (dev->callback_alarm != NULL) { -			dev->callback_alarm(); -		} - -		/* Clear interrupt flag */ -		hri_rtcmode0_clear_interrupt_CMP0_bit(dev->hw); -	} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_PER7) { -		if (dev->callback_tick != NULL) { -			dev->callback_tick(); -		} - -		/* Clear interrupt flag */ -		hri_rtcmode0_clear_interrupt_PER7_bit(dev->hw); -	} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_TAMPER) { -		uint8_t reason = hri_rtc_get_TAMPID_reg(dev->hw, 0x1F); -		if (dev->callback_tamper != NULL) { -			dev->callback_tamper(reason); -		} -		hri_rtc_write_TAMPID_reg(dev->hw, reason); - -		/* Clear interrupt flag */ -		hri_rtcmode0_clear_interrupt_TAMPER_bit(dev->hw); -	} -} -/** - * \brief Set calendar IRQ - */  void _calendar_set_irq(struct calendar_dev *const dev)  {  	(void)dev;  	NVIC_SetPendingIRQ(RTC_IRQn);  } - -/** - * \brief Rtc interrupt handler - */ -void RTC_Handler(void) -{ -	_rtc_interrupt_handler(_rtc_dev); -} diff --git a/watch-library/hw/driver_init.c b/watch-library/hw/driver_init.c index 2cfdced7..09723bec 100644 --- a/watch-library/hw/driver_init.c +++ b/watch-library/hw/driver_init.c @@ -13,19 +13,8 @@  struct slcd_sync_descriptor SEGMENT_LCD_0; -struct calendar_descriptor CALENDAR_0; -  struct i2c_m_sync_desc I2C_0; -void CALENDAR_0_CLOCK_init(void) { -	hri_mclk_set_APBAMASK_RTC_bit(MCLK); -} - -void CALENDAR_0_init(void) { -	CALENDAR_0_CLOCK_init(); -	calendar_init(&CALENDAR_0, RTC); -} -  void I2C_0_PORT_init(void) {  	gpio_set_pin_pull_mode(SDA, diff --git a/watch-library/hw/driver_init.h b/watch-library/hw/driver_init.h index 019a0b56..f56f8f9e 100644 --- a/watch-library/hw/driver_init.h +++ b/watch-library/hw/driver_init.h @@ -38,36 +38,16 @@ extern "C" {  extern struct adc_sync_descriptor ADC_0; -extern struct calendar_descriptor CALENDAR_0; -  extern struct i2c_m_sync_desc I2C_0; -extern struct pwm_descriptor PWM_0; - -extern struct pwm_descriptor       PWM_1;  extern struct slcd_sync_descriptor SEGMENT_LCD_0; -void ADC_0_PORT_init(void); -void ADC_0_CLOCK_init(void); -void ADC_0_init(void); - -void CALENDAR_0_CLOCK_init(void); -void CALENDAR_0_init(void); -  void I2C_0_CLOCK_init(void);  void I2C_0_init(void);  void I2C_0_PORT_init(void);  void delay_driver_init(void); -void PWM_0_PORT_init(void); -void PWM_0_CLOCK_init(void); -void PWM_0_init(void); - -void PWM_1_PORT_init(void); -void PWM_1_CLOCK_init(void); -void PWM_1_init(void); -  void EXTERNAL_IRQ_0_init(void);  void SEGMENT_LCD_0_init(void); diff --git a/watch-library/watch/watch_deepsleep.c b/watch-library/watch/watch_deepsleep.c index a69ba66c..4294b660 100644 --- a/watch-library/watch/watch_deepsleep.c +++ b/watch-library/watch/watch_deepsleep.c @@ -29,24 +29,9 @@  #warning This board revision does not support external wake on BTN_ALARM, so watch_register_extwake_callback will not work with it. Use watch_register_interrupt_callback instead.  #endif -static void extwake_callback(uint8_t reason); -ext_irq_cb_t btn_alarm_callback; -ext_irq_cb_t a2_callback; -ext_irq_cb_t a4_callback; - - static void extwake_callback(uint8_t reason) { -    if (reason & RTC_TAMPID_TAMPID2) { -        if (btn_alarm_callback != NULL) btn_alarm_callback(); -    } else if (reason & RTC_TAMPID_TAMPID1) { -        if (a2_callback != NULL) a2_callback(); -    } else if (reason & RTC_TAMPID_TAMPID0) { -        if (a4_callback != NULL) a4_callback(); -    } -} -  void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool level) {      uint32_t pinmux; -    hri_rtc_tampctrl_reg_t config = hri_rtc_get_TAMPCTRL_reg(RTC, 0xFFFFFFFF); +    hri_rtc_tampctrl_reg_t config = RTC->MODE2.TAMPCTRL.reg;      switch (pin) {          case A4: @@ -84,16 +69,17 @@ void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool le      gpio_set_pin_function(pin, pinmux);      // disable the RTC -	if (hri_rtcmode0_get_CTRLA_ENABLE_bit(RTC)) { -		hri_rtcmode0_clear_CTRLA_ENABLE_bit(RTC); -		hri_rtcmode0_wait_for_sync(RTC, RTC_MODE0_SYNCBUSY_ENABLE); -	} +    RTC->MODE2.CTRLA.bit.ENABLE = 0; +    while (RTC->MODE2.SYNCBUSY.bit.ENABLE); +      // update the configuration -    hri_rtc_write_TAMPCTRL_reg(RTC, config); +    RTC->MODE2.TAMPCTRL.reg = config;      // re-enable the RTC -    hri_rtcmode0_set_CTRLA_ENABLE_bit(RTC); +    RTC->MODE2.CTRLA.bit.ENABLE = 1; -    _extwake_register_callback(&CALENDAR_0.device, extwake_callback); +    NVIC_ClearPendingIRQ(RTC_IRQn); +    NVIC_EnableIRQ(RTC_IRQn); +    RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_TAMPER;  }  void watch_disable_extwake_interrupt(uint8_t pin) { @@ -176,7 +162,7 @@ void watch_enter_shallow_sleep(char *message) {      _watch_disable_all_peripherals_except_slcd();      // disable tick interrupt -    watch_register_tick_callback(NULL); +    watch_disable_tick_callback();      // disable brownout detector interrupt, which could inadvertently wake us up.      SUPC->INTENCLR.bit.BOD33DET = 1; @@ -202,7 +188,7 @@ void watch_enter_deep_sleep() {      // so let's do it!      watch_register_extwake_callback(BTN_ALARM, NULL, true); -    watch_register_tick_callback(NULL); +    watch_disable_tick_callback();      _watch_disable_all_peripherals_except_slcd();      slcd_sync_deinit(&SEGMENT_LCD_0);      hri_mclk_clear_APBCMASK_SLCD_bit(SLCD); diff --git a/watch-library/watch/watch_deepsleep.h b/watch-library/watch/watch_deepsleep.h index 1e118929..dc8724d9 100644 --- a/watch-library/watch/watch_deepsleep.h +++ b/watch-library/watch/watch_deepsleep.h @@ -23,6 +23,11 @@   */  ////< @file watch_deepsleep.h +// These are declared in watch_rtc.c. +extern ext_irq_cb_t btn_alarm_callback; +extern ext_irq_cb_t a2_callback; +extern ext_irq_cb_t a4_callback; +  /** @addtogroup deepsleep Deep Sleep Control    * @brief This section covers functions related to preparing for and entering BACKUP mode, the    *        deepest sleep mode available on the SAM L22 diff --git a/watch-library/watch/watch_private.c b/watch-library/watch/watch_private.c index bdf6b78a..dc70f4cb 100644 --- a/watch-library/watch/watch_private.c +++ b/watch-library/watch/watch_private.c @@ -56,8 +56,7 @@ void _watch_init() {      SUPC->BOD33.bit.ENABLE = 1;      // External wake depends on RTC; calendar is a required module. -    CALENDAR_0_init(); -    calendar_enable(&CALENDAR_0); +    _watch_rtc_init();      // set up state      btn_alarm_callback = NULL; diff --git a/watch-library/watch/watch_rtc.c b/watch-library/watch/watch_rtc.c index 2d6d598f..3d2104f3 100644 --- a/watch-library/watch/watch_rtc.c +++ b/watch-library/watch/watch_rtc.c @@ -22,19 +22,153 @@   * SOFTWARE.   */ - bool _watch_rtc_is_enabled() { -    return RTC->MODE0.CTRLA.bit.ENABLE; +ext_irq_cb_t tick_callback; +ext_irq_cb_t alarm_callback; +ext_irq_cb_t btn_alarm_callback; +ext_irq_cb_t a2_callback; +ext_irq_cb_t a4_callback; + +bool _watch_rtc_is_enabled() { +    return RTC->MODE2.CTRLA.bit.ENABLE;  } -void watch_set_date_time(struct calendar_date_time date_time) { -    calendar_set_date(&CALENDAR_0, &date_time.date); -    calendar_set_time(&CALENDAR_0, &date_time.time); +void _sync_rtc() { +    while (RTC->MODE2.SYNCBUSY.reg);  } -void watch_get_date_time(struct calendar_date_time *date_time) { -    calendar_get_date_time(&CALENDAR_0, date_time); +void _watch_rtc_init() { +    MCLK->APBAMASK.reg |= MCLK_APBAMASK_RTC; + +    if (_watch_rtc_is_enabled()) return; // don't reset the RTC if it's already set up. + +    RTC->MODE2.CTRLA.bit.ENABLE = 0; +    _sync_rtc(); + +    RTC->MODE2.CTRLA.bit.SWRST = 1; +    _sync_rtc(); + +    RTC->MODE2.CTRLA.bit.MODE = RTC_MODE2_CTRLA_MODE_CLOCK_Val; +    RTC->MODE2.CTRLA.bit.PRESCALER = RTC_MODE2_CTRLA_PRESCALER_DIV1024_Val; +    RTC->MODE2.CTRLA.bit.CLOCKSYNC = 1; +    RTC->MODE2.CTRLA.bit.ENABLE = 1; +    _sync_rtc(); +} + +void watch_rtc_set_date_time(watch_date_time date_time) { +    RTC_MODE2_CLOCK_Type val; + +    val.bit.SECOND = date_time.second; +    val.bit.MINUTE = date_time.minute; +    val.bit.HOUR = date_time.hour; +    val.bit.DAY = date_time.day; +    val.bit.MONTH = date_time.month; +    val.bit.YEAR = (uint8_t)(date_time.year - WATCH_RTC_REFERENCE_YEAR); + +    RTC->MODE2.CLOCK.reg = val.reg; +    _sync_rtc(); +} + +watch_date_time watch_rtc_get_date_time() { +    watch_date_time retval; + +    _sync_rtc(); +    RTC_MODE2_CLOCK_Type val = RTC->MODE2.CLOCK; + +    retval.year = val.bit.YEAR + WATCH_RTC_REFERENCE_YEAR; +    retval.month = val.bit.MONTH; +    retval.day = val.bit.DAY; +    retval.hour = val.bit.HOUR; +    retval.minute = val.bit.MINUTE; +    retval.second = val.bit.SECOND; + +    return retval;  }  void watch_register_tick_callback(ext_irq_cb_t callback) { -    _prescaler_register_callback(&CALENDAR_0.device, callback); +    tick_callback = callback; +    NVIC_ClearPendingIRQ(RTC_IRQn); +    NVIC_EnableIRQ(RTC_IRQn); +    RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_PER7; +} + +void watch_disable_tick_callback() { +    RTC->MODE2.INTENCLR.reg = RTC_MODE2_INTENCLR_PER7; +} + +void watch_rtc_register_alarm_callback(ext_irq_cb_t callback, watch_date_time alarm_time, watch_rtc_alarm_match mask) { +    RTC->MODE2.Mode2Alarm[0].ALARM.bit.SECOND = alarm_time.second; +    RTC->MODE2.Mode2Alarm[0].ALARM.bit.MINUTE = alarm_time.minute; +    RTC->MODE2.Mode2Alarm[0].ALARM.bit.HOUR = alarm_time.hour; +    RTC->MODE2.Mode2Alarm[0].ALARM.bit.DAY = alarm_time.day; +    RTC->MODE2.Mode2Alarm[0].ALARM.bit.MONTH = alarm_time.month; +    RTC->MODE2.Mode2Alarm[0].ALARM.bit.YEAR = (uint8_t)(alarm_time.year - WATCH_RTC_REFERENCE_YEAR); +    RTC->MODE2.Mode2Alarm[0].MASK.reg = mask; + +    RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_ALARM0; +    alarm_callback = callback; +    NVIC_ClearPendingIRQ(RTC_IRQn); +    NVIC_EnableIRQ(RTC_IRQn); +    RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_ALARM0; +} + +void watch_rtc_disable_alarm_callback() { +    RTC->MODE2.INTENCLR.reg = RTC_MODE2_INTENCLR_ALARM0; +} + +void RTC_Handler(void) { +    uint16_t interrupt_status = RTC->MODE2.INTFLAG.reg; +    uint16_t interrupt_enabled = RTC->MODE2.INTENSET.reg; + +    if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_ALARM0) { +        if (alarm_callback != NULL) { +            alarm_callback(); +        } +        RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_ALARM0; +    } else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_PER7) { +        if (tick_callback != NULL) { +            tick_callback(); +        } +        RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_PER7; +    } else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_TAMPER) { +        uint8_t reason = RTC->MODE2.TAMPID.reg; +        if (reason & RTC_TAMPID_TAMPID2) { +            if (btn_alarm_callback != NULL) btn_alarm_callback(); +        } else if (reason & RTC_TAMPID_TAMPID1) { +            if (a2_callback != NULL) a2_callback(); +        } else if (reason & RTC_TAMPID_TAMPID0) { +            if (a4_callback != NULL) a4_callback(); +        } +        RTC->MODE2.TAMPID.reg = reason; +        RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_TAMPER; +    } +} + +/////////////////////// +// Deprecated functions + +void watch_set_date_time(struct calendar_date_time date_time) { +    RTC_MODE2_CLOCK_Type val; + +    val.bit.SECOND = date_time.time.sec; +    val.bit.MINUTE = date_time.time.min; +    val.bit.HOUR = date_time.time.hour; +    val.bit.DAY = date_time.date.day; +    val.bit.MONTH = date_time.date.month; +    val.bit.YEAR = (uint8_t)(date_time.date.year - WATCH_RTC_REFERENCE_YEAR); + +    RTC->MODE2.CLOCK.reg = val.reg; + +    _sync_rtc(); +} + +void watch_get_date_time(struct calendar_date_time *date_time) { +    _sync_rtc(); +    RTC_MODE2_CLOCK_Type val = RTC->MODE2.CLOCK; + +    date_time->time.sec = val.bit.SECOND; +    date_time->time.min = val.bit.MINUTE; +    date_time->time.hour = val.bit.HOUR; +    date_time->date.day = val.bit.DAY; +    date_time->date.month = val.bit.MONTH; +    date_time->date.year = val.bit.YEAR + WATCH_RTC_REFERENCE_YEAR;  } diff --git a/watch-library/watch/watch_rtc.h b/watch-library/watch/watch_rtc.h index c685ac26..a71eab42 100644 --- a/watch-library/watch/watch_rtc.h +++ b/watch-library/watch/watch_rtc.h @@ -35,24 +35,84 @@    *          to wake from STANDBY mode.    */  /// @{ + +#define WATCH_RTC_REFERENCE_YEAR (2020) + +typedef struct watch_date_time { +    uint16_t year; +    uint8_t month; +    uint8_t day; +    uint8_t hour; +    uint8_t minute; +    uint8_t second; +} watch_date_time; + +typedef enum watch_rtc_alarm_match { +    ALARM_MATCH_DISABLED = 0, +    ALARM_MATCH_SS, +    ALARM_MATCH_MMSS, +    ALARM_MATCH_HHMMSS, +} watch_rtc_alarm_match; +  /** @brief Called by main.c to check if the RTC is enabled. -  * You may call this function, but outside of app_init, it sbould always return true. +  * You may call this function, but outside of app_init, it should always return true.    */  bool _watch_rtc_is_enabled(); +/** @brief Sets the date and time. +  * @param date_time The time you wish to set. +  * @note Internally, the SAM L22 stores the year as six bits representing a value from 0 to 63. It treats this +  *       as a year offset from a reference year, which must be a leap year. For now, this library uses 2020 as +  *       the reference year, so the range of valid values is 2020 to 2083. +  */ +void watch_rtc_set_date_time(watch_date_time date_time); + +/** @brief Returns the system date and time in the provided struct. +  * @return A watch_date_time with the current date and time. +  */ +watch_date_time watch_rtc_get_date_time(); + +/** @brief Registers an alarm callback that will be called when the RTC time matches the target time, as masked +  *        by the provided mask. +  * @param callback The function you wish to have called when the alarm fires. If this value is NULL, the alarm +  *                 interrupt will still be enabled, but no callback function will be called. +  * @param alarm_time The time that you wish to match. The date is currently ignored. +  * @param mask One of the values in watch_rtc_alarm_match indicating which values to check. +  * @details The alarm interrupt is a versatile tool for scheduling events in the future, especially since it can +  *          wake the device from both shallow and deep sleep modes. The key to its versatility is the mask +  *          parameter. Suppose we set an alarm for midnight, 00:00:00. +  *           * if mask is ALARM_MATCH_SS, the alarm will fire every minute when the clock ticks to seconds == 0. +  *           * with ALARM_MATCH_MMSS, the alarm will once an hour, at the top of each hour. +  *           * with ALARM_MATCH_HHMMSS, the alarm will fire at midnight every day. +  *          In theory the SAM L22's alarm function can match on days, months and even years, but I have not had +  *          success with this yet; as such, I am omitting these options for now. +  */ +void watch_rtc_register_alarm_callback(ext_irq_cb_t callback, watch_date_time alarm_time, watch_rtc_alarm_match mask); + +/** @brief Disables the alarm callback. +  */ +void watch_rtc_disable_alarm_callback(); + +/** @brief Registers a "tick" callback that will be called once per second. +  * @param callback The function you wish to have called when the clock ticks. If you pass in NULL, the tick +  *                 interrupt will still be enabled, but no callback function will be called. +  */ +void watch_register_tick_callback(ext_irq_cb_t callback); + +/** @brief Disables the tick callback. +  */ +void watch_disable_tick_callback(); +  /** @brief Sets the system date and time.    * @param date_time A struct representing the date and time you wish to set.    */ +__attribute__((deprecated("Use watch_rtc_set_date_time function instead")))  void watch_set_date_time(struct calendar_date_time date_time);  /** @brief Returns the system date and time in the provided struct. -  * @param date_time A pointer to a calendar_date_time struct. -                     It will be populated with the correct date and time on return. +  * @param date_time A pointer to a calendar_date_time struct. It will have with the correct date and time on return.    */ +__attribute__((deprecated("Use the watch_rtc_get_date_time function instead")))  void watch_get_date_time(struct calendar_date_time *date_time); -/** @brief Registers a "tick" callback that will be called once per second. -  * @param callback The function you wish to have called when the clock ticks. -  */ -void watch_register_tick_callback(ext_irq_cb_t callback);  /// @} | 
