diff options
author | Joey Castillo <jose.castillo@gmail.com> | 2021-08-25 12:15:58 -0600 |
---|---|---|
committer | Joey Castillo <jose.castillo@gmail.com> | 2021-08-25 12:15:58 -0600 |
commit | d09d3c3c95a412ce5f5af8068205c416938510e7 (patch) | |
tree | 3881a35421aec0ad645cc43fd226e848d1cb6a1e /watch-library | |
parent | c35e8e2b07e971c587bda9a4c28a5916312fd4cd (diff) | |
download | Sensor-Watch-d09d3c3c95a412ce5f5af8068205c416938510e7.tar.gz Sensor-Watch-d09d3c3c95a412ce5f5af8068205c416938510e7.tar.bz2 Sensor-Watch-d09d3c3c95a412ce5f5af8068205c416938510e7.zip |
external interrupt refactor: allow enabling in watch library functions
Diffstat (limited to 'watch-library')
-rw-r--r-- | watch-library/hw/driver_init.c | 46 | ||||
-rw-r--r-- | watch-library/watch/watch.c | 5 | ||||
-rw-r--r-- | watch-library/watch/watch_deepsleep.c | 62 | ||||
-rw-r--r-- | watch-library/watch/watch_deepsleep.h | 16 | ||||
-rw-r--r-- | watch-library/watch/watch_extint.c | 100 | ||||
-rw-r--r-- | watch-library/watch/watch_extint.h | 59 | ||||
-rw-r--r-- | watch-library/watch/watch_private.c | 2 |
7 files changed, 196 insertions, 94 deletions
diff --git a/watch-library/hw/driver_init.c b/watch-library/hw/driver_init.c index 02907feb..6d910d22 100644 --- a/watch-library/hw/driver_init.c +++ b/watch-library/hw/driver_init.c @@ -35,52 +35,6 @@ void ADC_0_init(void) { adc_sync_init(&ADC_0, ADC, (void *)NULL); } -void EXTERNAL_IRQ_0_init(void) { - hri_gclk_write_PCHCTRL_reg(GCLK, EIC_GCLK_ID, CONF_GCLK_EIC_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos)); - hri_mclk_set_APBAMASK_EIC_bit(MCLK); - - // Set pin direction to input - gpio_set_pin_direction(BTN_ALARM, GPIO_DIRECTION_IN); - - gpio_set_pin_pull_mode(BTN_ALARM, - // <y> Pull configuration - // <id> pad_pull_config - // <GPIO_PULL_OFF"> Off - // <GPIO_PULL_UP"> Pull-up - // <GPIO_PULL_DOWN"> Pull-down - GPIO_PULL_DOWN); - - gpio_set_pin_function(BTN_ALARM, PINMUX_PA02A_EIC_EXTINT2); - - // Set pin direction to input - gpio_set_pin_direction(BTN_LIGHT, GPIO_DIRECTION_IN); - - gpio_set_pin_pull_mode(BTN_LIGHT, - // <y> Pull configuration - // <id> pad_pull_config - // <GPIO_PULL_OFF"> Off - // <GPIO_PULL_UP"> Pull-up - // <GPIO_PULL_DOWN"> Pull-down - GPIO_PULL_DOWN); - - gpio_set_pin_function(BTN_LIGHT, PINMUX_PA22A_EIC_EXTINT6); - - // Set pin direction to input - gpio_set_pin_direction(BTN_MODE, GPIO_DIRECTION_IN); - - gpio_set_pin_pull_mode(BTN_MODE, - // <y> Pull configuration - // <id> pad_pull_config - // <GPIO_PULL_OFF"> Off - // <GPIO_PULL_UP"> Pull-up - // <GPIO_PULL_DOWN"> Pull-down - GPIO_PULL_DOWN); - - gpio_set_pin_function(BTN_MODE, PINMUX_PA23A_EIC_EXTINT7); - - ext_irq_init(); -} - void CALENDAR_0_CLOCK_init(void) { hri_mclk_set_APBAMASK_RTC_bit(MCLK); } diff --git a/watch-library/watch/watch.c b/watch-library/watch/watch.c index d55b61fe..059c2a93 100644 --- a/watch-library/watch/watch.c +++ b/watch-library/watch/watch.c @@ -24,11 +24,6 @@ #include "watch.h" -// TODO: this should all live in watch_deepsleep.c, but right now watch_extint.c needs it -// because we're being too clever about the alarm button. -static void extwake_callback(uint8_t reason); -ext_irq_cb_t btn_alarm_callback; - #include "watch_rtc.c" #include "watch_slcd.c" #include "watch_extint.c" diff --git a/watch-library/watch/watch_deepsleep.c b/watch-library/watch/watch_deepsleep.c index 53a41b5c..ecc10b2b 100644 --- a/watch-library/watch/watch_deepsleep.c +++ b/watch-library/watch/watch_deepsleep.c @@ -22,8 +22,10 @@ * SOFTWARE. */ +static void extwake_callback(uint8_t reason); +ext_irq_cb_t btn_alarm_callback; ext_irq_cb_t a2_callback; -ext_irq_cb_t d1_callback; +ext_irq_cb_t a4_callback; static void extwake_callback(uint8_t reason) { if (reason & RTC_TAMPID_TAMPID2) { @@ -31,23 +33,59 @@ ext_irq_cb_t d1_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(); + if (a4_callback != NULL) a4_callback(); } } -void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback) { +void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool level) { 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; + hri_rtc_tampctrl_reg_t config = hri_rtc_get_TAMPCTRL_reg(RTC, 0xFFFFFFFF); + + switch (pin) { + case A4: + a4_callback = callback; + pinmux = PINMUX_PB00G_RTC_IN0; + config &= ~(3 << RTC_TAMPCTRL_IN0ACT_Pos); + config &= ~(1 << RTC_TAMPCTRL_TAMLVL0_Pos); + config |= 1 << RTC_TAMPCTRL_IN0ACT_Pos; + config |= 1 << RTC_TAMPCTRL_DEBNC0_Pos; + if (level) config |= 1 << RTC_TAMPCTRL_TAMLVL0_Pos; + break; + case A2: + a2_callback = callback; + pinmux = PINMUX_PB02G_RTC_IN1; + config &= ~(3 << RTC_TAMPCTRL_IN1ACT_Pos); + config &= ~(1 << RTC_TAMPCTRL_TAMLVL1_Pos); + config |= 1 << RTC_TAMPCTRL_IN1ACT_Pos; + config |= 1 << RTC_TAMPCTRL_DEBNC1_Pos; + if (level) config |= 1 << RTC_TAMPCTRL_TAMLVL1_Pos; + break; + case BTN_ALARM: + gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN); + btn_alarm_callback = callback; + pinmux = PINMUX_PA02G_RTC_IN2; + config &= ~(3 << RTC_TAMPCTRL_IN2ACT_Pos); + config &= ~(1 << RTC_TAMPCTRL_TAMLVL2_Pos); + config |= 1 << RTC_TAMPCTRL_IN2ACT_Pos; + config |= 1 << RTC_TAMPCTRL_DEBNC2_Pos; + if (level) config |= 1 << RTC_TAMPCTRL_TAMLVL2_Pos; + break; + default: + return; } gpio_set_pin_direction(pin, GPIO_DIRECTION_IN); 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); + _extwake_register_callback(&CALENDAR_0.device, extwake_callback); } @@ -67,7 +105,7 @@ uint32_t watch_get_backup_data(uint8_t reg) { void watch_enter_deep_sleep() { // enable and configure the external wake interrupt, if not already set up. - if (btn_alarm_callback == NULL && a2_callback == NULL && d1_callback == NULL) { + if (btn_alarm_callback == NULL && a2_callback == NULL && a4_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); diff --git a/watch-library/watch/watch_deepsleep.h b/watch-library/watch/watch_deepsleep.h index cea6f1aa..14fe8115 100644 --- a/watch-library/watch/watch_deepsleep.h +++ b/watch-library/watch/watch_deepsleep.h @@ -28,16 +28,24 @@ * deepest sleep mode available on the SAM L22 */ /// @{ + /** @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. + * from deep sleep (aka BACKUP) mode. + * @param pin Either pin BTN_ALARM, A2, or A4. These are the three external wake pins. If the pin + * is BTN_ALARM, this function also enables an internal pull down on that pin. * @param callback The callback to be called if this pin triggers outside of deep sleep mode. + * @param level The level you wish to scan for: true for rising, false for falling. Note that you + * cannot scan for both rising and falling edges like you can with the external interrupt + * pins; with the external wake interrupt, you can only get one or the other. * @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. + * WILL NOT be called, as the device is basically waking from reset at that point. + * @warning As of the current SAM L22 silicon revision (rev B), the BTN_ALARM pin cannot wake the + * device from BACKUP mode. You can still use this function to register a BTN_ALARM interrupt + * in normal or STANDBY mode, but to wake from BACKUP, you will need to use pin A2 or A4. */ -void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback); +void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool level); /** @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. diff --git a/watch-library/watch/watch_extint.c b/watch-library/watch/watch_extint.c index 19f6041a..421700bc 100644 --- a/watch-library/watch/watch_extint.c +++ b/watch-library/watch/watch_extint.c @@ -22,18 +22,96 @@ * SOFTWARE. */ - void watch_enable_buttons() { - EXTERNAL_IRQ_0_init(); +void watch_enable_external_interrupts() { + // Configure EIC to use GCLK3 (the 32.768 kHz crystal) + hri_gclk_write_PCHCTRL_reg(GCLK, EIC_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos)); + // Enable AHB clock for the EIC + hri_mclk_set_APBAMASK_EIC_bit(MCLK); + // call HAL's external interrupt init function + ext_irq_init(); } -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); +void watch_disable_external_interrupts() { + ext_irq_deinit(); + hri_mclk_clear_APBAMASK_EIC_bit(MCLK); +} + +void watch_register_interrupt_callback(const uint8_t pin, ext_irq_cb_t callback, watch_interrupt_trigger trigger) { + uint32_t pinmux; + hri_eic_config_reg_t config = hri_eic_get_CONFIG_reg(EIC, 0, 0xFFFFFFFF); + + switch (pin) { + case A4: + // same steps for each: determine the correct pin mux... + pinmux = PINMUX_PB00A_EIC_EXTINT0; + // ...clear out the configuration for this EIC channel... + config &= ~EIC_CONFIG_SENSE0_Msk; + // ...and reconfigure it with our new trigger value. + config |= EIC_CONFIG_SENSE0(trigger); + break; + case A1: + pinmux = PINMUX_PB01A_EIC_EXTINT1; + config &= ~EIC_CONFIG_SENSE1_Msk; + config |= EIC_CONFIG_SENSE1(trigger); + break; + case BTN_ALARM: + gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN); + pinmux = PINMUX_PA02A_EIC_EXTINT2; + config &= ~EIC_CONFIG_SENSE2_Msk; + config |= EIC_CONFIG_SENSE2(trigger); + break; + case A2: + pinmux = PINMUX_PB02A_EIC_EXTINT2; + config &= ~EIC_CONFIG_SENSE2_Msk; + config |= EIC_CONFIG_SENSE2(trigger); + break; + case A3: + pinmux = PINMUX_PB03A_EIC_EXTINT3; + config &= ~EIC_CONFIG_SENSE3_Msk; + config |= EIC_CONFIG_SENSE3(trigger); + break; + case A0: + pinmux = PINMUX_PB04A_EIC_EXTINT4; + config &= ~EIC_CONFIG_SENSE4_Msk; + config |= EIC_CONFIG_SENSE4(trigger); + break; + case BTN_LIGHT: + gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN); + pinmux = PINMUX_PA22A_EIC_EXTINT6; + config &= ~EIC_CONFIG_SENSE6_Msk; + config |= EIC_CONFIG_SENSE6(trigger); + break; + case BTN_MODE: + gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN); + pinmux = PINMUX_PA23A_EIC_EXTINT7; + config &= ~EIC_CONFIG_SENSE7_Msk; + config |= EIC_CONFIG_SENSE7(trigger); + break; + default: + return; + } + + gpio_set_pin_direction(pin, GPIO_DIRECTION_IN); + gpio_set_pin_function(pin, pinmux); + + // EIC configuration register is enable-protected, so we have to disable it first... + if (hri_eic_get_CTRLA_reg(EIC, EIC_CTRLA_ENABLE)) { + hri_eic_clear_CTRLA_ENABLE_bit(EIC); + // ...and wait for it to synchronize. + hri_eic_wait_for_sync(EIC, EIC_SYNCBUSY_ENABLE); } + // now update the configuration... + hri_eic_write_CONFIG_reg(EIC, 0, config); + // ...and re-enable the EIC + hri_eic_set_CTRLA_ENABLE_bit(EIC); + + ext_irq_register(pin, callback); +} + +inline void watch_register_button_callback(const uint8_t pin, ext_irq_cb_t callback) { + watch_register_interrupt_callback(pin, callback, INTERRUPT_TRIGGER_RISING); +} + +inline void watch_enable_buttons() { + watch_enable_external_interrupts(); } diff --git a/watch-library/watch/watch_extint.h b/watch-library/watch/watch_extint.h index 71295902..d1265119 100644 --- a/watch-library/watch/watch_extint.h +++ b/watch-library/watch/watch_extint.h @@ -25,28 +25,57 @@ #include "hal_ext_irq.h" -/** @addtogroup buttons Buttons - * @brief This section covers functions related to the three buttons: Light, Mode and Alarm. +/** @addtogroup buttons Buttons & External Interrupts + * @brief This section covers functions related to the three buttons: Light, Mode and Alarm, as well as + * external interrupts from devices on the nine-pin connector. * @details The buttons are the core input UI of the watch, and the way the user will interact with * your application. They are active high, pulled down by the microcontroller, and triggered * when one of the "pushers" brings a tab from the metal frame into contact with the edge - * of the board. Note that the buttons can only wake the watch from STANDBY mode (except maybe for the - * ALARM button; still working on that one). The external interrupt controller runs in - STANDBY mode, but it does not runin BACKUP mode; to wake from BACKUP, buttons will not cut it, + * of the board. Note that the buttons can only wake the watch from STANDBY mode, at least as + * of the current SAM L22 silicon revision. The external interrupt controller runs in STANDBY + * mode, but it does not run in BACKUP mode; to wake from BACKUP, buttons will not cut it. */ /// @{ -/** @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 on one of the button pins. - * @param pin One of pins BTN_LIGHT, BTN_MODE or BTN_ALARM. +///@brief An enum defining the types of interrupt trigger you wish to scan for. +typedef enum watch_interrupt_trigger { + INTERRUPT_TRIGGER_NONE = 0, + INTERRUPT_TRIGGER_RISING, + INTERRUPT_TRIGGER_FALLING, + INTERRUPT_TRIGGER_BOTH, +} watch_interrupt_trigger; + +/// @brief Enables the external interrupt controller. +void watch_enable_external_interrupts(); + +/// @brief Disables the external interrupt controller. +void watch_disable_external_interrupts(); + +/** @brief Configures an external interrupt callback on one of the external interrupt pins. + * @details You can set one interrupt callback per pin, and you can monitor for a rising condition, + * a falling condition, or both. If you just want to detect a button press, register your + * interrupt with INTERRUPT_TRIGGER_RISING; if you want to detect an active-low interrupt + * signal from a device on the nine-pin connector, use INTERRUPT_TRIGGER_FALLING. If you + * want to detect both rising and falling conditions (i.e. button down and button up), use + * INTERRUPT_TRIGGER_BOTH and use watch_get_pin_level to check the pin level in your callback + * to determine which condition caused the interrupt. + * @param pin One of pins BTN_LIGHT, BTN_MODE, BTN_ALARM, or A0-A5. If the pin parameter matches one of + * the three button pins, this function will also enable an internal pull-down resistor. If + * the pin parameter is A0-A5, you are responsible for setting any required pull configuration + * using watch_enable_pull_up or watch_enable_pull_down. * @param callback The function you wish to have called when the button is pressed. - * @note The BTN_ALARM button runs off of an interrupt in the the RTC controller, not the EIC. This - * implementation detail should not make any difference to your app, + * @param trigger The condition on which you wish to trigger: rising, falling or both. + * @note The alarm button and pin A2 share an external interrupt channel EXTINT[2]; you can only use one + * or the other. However! These pins both have an alternate method of triggering via the RTC tamper + * interrupt, which for A2 at least has the added benefit of being able to trigger in the low-power + * BACKUP mode. + * @see watch_register_extwake_callback */ +void watch_register_interrupt_callback(const uint8_t pin, ext_irq_cb_t callback, watch_interrupt_trigger trigger); + +__attribute__((deprecated("Use watch_register_interrupt_callback instead"))) void watch_register_button_callback(const uint8_t pin, ext_irq_cb_t callback); + +__attribute__((deprecated("Use watch_enable_external_interrupts instead"))) +void watch_enable_buttons(); /// @} diff --git a/watch-library/watch/watch_private.c b/watch-library/watch/watch_private.c index bfe171f1..cd9c2baa 100644 --- a/watch-library/watch/watch_private.c +++ b/watch-library/watch/watch_private.c @@ -40,5 +40,5 @@ void _watch_init() { // set up state btn_alarm_callback = NULL; a2_callback = NULL; - d1_callback = NULL; + a4_callback = NULL; } |