work on RTC tamper interrupt and external wake

This commit is contained in:
Joey Castillo 2021-08-08 15:02:38 -04:00
parent 27edc50be1
commit e45fdf15af
6 changed files with 106 additions and 41 deletions

View file

@ -62,6 +62,7 @@ void app_wake_from_deep_sleep() {
application_state.mode = (ApplicationMode)watch_get_backup_data(0);
application_state.color = (LightColor)watch_get_backup_data(1);
application_state.wake_count = (uint8_t)watch_get_backup_data(2) + 1;
application_state.debounce_wait = true;
}
/**
@ -184,5 +185,8 @@ void cb_mode_pressed() {
void cb_alarm_pressed() {
if (application_state.debounce_wait) return;
application_state.debounce_wait = true;
application_state.enter_deep_sleep = true;
// boo: http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_L22_Family_Errata_DS80000782B.pdf
// Reference 15010. doesn't say it applies to PA02 but it seems it does?
// anyway can't deep sleep now :(
// application_state.enter_deep_sleep = true;
}

View file

@ -48,7 +48,7 @@
// <e> RTC Tamper Input 0 settings
// <id> tamper_input_0_settings
#ifndef CONF_TAMPER_INPUT_0_SETTINGS
#define CONF_TAMPER_INPUT_0_SETTINGS 0
#define CONF_TAMPER_INPUT_0_SETTINGS 1
#endif
// <q> Tamper Level Settings
@ -66,7 +66,7 @@
// <i> These bits define the RTC Tamper Input Action to be performed
// <id> rtc_tamper_input_action_0
#ifndef CONF_RTC_TAMPER_INACT_0
#define CONF_RTC_TAMPER_INACT_0 0
#define CONF_RTC_TAMPER_INACT_0 1
#endif
// <q> Debounce Enable for Tamper Input
@ -81,7 +81,7 @@
// <e> RTC Tamper Input 1 settings
// <id> tamper_input_1_settings
#ifndef CONF_TAMPER_INPUT_1_SETTINGS
#define CONF_TAMPER_INPUT_1_SETTINGS 0
#define CONF_TAMPER_INPUT_1_SETTINGS 1
#endif
// <q> Tamper Level Settings
@ -99,7 +99,7 @@
// <i> These bits define the RTC Tamper Input Action to be performed
// <id> rtc_tamper_input_action_1
#ifndef CONF_RTC_TAMPER_INACT_1
#define CONF_RTC_TAMPER_INACT_1 0
#define CONF_RTC_TAMPER_INACT_1 1
#endif
// <q> Debounce Enable for Tamper Input

View file

@ -77,7 +77,8 @@ enum calendar_alarm_mode { ONESHOT = 1, REPEAT };
/**
* \brief Prototype of callback on alarm match
*/
typedef void (*calendar_drv_cb_t)(struct calendar_dev *const dev);
typedef void (*calendar_drv_cb_t)();
typedef void (*calendar_drv_extwake_cb_t)(uint8_t reason);
/**
* \brief Structure of Calendar instance
@ -88,8 +89,8 @@ struct calendar_dev {
/** Alarm match callback */
calendar_drv_cb_t callback_alarm;
/** Tamper callback */
calendar_drv_cb_t callback_tamper;
/** Tamper callback */
calendar_drv_extwake_cb_t callback_tamper;
/** Tick callback */
calendar_drv_cb_t callback_tick;
/** IRQ struct */
struct _irq_descriptor irq;
@ -260,7 +261,7 @@ int32_t _prescaler_register_callback(struct calendar_dev *const dev, calendar_dr
*
* \return ERR_NONE on success, or an error code on failure.
*/
int32_t _extwake_register_callback(struct calendar_dev *const dev, calendar_drv_cb_t callback);
int32_t _extwake_register_callback(struct calendar_dev *const dev, calendar_drv_extwake_cb_t callback);
/**
* \brief Find tamper is detected on specified pin

View file

@ -327,8 +327,7 @@ int32_t _prescaler_register_callback(struct calendar_dev *const dev, calendar_dr
return ERR_NONE;
}
// TODO: refactor this so it doesn't take a callback (it will never get called anyway)
int32_t _extwake_register_callback(struct calendar_dev *const dev, calendar_drv_cb_t callback)
int32_t _extwake_register_callback(struct calendar_dev *const dev, calendar_drv_extwake_cb_t callback)
{
ASSERT(dev && dev->hw);
@ -389,16 +388,20 @@ static void _rtc_interrupt_handler(struct calendar_dev *dev)
uint16_t interrupt_enabled = hri_rtcmode0_read_INTEN_reg(dev->hw);
if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_ALARM0) {
dev->callback_alarm(dev);
dev->callback_alarm();
/* Clear interrupt flag */
hri_rtcmode0_clear_interrupt_CMP0_bit(dev->hw);
} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_PER7) {
dev->callback_tick(dev);
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);
dev->callback_tamper(reason);
hri_rtc_write_TAMPID_reg(dev->hw, reason);
/* Clear interrupt flag */
hri_rtcmode0_clear_interrupt_TAMPER_bit(dev->hw);
}

