work on RTC tamper interrupt and external wake
This commit is contained in:
		
							parent
							
								
									27edc50be1
								
							
						
					
					
						commit
						e45fdf15af
					
				| @ -62,6 +62,7 @@ void app_wake_from_deep_sleep() { | |||||||
|     application_state.mode = (ApplicationMode)watch_get_backup_data(0); |     application_state.mode = (ApplicationMode)watch_get_backup_data(0); | ||||||
|     application_state.color = (LightColor)watch_get_backup_data(1); |     application_state.color = (LightColor)watch_get_backup_data(1); | ||||||
|     application_state.wake_count = (uint8_t)watch_get_backup_data(2) + 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() { | void cb_alarm_pressed() { | ||||||
|     if (application_state.debounce_wait) return; |     if (application_state.debounce_wait) return; | ||||||
|     application_state.debounce_wait = true; |     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;
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ | |||||||
| // <e> RTC Tamper Input 0 settings
 | // <e> RTC Tamper Input 0 settings
 | ||||||
| // <id> tamper_input_0_settings
 | // <id> tamper_input_0_settings
 | ||||||
| #ifndef CONF_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 | #endif | ||||||
| 
 | 
 | ||||||
| // <q> Tamper Level Settings
 | // <q> Tamper Level Settings
 | ||||||
| @ -66,7 +66,7 @@ | |||||||
| // <i> These bits define the RTC Tamper Input Action to be performed
 | // <i> These bits define the RTC Tamper Input Action to be performed
 | ||||||
| // <id> rtc_tamper_input_action_0
 | // <id> rtc_tamper_input_action_0
 | ||||||
| #ifndef CONF_RTC_TAMPER_INACT_0 | #ifndef CONF_RTC_TAMPER_INACT_0 | ||||||
| #define CONF_RTC_TAMPER_INACT_0 0 | #define CONF_RTC_TAMPER_INACT_0 1 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // <q> Debounce Enable for Tamper Input
 | // <q> Debounce Enable for Tamper Input
 | ||||||
| @ -81,7 +81,7 @@ | |||||||
| // <e> RTC Tamper Input 1 settings
 | // <e> RTC Tamper Input 1 settings
 | ||||||
| // <id> tamper_input_1_settings
 | // <id> tamper_input_1_settings
 | ||||||
| #ifndef CONF_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 | #endif | ||||||
| 
 | 
 | ||||||
| // <q> Tamper Level Settings
 | // <q> Tamper Level Settings
 | ||||||
| @ -99,7 +99,7 @@ | |||||||
| // <i> These bits define the RTC Tamper Input Action to be performed
 | // <i> These bits define the RTC Tamper Input Action to be performed
 | ||||||
| // <id> rtc_tamper_input_action_1
 | // <id> rtc_tamper_input_action_1
 | ||||||
| #ifndef CONF_RTC_TAMPER_INACT_1 | #ifndef CONF_RTC_TAMPER_INACT_1 | ||||||
| #define CONF_RTC_TAMPER_INACT_1 0 | #define CONF_RTC_TAMPER_INACT_1 1 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // <q> Debounce Enable for Tamper Input
 | // <q> Debounce Enable for Tamper Input
 | ||||||
|  | |||||||
| @ -77,7 +77,8 @@ enum calendar_alarm_mode { ONESHOT = 1, REPEAT }; | |||||||
| /**
 | /**
 | ||||||
|  * \brief Prototype of callback on alarm match |  * \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 |  * \brief Structure of Calendar instance | ||||||
| @ -88,8 +89,8 @@ struct calendar_dev { | |||||||
| 	/** Alarm match callback */ | 	/** Alarm match callback */ | ||||||
| 	calendar_drv_cb_t callback_alarm; | 	calendar_drv_cb_t callback_alarm; | ||||||
| 	/** Tamper callback */ | 	/** Tamper callback */ | ||||||
| 	calendar_drv_cb_t callback_tamper; | 	calendar_drv_extwake_cb_t callback_tamper; | ||||||
| 	/** Tamper callback */ | 	/** Tick callback */ | ||||||
| 	calendar_drv_cb_t callback_tick; | 	calendar_drv_cb_t callback_tick; | ||||||
| 	/** IRQ struct */ | 	/** IRQ struct */ | ||||||
| 	struct _irq_descriptor irq; | 	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. |  * \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 |  * \brief Find tamper is detected on specified pin | ||||||
|  | |||||||
| @ -327,8 +327,7 @@ int32_t _prescaler_register_callback(struct calendar_dev *const dev, calendar_dr | |||||||
| 	return ERR_NONE; | 	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_extwake_cb_t callback) | ||||||
| int32_t _extwake_register_callback(struct calendar_dev *const dev, calendar_drv_cb_t callback) |  | ||||||
| { | { | ||||||
| 	ASSERT(dev && dev->hw); | 	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); | 	uint16_t interrupt_enabled = hri_rtcmode0_read_INTEN_reg(dev->hw); | ||||||
| 
 | 
 | ||||||
| 	if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_ALARM0) { | 	if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_ALARM0) { | ||||||
| 		dev->callback_alarm(dev); | 		dev->callback_alarm(); | ||||||
| 
 | 
 | ||||||
| 		/* Clear interrupt flag */ | 		/* Clear interrupt flag */ | ||||||
| 		hri_rtcmode0_clear_interrupt_CMP0_bit(dev->hw); | 		hri_rtcmode0_clear_interrupt_CMP0_bit(dev->hw); | ||||||
| 	} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_PER7) { | 	} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_PER7) { | ||||||
| 		dev->callback_tick(dev); | 		dev->callback_tick(); | ||||||
| 
 | 
 | ||||||
| 		/* Clear interrupt flag */ | 		/* Clear interrupt flag */ | ||||||
| 		hri_rtcmode0_clear_interrupt_PER7_bit(dev->hw); | 		hri_rtcmode0_clear_interrupt_PER7_bit(dev->hw); | ||||||
| 	} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_TAMPER) { | 	} 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 */ | 		/* Clear interrupt flag */ | ||||||
| 		hri_rtcmode0_clear_interrupt_TAMPER_bit(dev->hw); | 		hri_rtcmode0_clear_interrupt_TAMPER_bit(dev->hw); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -1,6 +1,16 @@ | |||||||
| #include "watch.h" | #include "watch.h" | ||||||
| #include <stdlib.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
 | // Initialization
 | ||||||
