WIP: refactor RTC to use clock mode directly

This commit is contained in:
Joey Castillo 2021-09-27 17:42:27 -04:00
parent 751ed9c7a4
commit a65dcf1ec8
8 changed files with 228 additions and 122 deletions

View file

@ -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);
}

View file

@ -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,

View file

@ -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);

View file

@ -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);
}
// update the configuration
hri_rtc_write_TAMPCTRL_reg(RTC, config);
// re-enable the RTC
hri_rtcmode0_set_CTRLA_ENABLE_bit(RTC);
RTC->MODE2.CTRLA.bit.ENABLE = 0;
while (RTC->MODE2.SYNCBUSY.bit.ENABLE);
_extwake_register_callback(&CALENDAR_0.device, extwake_callback);
// update the configuration
RTC->MODE2.TAMPCTRL.reg = config;
// re-enable the RTC
RTC->MODE2.CTRLA.bit.ENABLE = 1;
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);

View file

@ -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

View file

@ -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;

View file

@ -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;
}

View file

@ -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);
/// @}