diff options
| -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;  } | 