| 
 | 
 | ||||||
| @ -15,7 +25,14 @@ void _watch_init() { | |||||||
| 
 | 
 | ||||||
|     // Not sure if this belongs in every app -- is there a power impact?
 |     // Not sure if this belongs in every app -- is there a power impact?
 | ||||||
|     delay_driver_init(); |     delay_driver_init(); | ||||||
|  | 
 | ||||||
|  |     // set up state
 | ||||||
|  |     btn_alarm_callback = NULL; | ||||||
|  |     a2_callback = NULL; | ||||||
|  |     d1_callback = NULL; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| //////////////////////////////////////////////////////////////////////////////////////////
 | //////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| // Segmented Display
 | // Segmented Display
 | ||||||
| 
 | 
 | ||||||
| @ -215,9 +232,17 @@ void watch_enable_buttons() { | |||||||
|     EXTERNAL_IRQ_0_init(); |     EXTERNAL_IRQ_0_init(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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) { | ||||||
|  |     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); |         ext_irq_register(pin, callback); | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| //////////////////////////////////////////////////////////////////////////////////////////
 | //////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| // LED
 | // LED
 | ||||||
| @ -310,15 +335,8 @@ void watch_get_date_time(struct calendar_date_time *date_time) { | |||||||
|     calendar_get_date_time(&CALENDAR_0, 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) { | void watch_register_tick_callback(ext_irq_cb_t callback) { | ||||||
|     tick_user_callback = callback; |     _prescaler_register_callback(&CALENDAR_0.device, callback); | ||||||
|     _prescaler_register_callback(&CALENDAR_0.device, &tick_callback); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //////////////////////////////////////////////////////////////////////////////////////////
 | //////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| @ -448,6 +466,32 @@ uint32_t watch_i2c_read32(int16_t addr, uint8_t reg) { | |||||||
| //////////////////////////////////////////////////////////////////////////////////////////
 | //////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| // Deep Sleep
 | // 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) { | void watch_store_backup_data(uint32_t data, uint8_t reg) { | ||||||
|     if (reg < 8) { |     if (reg < 8) { | ||||||
|         RTC->MODE0.BKUP[reg].reg = data; |         RTC->MODE0.BKUP[reg].reg = data; | ||||||
| @ -462,14 +506,14 @@ uint32_t watch_get_backup_data(uint8_t reg) { | |||||||
|     return 0; |     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() { | void watch_enter_deep_sleep() { | ||||||
|     // enable and configure the external wake interrupt
 |     // enable and configure the external wake interrupt, if not already set up.
 | ||||||
|     _extwake_register_callback(&CALENDAR_0.device, &extwake_callback); |     if (btn_alarm_callback == NULL && a2_callback == NULL && d1_callback == NULL) { | ||||||
|     _tamper_enable_debounce_asynchronous(&CALENDAR_0.device); |         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
 |     // disable SLCD
 | ||||||
|     slcd_sync_deinit(&SEGMENT_LCD_0); |     slcd_sync_deinit(&SEGMENT_LCD_0); | ||||||
| @ -477,12 +521,6 @@ void watch_enter_deep_sleep() { | |||||||
| 
 | 
 | ||||||
|     // TODO: disable other peripherals
 |     // 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
 |     // go into backup sleep mode
 | ||||||
|     sleep(5); |     sleep(5); | ||||||
| } | } | ||||||
|  | |||||||
| @ -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 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(); | void watch_enable_buttons(); | ||||||
| 
 | 
 | ||||||
| /** @brief Configures an external interrupt
 | /** @brief Configures an external interrupt
 | ||||||
|   * @param pin One of pins BTN_LIGHT, BTN_MODE or BTN_ALARM. |   * @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. |   * @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 |   *        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 data An unsigned 32 bit integer with the data you wish to store. | ||||||
|   * @param reg A register from 0-7. |   * @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. |   *       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 |   *       Waking from backup is effectively like waking from reset, except that your @ref | ||||||
|   *       app_wake_from_deep_sleep function will be called. |   *       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(); | void watch_enter_deep_sleep(); | ||||||
| /// @}
 | /// @}
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user