diff options
author | joeycastillo <joeycastillo@utexas.edu> | 2021-08-26 16:32:42 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-26 16:32:42 -0400 |
commit | 6050aff2358c1535d967e13548bf54e7358a1b29 (patch) | |
tree | 9047066f6e241b3008199241f6146439b4455a9d | |
parent | ef9736a3c96d3e345b7184c678921b5243bbbb99 (diff) | |
parent | d09d3c3c95a412ce5f5af8068205c416938510e7 (diff) | |
download | Sensor-Watch-6050aff2358c1535d967e13548bf54e7358a1b29.tar.gz Sensor-Watch-6050aff2358c1535d967e13548bf54e7358a1b29.tar.bz2 Sensor-Watch-6050aff2358c1535d967e13548bf54e7358a1b29.zip |
Merge pull request #8 from joeycastillo/external-interrupts
External interrupt refactor (closes #4, closes #5)
-rw-r--r-- | Sensor Watch Starter Project/app.c | 71 | ||||
-rw-r--r-- | watch-library/config/hpl_eic_config.h | 20 | ||||
-rw-r--r-- | watch-library/config/hpl_rtc_config.h | 6 | ||||
-rw-r--r-- | watch-library/hw/atmel_start_pins.h | 6 | ||||
-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 |
11 files changed, 250 insertions, 143 deletions
diff --git a/Sensor Watch Starter Project/app.c b/Sensor Watch Starter Project/app.c index 87326422..3fa3a618 100644 --- a/Sensor Watch Starter Project/app.c +++ b/Sensor Watch Starter Project/app.c @@ -11,17 +11,16 @@ typedef enum ApplicationMode { } ApplicationMode; typedef enum LightColor { - COLOR_OFF = 0, - COLOR_RED = 1, - COLOR_GREEN = 2, - COLOR_YELLOW = 3 + COLOR_RED = 0, + COLOR_GREEN, + COLOR_YELLOW } LightColor; typedef struct ApplicationState { ApplicationMode mode; LightColor color; + bool light_on; uint8_t wake_count; - bool debounce_wait; bool enter_deep_sleep; } ApplicationState; @@ -62,7 +61,6 @@ 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; } /** @@ -80,10 +78,18 @@ void app_wake_from_deep_sleep() { void app_setup() { watch_enable_led(false); // enable LED with plain digital IO, not PWM - watch_enable_buttons(); - watch_register_button_callback(BTN_LIGHT, cb_light_pressed); - watch_register_button_callback(BTN_MODE, cb_mode_pressed); - watch_register_button_callback(BTN_ALARM, cb_alarm_pressed); + watch_enable_external_interrupts(); + // This starter app demonstrates three different ways of using the button interrupts. + // The BTN_MODE interrupt only triggers on a rising edge, so the mode changes once per press. + watch_register_interrupt_callback(BTN_MODE, cb_mode_pressed, INTERRUPT_TRIGGER_RISING); + // The BTN_LIGHT interrupt triggers on both rising and falling edges. The callback then checks + // the pin state when triggered: on a button down event, it increments the color and turns the + // LED on, whereas on a button up event, it turns the light off. + watch_register_interrupt_callback(BTN_LIGHT, cb_light_pressed, INTERRUPT_TRIGGER_BOTH); + // The BTN_ALARM callback is on an external wake pin; we can avoid using the EIC for this pin + // by using the extwake interrupt — but note that it can only trigger on either a rising or + // a falling edge, not both. + watch_register_extwake_callback(BTN_ALARM, cb_alarm_pressed, true); watch_enable_display(); } @@ -95,7 +101,6 @@ void app_setup() { * a press on one of the buttons). */ void app_prepare_for_sleep() { - application_state.debounce_wait = false; } /** @@ -112,19 +117,20 @@ void app_wake_from_sleep() { */ bool app_loop() { // set the LED to a color - switch (application_state.color) { - case COLOR_OFF: - watch_set_led_off(); - break; - case COLOR_RED: - watch_set_led_red(); - break; - case COLOR_GREEN: - watch_set_led_green(); - break; - case COLOR_YELLOW: - watch_set_led_yellow(); - break; + if (application_state.light_on) { + switch (application_state.color) { + case COLOR_RED: + watch_set_led_red(); + break; + case COLOR_GREEN: + watch_set_led_green(); + break; + case COLOR_YELLOW: + watch_set_led_yellow(); + break; + } + } else { + watch_set_led_off(); } // Display the number of times we've woken up (modulo 32 to fit in 2 digits at top right) @@ -142,9 +148,6 @@ bool app_loop() { break; } - // Wait a moment to debounce button input - delay_ms(250); - if (application_state.enter_deep_sleep) { application_state.enter_deep_sleep = false; @@ -171,20 +174,20 @@ bool app_loop() { // Implementations for our callback functions. Replace these with whatever functionality // your app requires. void cb_light_pressed() { - if (application_state.debounce_wait) return; - application_state.debounce_wait = true; - application_state.color = (application_state.color + 1) % 4; + // always turn the light off when the pin goes low + if (watch_get_pin_level(BTN_LIGHT) == 0) { + application_state.light_on = false; + return; + } + application_state.color = (application_state.color + 1) % 3; + application_state.light_on = true; } void cb_mode_pressed() { - if (application_state.debounce_wait) return; - application_state.debounce_wait = true; application_state.mode = (application_state.mode + 1) % 2; } void cb_alarm_pressed() { - if (application_state.debounce_wait) return; - application_state.debounce_wait = 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 :( diff --git a/watch-library/config/hpl_eic_config.h b/watch-library/config/hpl_eic_config.h index 5066be66..46aba150 100644 --- a/watch-library/config/hpl_eic_config.h +++ b/watch-library/config/hpl_eic_config.h @@ -61,7 +61,7 @@ // <i> Indicates whether the external interrupt 0 filter is enabled or not // <id> eic_arch_filten0 #ifndef CONF_EIC_FILTEN0 -#define CONF_EIC_FILTEN0 0 +#define CONF_EIC_FILTEN0 1 #endif // <q> External Interrupt 0 Event Output Enable @@ -103,7 +103,7 @@ // <i> Indicates whether the external interrupt 1 filter is enabled or not // <id> eic_arch_filten1 #ifndef CONF_EIC_FILTEN1 -#define CONF_EIC_FILTEN1 0 +#define CONF_EIC_FILTEN1 1 #endif // <q> External Interrupt 1 Event Output Enable @@ -138,7 +138,7 @@ // <e> Interrupt 2 Settings // <id> eic_arch_enable_irq_setting2 #ifndef CONF_EIC_ENABLE_IRQ_SETTING2 -#define CONF_EIC_ENABLE_IRQ_SETTING2 1 +#define CONF_EIC_ENABLE_IRQ_SETTING2 0 #endif // <q> External Interrupt 2 Filter Enable @@ -165,7 +165,7 @@ // <i> This defines input sense trigger // <id> eic_arch_sense2 #ifndef CONF_EIC_SENSE2 -#define CONF_EIC_SENSE2 EIC_NMICTRL_NMISENSE_RISE_Val +#define CONF_EIC_SENSE2 EIC_NMICTRL_NMISENSE_NONE_Val #endif // <q> External Interrupt 2 Asynchronous Edge Detection Mode @@ -187,7 +187,7 @@ // <i> Indicates whether the external interrupt 3 filter is enabled or not // <id> eic_arch_filten3 #ifndef CONF_EIC_FILTEN3 -#define CONF_EIC_FILTEN3 0 +#define CONF_EIC_FILTEN3 1 #endif // <q> External Interrupt 3 Event Output Enable @@ -229,7 +229,7 @@ // <i> Indicates whether the external interrupt 4 filter is enabled or not // <id> eic_arch_filten4 #ifndef CONF_EIC_FILTEN4 -#define CONF_EIC_FILTEN4 0 +#define CONF_EIC_FILTEN4 1 #endif // <q> External Interrupt 4 Event Output Enable @@ -306,7 +306,7 @@ // <e> Interrupt 6 Settings // <id> eic_arch_enable_irq_setting6 #ifndef CONF_EIC_ENABLE_IRQ_SETTING6 -#define CONF_EIC_ENABLE_IRQ_SETTING6 1 +#define CONF_EIC_ENABLE_IRQ_SETTING6 0 #endif // <q> External Interrupt 6 Filter Enable @@ -333,7 +333,7 @@ // <i> This defines input sense trigger // <id> eic_arch_sense6 #ifndef CONF_EIC_SENSE6 -#define CONF_EIC_SENSE6 EIC_NMICTRL_NMISENSE_RISE_Val +#define CONF_EIC_SENSE6 EIC_NMICTRL_NMISENSE_NONE_Val #endif // <q> External Interrupt 6 Asynchronous Edge Detection Mode @@ -348,7 +348,7 @@ // <e> Interrupt 7 Settings // <id> eic_arch_enable_irq_setting7 #ifndef CONF_EIC_ENABLE_IRQ_SETTING7 -#define CONF_EIC_ENABLE_IRQ_SETTING7 1 +#define CONF_EIC_ENABLE_IRQ_SETTING7 0 #endif // <q> External Interrupt 7 Filter Enable @@ -375,7 +375,7 @@ // <i> This defines input sense trigger // <id> eic_arch_sense7 #ifndef CONF_EIC_SENSE7 -#define CONF_EIC_SENSE7 EIC_NMICTRL_NMISENSE_RISE_Val +#define CONF_EIC_SENSE7 EIC_NMICTRL_NMISENSE_NONE_Val #endif // <q> External Interrupt 7 Asynchronous Edge Detection Mode diff --git a/watch-library/config/hpl_rtc_config.h b/watch-library/config/hpl_rtc_config.h index 582a1c23..9085ca37 100644 --- a/watch-library/config/hpl_rtc_config.h +++ b/watch-library/config/hpl_rtc_config.h @@ -114,14 +114,14 @@ // <e> RTC Tamper Input 2 settings // <id> tamper_input_2_settings #ifndef CONF_TAMPER_INPUT_2_SETTINGS -#define CONF_TAMPER_INPUT_2_SETTINGS 1 +#define CONF_TAMPER_INPUT_2_SETTINGS 0 #endif // <q> Tamper Level Settings // <i> Indicates Tamper input 2 level // <id> tamper_level_2 #ifndef CONF_RTC_TAMP_LVL_2 -#define CONF_RTC_TAMP_LVL_2 1 +#define CONF_RTC_TAMP_LVL_2 0 #endif // <o> RTC Tamper Input Action @@ -132,7 +132,7 @@ // <i> These bits define the RTC Tamper Input Action to be performed // <id> rtc_tamper_input_action_2 #ifndef CONF_RTC_TAMPER_INACT_2 -#define CONF_RTC_TAMPER_INACT_2 1 +#define CONF_RTC_TAMPER_INACT_2 0 #endif // <q> Debounce Enable for Tamper Input diff --git a/watch-library/hw/atmel_start_pins.h b/watch-library/hw/atmel_start_pins.h index 28dc9919..7a3a7d78 100644 --- a/watch-library/hw/atmel_start_pins.h +++ b/watch-library/hw/atmel_start_pins.h @@ -44,11 +44,13 @@ #define BTN_LIGHT GPIO(GPIO_PORTA, 22) #define BTN_MODE GPIO(GPIO_PORTA, 23) #define BUZZER GPIO(GPIO_PORTA, 27) -#define D1 GPIO(GPIO_PORTB, 0) +#define A0 GPIO(GPIO_PORTB, 4) #define A1 GPIO(GPIO_PORTB, 1) #define A2 GPIO(GPIO_PORTB, 2) +#define A3 GPIO(GPIO_PORTB, 3) +#define A4 GPIO(GPIO_PORTB, 0) #define D0 GPIO(GPIO_PORTB, 3) -#define A0 GPIO(GPIO_PORTB, 4) +#define D1 GPIO(GPIO_PORTB, 0) #define BTN_ALARM GPIO(GPIO_PORTA, 2) #define COM0 GPIO(GPIO_PORTB, 6) #define COM1 GPIO(GPIO_PORTB, 7) 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; } |