mirror of
https://github.com/firewalkwithm3/Sensor-Watch.git
synced 2024-11-22 11:10:29 +08:00
WIP: refactor RTC to use clock mode directly
This commit is contained in:
parent
751ed9c7a4
commit
a65dcf1ec8
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
/// @}
|
||||
|
|
Loading…
Reference in a new issue