View file

@ -1,6 +1,16 @@
#include "watch.h"
#include <stdlib.h>
//////////////////////////////////////////////////////////////////////////////////////////
// User callbacks and other definitions
ext_irq_cb_t btn_alarm_callback;
ext_irq_cb_t a2_callback;
ext_irq_cb_t d1_callback;
static void extwake_callback(uint8_t reason);
//////////////////////////////////////////////////////////////////////////////////////////
// Initialization
@ -15,7 +25,14 @@ void _watch_init() {
// Not sure if this belongs in every app -- is there a power impact?
delay_driver_init();
// set up state
btn_alarm_callback = NULL;
a2_callback = NULL;
d1_callback = NULL;
}
//////////////////////////////////////////////////////////////////////////////////////////
// Segmented Display
@ -215,8 +232,16 @@ void watch_enable_buttons() {
EXTERNAL_IRQ_0_init();
}
void watch_register_button_callback(const uint32_t pin, ext_irq_cb_t callback) {
ext_irq_register(pin, callback);
void watch_register_button_callback(const uint8_t pin, ext_irq_cb_t callback) {
if (pin == BTN_ALARM) {
gpio_set_pin_direction(BTN_ALARM, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(BTN_ALARM, GPIO_PULL_DOWN);
gpio_set_pin_function(BTN_ALARM, PINMUX_PA02G_RTC_IN2);
btn_alarm_callback = callback;
_extwake_register_callback(&CALENDAR_0.device, extwake_callback);
} else {
ext_irq_register(pin, callback);
}
}
//////////////////////////////////////////////////////////////////////////////////////////
@ -310,15 +335,8 @@ void watch_get_date_time(struct calendar_date_time *date_time) {
calendar_get_date_time(&CALENDAR_0, date_time);
}
static ext_irq_cb_t tick_user_callback;
static void tick_callback(struct calendar_dev *const dev) {
tick_user_callback();
}
void watch_register_tick_callback(ext_irq_cb_t callback) {
tick_user_callback = callback;
_prescaler_register_callback(&CALENDAR_0.device, &tick_callback);
_prescaler_register_callback(&CALENDAR_0.device, callback);
}
//////////////////////////////////////////////////////////////////////////////////////////
@ -448,6 +466,32 @@ uint32_t watch_i2c_read32(int16_t addr, uint8_t reg) {
//////////////////////////////////////////////////////////////////////////////////////////
// Deep Sleep
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 (d1_callback != NULL) d1_callback();
}
}
void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback) {
uint32_t pinmux;
if (pin == D1) {
d1_callback = callback;
pinmux = PINMUX_PB00G_RTC_IN0;
} else if (pin == A2) {
a2_callback = callback;
pinmux = PINMUX_PB02G_RTC_IN1;
} else {
return;
}
gpio_set_pin_direction(pin, GPIO_DIRECTION_IN);
gpio_set_pin_function(pin, pinmux);
_extwake_register_callback(&CALENDAR_0.device, extwake_callback);
}
void watch_store_backup_data(uint32_t data, uint8_t reg) {
if (reg < 8) {
RTC->MODE0.BKUP[reg].reg = data;
@ -462,14 +506,14 @@ uint32_t watch_get_backup_data(uint8_t reg) {
return 0;
}
static void extwake_callback(struct calendar_dev *const dev) {
// this will never get called since we are basically waking from reset
}
void watch_enter_deep_sleep() {
// enable and configure the external wake interrupt
_extwake_register_callback(&CALENDAR_0.device, &extwake_callback);
_tamper_enable_debounce_asynchronous(&CALENDAR_0.device);
// enable and configure the external wake interrupt, if not already set up.
if (btn_alarm_callback == NULL && a2_callback == NULL && d1_callback == NULL) {
gpio_set_pin_direction(BTN_ALARM, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(BTN_ALARM, GPIO_PULL_DOWN);
gpio_set_pin_function(BTN_ALARM, PINMUX_PA02G_RTC_IN2);
_extwake_register_callback(&CALENDAR_0.device, extwake_callback);
}
// disable SLCD
slcd_sync_deinit(&SEGMENT_LCD_0);
@ -477,12 +521,6 @@ void watch_enter_deep_sleep() {
// TODO: disable other peripherals
// disable EIC interrupt on ALARM pin (if any) and enable RTC interrupt.
ext_irq_disable(BTN_ALARM);
gpio_set_pin_direction(BTN_ALARM, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(BTN_ALARM, GPIO_PULL_DOWN);
gpio_set_pin_function(BTN_ALARM, PINMUX_PA02G_RTC_IN2);
// go into backup sleep mode
sleep(5);
}

View file

@ -256,16 +256,18 @@ void watch_enable_analog(const uint8_t pin);
* @brief This section covers functions related to the three buttons: Light, Mode and Alarm.
*/
/// @{
/** @brief Enables the external interrupt controller.
/** @brief Enables the external interrupt controller for use with the buttons.
* @note The BTN_ALARM button runs off of an interrupt in the the RTC controller, not the EIC. If your
* application ONLY makes use of the alarm button, you do not need to call this method; you can
* save ~5µA by leaving the EIC disabled and only registering a callback for BTN_ALARM.
*/
void watch_enable_buttons();
/** @brief Configures an external interrupt
* @param pin One of pins BTN_LIGHT, BTN_MODE or BTN_ALARM.
* @param callback The function you wish to have called when the button is pressed.
* @todo Make the alarm interrupt use the RTC tamper interrupt instead of the EIC.
*/
void watch_register_button_callback(const uint32_t pin, ext_irq_cb_t callback);
void watch_register_button_callback(const uint8_t pin, ext_irq_cb_t callback);
/// @}
@ -391,7 +393,18 @@ uint32_t watch_i2c_read32(int16_t addr, uint8_t reg);
* deepest sleep mode available on the SAM L22
*/
/// @{
/** @brief Stores 32 bits of data in the RTC's backup register, which retains its data in deep sleep.
/** @brief Registers a callback on one of the RTC's external wake pins, which can wake the device
* from deep sleep mode.
* @param pin Either pin A2 or pin D1, the two external wake pins on the nine-pin connector.
* @param callback The callback to be called if this pin triggers outside of deep sleep mode.
* @note When in normal or STANDBY mode, this will function much like a standard external interrupt
* situation: these pins will wake from standby, and your callback will be called. However,
* if the device enters deep sleep and one of these pins wakes the device, your callback
* WILL NOT be called.
*/
void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback);
/** @brief Stores data in one of the RTC's backup registers, which retain their data in deep sleep.
* @param data An unsigned 32 bit integer with the data you wish to store.
* @param reg A register from 0-7.
*/
@ -413,7 +426,13 @@ uint32_t watch_get_backup_data(uint8_t reg);
* in ACTIVE, IDLE or STANDBY modes, but it *will not be called* when waking from BACKUP.
* Waking from backup is effectively like waking from reset, except that your @ref
* app_wake_from_deep_sleep function will be called.
* @warning still kind of glitchy!
* @warning In initial testing, it seems like the ALARM_BTN pin (PA02 RTC/IN2) cannot wake the device
from deep sleep mode. There is an errata note (Reference: 15010, linked) that says that
due to a silicon bug, PB01 cannot be used as RTC/IN2. It seems though that this bug may
also affect PA02. As a result and I'm very bummed about this you cannot use deep sleep
mode unless you set up an external wake interrupt using a device on the nine-pin connector
(i.e. an accelerometer with an interrupt pin). Otherwise your only option for waking will
be to unscrew the watch case and press the reset button on the back of the board.
*/
void watch_enter_deep_sleep();
/// @}