summaryrefslogtreecommitdiffstats
path: root/watch-library
diff options
context:
space:
mode:
Diffstat (limited to 'watch-library')
-rw-r--r--watch-library/hpl/rtc/hpl_rtc.c47
-rw-r--r--watch-library/hw/driver_init.c11
-rw-r--r--watch-library/hw/driver_init.h20
-rw-r--r--watch-library/watch/watch_deepsleep.c36
-rw-r--r--watch-library/watch/watch_deepsleep.h5
-rw-r--r--watch-library/watch/watch_private.c3
-rw-r--r--watch-library/watch/watch_rtc.c150
-rw-r--r--watch-library/watch/watch_rtc.h74
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);
/// @}