diff --git a/arch/xtensa/src/esp32s2/esp32s2_touch.c b/arch/xtensa/src/esp32s2/esp32s2_touch.c index ca4d8c4278..6ddccd5f1e 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_touch.c +++ b/arch/xtensa/src/esp32s2/esp32s2_touch.c @@ -45,7 +45,7 @@ * Pre-processor Definitions ****************************************************************************/ -#define TOUCH_GET_IO_NUM(channel) (touch_channel_to_gpio[channel]) +#define TOUCH_GET_IO_NUM(channel) (touch_channel_to_rtcio[channel]) /**************************************************************************** * Private Types @@ -68,6 +68,11 @@ struct touch_config_meas_mode_s * Private Function Prototypes ****************************************************************************/ +#ifdef CONFIG_ESP32S2_TOUCH_IRQ +static void touch_ensure_scan_done_isr(uint32_t *intr_mask); +static int touch_interrupt(int irq, void *context, void *arg); +static void touch_restore_irq(void *arg); +#endif static void touch_config(enum touch_pad_e tp); static void touch_init(struct touch_config_s *config); static void touch_set_meas_mode(enum touch_pad_e tp, @@ -82,10 +87,151 @@ static void touch_io_init(enum touch_pad_e tp); static mutex_t *touch_mux = NULL; static uint32_t touch_pad_logic_threshold[TOUCH_SENSOR_PINS]; +#ifdef CONFIG_ESP32S2_TOUCH_IRQ +static uint16_t touch_pad_isr_enabled; +static enum touch_intr_mask_e touch_pad_isr_types; +static int touch_last_irq = -1; +static int (*touch_release_cb)(int, void *, void *); +static struct rt_timer_args_s irq_timer_args; +static struct rt_timer_s *irq_timer_handler; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: touch_ensure_scan_done_isr + * + * Description: + * Fix for internal scan done interrupt issue. + * Generally, the SCAN_DONE interrupt will be triggered for the last + * two enabled channels, instead of the last one, this workaround is to + * ignore the first SCAN_DONE interrupt. + * + * Input Parameters: + * intr_mask - Pointer containing a copy of the interrupt register. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_TOUCH_IRQ +void touch_ensure_scan_done_isr(uint32_t *intr_mask) +{ + uint16_t ch_mask = 0; + int pad_num = touch_lh_get_current_meas_channel(); + int i; + + /* Make sure that the scan done interrupt is generated after the last + * channel measurement is completed. + */ + + if (*intr_mask & TOUCH_INTR_MASK_SCAN_DONE) + { + ch_mask = touch_lh_get_channel_mask(); + for (i = TOUCH_SENSOR_PINS - 1; i >= 0; i--) + { + if (BIT(i) & ch_mask) + { + if (pad_num != i) + { + *intr_mask &= (~RTC_CNTL_TOUCH_SCAN_DONE_INT_ST_M); + } + + break; + } + } + } +} +#endif + +/**************************************************************************** + * Name: touch_interrupt + * + * Description: + * Touch pads interrupt handler. + * + * Input Parameters: + * irq - Interrupt request number; + * context - Context data from the ISR; + * arg - Opaque pointer to the internal driver state structure. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_TOUCH_IRQ +static int touch_interrupt(int irq, void *context, void *arg) +{ + uint32_t *context_ptr = (uint32_t *) context; + touch_ensure_scan_done_isr(context_ptr); + + uint32_t intr_mask = *(context_ptr); + enum touch_pad_e pad_num = touch_lh_get_current_meas_channel(); + touch_last_irq = ESP32S2_TOUCHPAD2IRQ(pad_num); + + touch_lh_intr_disable(touch_pad_isr_types); + + rt_timer_start(irq_timer_handler, + CONFIG_ESP32S2_TOUCH_IRQ_INTERVAL_MS * USEC_PER_MSEC, + false); + + /* Read and clear the touch interrupt status */ + + if (intr_mask & TOUCH_INTR_MASK_TIMEOUT) + { + touch_lh_timer_force_done(); + } + + if (intr_mask & (TOUCH_INTR_MASK_ACTIVE | + TOUCH_INTR_MASK_INACTIVE)) + { + if ((touch_pad_isr_enabled >> pad_num) & 0x1) + { + irq_dispatch(touch_last_irq, context); + } + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: touch_restore_irq + * + * Description: + * IRQ timer callback. + * Re-enables touch IRQ after a certain time to avoid spam. + * + * Input Parameters: + * arg - Pointer to a memory location containing the function arguments. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_TOUCH_IRQ +static void touch_restore_irq(void *arg) +{ + if (touch_last_irq > 0 && touch_release_cb != NULL) + { + /* Call the button interrup handler again so we can detect touch pad + * releases + */ + + touch_release_cb(touch_last_irq, NULL, NULL); + } + + touch_lh_intr_enable(touch_pad_isr_types); +} +#endif + /**************************************************************************** * Name: touch_init * @@ -165,6 +311,43 @@ static void touch_init(struct touch_config_s *config) touch_lh_denoise_enable(); #endif +#ifdef CONFIG_ESP32S2_TOUCH_IRQ + irq_timer_args.arg = NULL; + irq_timer_args.callback = touch_restore_irq; + rt_timer_create(&(irq_timer_args), &(irq_timer_handler)); + + touch_pad_isr_types = TOUCH_INTR_MASK_ACTIVE | + TOUCH_INTR_MASK_INACTIVE | + TOUCH_INTR_MASK_TIMEOUT; + + int ret = 0; + + ret |= irq_attach(ESP32S2_IRQ_RTC_TOUCH_DONE, + touch_interrupt, + NULL); + + ret |= irq_attach(ESP32S2_IRQ_RTC_TOUCH_ACTIVE, + touch_interrupt, + NULL); + + ret |= irq_attach(ESP32S2_IRQ_RTC_TOUCH_INACTIVE, + touch_interrupt, + NULL); + + ret |= irq_attach(ESP32S2_IRQ_RTC_TOUCH_SCAN_DONE, + touch_interrupt, + NULL); + + ret |= irq_attach(ESP32S2_IRQ_RTC_TOUCH_TIMEOUT, + touch_interrupt, + NULL); + + if (ret < 0) + { + ierr("ERROR: irq_attach() failed.\n"); + } +#endif + leave_critical_section(flags); } @@ -298,6 +481,8 @@ static void touch_io_init(enum touch_pad_e tp) int esp32s2_configtouch(enum touch_pad_e tp, struct touch_config_s config) { + DEBUGASSERT(tp < TOUCH_SENSOR_PINS); + struct touch_config_volt_s volt_config = { .refh = config.refh, @@ -313,10 +498,10 @@ int esp32s2_configtouch(enum touch_pad_e tp, struct touch_config_s config) touch_init(&config); - touch_config(tp); touch_set_meas_mode(tp, &meas_config); touch_lh_set_fsm_mode(config.fsm_mode); touch_set_voltage(&volt_config); + touch_config(tp); touch_lh_start_fsm(); return OK; @@ -338,19 +523,44 @@ int esp32s2_configtouch(enum touch_pad_e tp, struct touch_config_s config) bool esp32s2_touchread(enum touch_pad_e tp) { + DEBUGASSERT(tp < TOUCH_SENSOR_PINS); + irqstate_t flags = enter_critical_section(); + uint32_t value = esp32s2_touchreadraw(tp); + + leave_critical_section(flags); + + return (value > touch_pad_logic_threshold[tp]); +} + +/**************************************************************************** + * Name: esp32s2_touchreadraw + * + * Description: + * Read the analog value of a touch pad channel. + * + * Input Parameters: + * tp - The touch pad channel. + * + * Returned Value: + * The number of charge cycles in the last measurement. + * + ****************************************************************************/ + +uint32_t esp32s2_touchreadraw(enum touch_pad_e tp) +{ + DEBUGASSERT(tp < TOUCH_SENSOR_PINS); + #ifdef CONFIG_ESP32S2_TOUCH_FILTER uint32_t value = touch_lh_filter_read_smooth(tp); #else uint32_t value = touch_lh_read_raw_data(tp); #endif - leave_critical_section(flags); - iinfo("Touch pad %d value: %u\n", tp, value); - return (value > touch_pad_logic_threshold[tp]); + return value; } /**************************************************************************** @@ -371,11 +581,7 @@ bool esp32s2_touchread(enum touch_pad_e tp) uint32_t esp32s2_touchbenchmark(enum touch_pad_e tp) { - if (tp >= TOUCH_SENSOR_PINS) - { - ierr("Invalid touch pad!\n"); - return 0; - } + DEBUGASSERT(tp < TOUCH_SENSOR_PINS); irqstate_t flags = enter_critical_section(); @@ -405,6 +611,8 @@ uint32_t esp32s2_touchbenchmark(enum touch_pad_e tp) void esp32s2_touchsetthreshold(enum touch_pad_e tp, uint32_t threshold) { + DEBUGASSERT(tp < TOUCH_SENSOR_PINS); + irqstate_t flags = enter_critical_section(); touch_lh_set_threshold(tp, threshold); @@ -414,3 +622,78 @@ void esp32s2_touchsetthreshold(enum touch_pad_e tp, uint32_t threshold) iinfo("Touch pad %d threshold set to: %u\n", tp, threshold); } + +/**************************************************************************** + * Name: esp32s2_touchirqenable + * + * Description: + * Enable the interrupt for the specified touch pad. + * + * Input Parameters: + * irq - The touch pad irq number. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_TOUCH_IRQ +void esp32s2_touchirqenable(int irq) +{ + DEBUGASSERT(irq >= ESP32S2_FIRST_RTCIOIRQ_TOUCHPAD && + irq <= ESP32S2_LAST_RTCIOIRQ_TOUCHPAD); + + int bit = ESP32S2_IRQ2TOUCHPAD(irq); + + touch_lh_intr_disable(touch_pad_isr_types); + + touch_pad_isr_enabled |= (UINT32_C(1) << bit); + + touch_lh_intr_enable(touch_pad_isr_types); +} +#endif + +/**************************************************************************** + * Name: esp32s2_touchirqdisable + * + * Description: + * Disable the interrupt for the specified touch pad. + * + * Input Parameters: + * irq - The touch pad irq number. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_TOUCH_IRQ +void esp32s2_touchirqdisable(int irq) +{ + DEBUGASSERT(irq >= ESP32S2_FIRST_RTCIOIRQ_TOUCHPAD && + irq <= ESP32S2_LAST_RTCIOIRQ_TOUCHPAD); + + int bit = ESP32S2_IRQ2TOUCHPAD(irq); + + touch_lh_intr_disable(touch_pad_isr_types); + + touch_pad_isr_enabled &= (~(UINT32_C(1) << bit)); + + touch_lh_intr_enable(touch_pad_isr_types); +} +#endif + +/**************************************************************************** + * Name: esp32s2_touchregisterreleasecb + * + * Description: + * Register the release callback. + * + ****************************************************************************/ + +void esp32s2_touchregisterreleasecb(int (*func)(int, void *, void *)) +{ + DEBUGASSERT(func != NULL); + + touch_release_cb = func; +} diff --git a/arch/xtensa/src/esp32s2/esp32s2_touch.h b/arch/xtensa/src/esp32s2/esp32s2_touch.h index 73c1a0fbf2..80305813d7 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_touch.h +++ b/arch/xtensa/src/esp32s2/esp32s2_touch.h @@ -85,23 +85,23 @@ struct touch_config_s * external GPIO. */ -static const int touch_channel_to_gpio[] = +static const int touch_channel_to_rtcio[] = { -1, - TOUCH_PAD_NUM1_GPIO_NUM, - TOUCH_PAD_NUM2_GPIO_NUM, - TOUCH_PAD_NUM3_GPIO_NUM, - TOUCH_PAD_NUM4_GPIO_NUM, - TOUCH_PAD_NUM5_GPIO_NUM, - TOUCH_PAD_NUM6_GPIO_NUM, - TOUCH_PAD_NUM7_GPIO_NUM, - TOUCH_PAD_NUM8_GPIO_NUM, - TOUCH_PAD_NUM9_GPIO_NUM, - TOUCH_PAD_NUM10_GPIO_NUM, - TOUCH_PAD_NUM11_GPIO_NUM, - TOUCH_PAD_NUM12_GPIO_NUM, - TOUCH_PAD_NUM13_GPIO_NUM, - TOUCH_PAD_NUM14_GPIO_NUM + TOUCH_PAD_NUM1_CHANNEL_NUM, + TOUCH_PAD_NUM2_CHANNEL_NUM, + TOUCH_PAD_NUM3_CHANNEL_NUM, + TOUCH_PAD_NUM4_CHANNEL_NUM, + TOUCH_PAD_NUM5_CHANNEL_NUM, + TOUCH_PAD_NUM6_CHANNEL_NUM, + TOUCH_PAD_NUM7_CHANNEL_NUM, + TOUCH_PAD_NUM8_CHANNEL_NUM, + TOUCH_PAD_NUM9_CHANNEL_NUM, + TOUCH_PAD_NUM10_CHANNEL_NUM, + TOUCH_PAD_NUM11_CHANNEL_NUM, + TOUCH_PAD_NUM12_CHANNEL_NUM, + TOUCH_PAD_NUM13_CHANNEL_NUM, + TOUCH_PAD_NUM14_CHANNEL_NUM }; #undef EXTERN @@ -154,6 +154,22 @@ int esp32s2_configtouch(enum touch_pad_e tp, struct touch_config_s config); bool esp32s2_touchread(enum touch_pad_e tp); +/**************************************************************************** + * Name: esp32s2_touchreadraw + * + * Description: + * Read the analog value of a touch pad channel. + * + * Input Parameters: + * tp - The touch pad channel. + * + * Returned Value: + * The number of charge cycles in the last measurement. + * + ****************************************************************************/ + +uint32_t esp32s2_touchreadraw(enum touch_pad_e tp); + /**************************************************************************** * Name: esp32s2_touchbenchmark * @@ -187,6 +203,64 @@ uint32_t esp32s2_touchbenchmark(enum touch_pad_e tp); void esp32s2_touchsetthreshold(enum touch_pad_e tp, uint32_t threshold); +/**************************************************************************** + * Name: esp32s2_touchirqenable + * + * Description: + * Enable the interrupt for the specified touch pad. + * + * Input Parameters: + * irq - The touch pad irq number. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_TOUCH_IRQ +void esp32s2_touchirqenable(int irq); +#else +# define esp32s2_touchirqenable(irq) +#endif + +/**************************************************************************** + * Name: esp32s2_touchirqdisable + * + * Description: + * Disable the interrupt for the specified touch pad. + * + * Input Parameters: + * irq - The touch pad irq number. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_TOUCH_IRQ +void esp32s2_touchirqdisable(int irq); +#else +# define esp32s2_touchirqdisable(irq) +#endif + +/**************************************************************************** + * Name: esp32s2_touchregisterreleasecb + * + * Description: + * Register the release callback. + * + * Input Parameters: + * func - The handler function to be used. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S2_TOUCH_IRQ +void esp32s2_touchregisterreleasecb(int (*func)(int, void *, void *)); +#endif + #ifdef __cplusplus } #endif diff --git a/arch/xtensa/src/esp32s2/hardware/esp32s2_touch.h b/arch/xtensa/src/esp32s2/hardware/esp32s2_touch.h index f818410837..93f4977c85 100644 --- a/arch/xtensa/src/esp32s2/hardware/esp32s2_touch.h +++ b/arch/xtensa/src/esp32s2/hardware/esp32s2_touch.h @@ -31,6 +31,7 @@ #define TOUCH_SENSOR_PINS 15 #define TOUCH_MEASURE_WAIT_MAX (0xff) +#define TOUCH_THRESHOLD_NO_USE (0) /* The waterproof function includes a shielded channel (TOUCH_PAD_NUM14) */ @@ -50,6 +51,21 @@ * external GPIO. */ +#define TOUCH_PAD_NUM1_CHANNEL_NUM 1 +#define TOUCH_PAD_NUM2_CHANNEL_NUM 2 +#define TOUCH_PAD_NUM3_CHANNEL_NUM 3 +#define TOUCH_PAD_NUM4_CHANNEL_NUM 4 +#define TOUCH_PAD_NUM5_CHANNEL_NUM 5 +#define TOUCH_PAD_NUM6_CHANNEL_NUM 6 +#define TOUCH_PAD_NUM7_CHANNEL_NUM 7 +#define TOUCH_PAD_NUM8_CHANNEL_NUM 8 +#define TOUCH_PAD_NUM9_CHANNEL_NUM 9 +#define TOUCH_PAD_NUM10_CHANNEL_NUM 10 +#define TOUCH_PAD_NUM11_CHANNEL_NUM 11 +#define TOUCH_PAD_NUM12_CHANNEL_NUM 12 +#define TOUCH_PAD_NUM13_CHANNEL_NUM 13 +#define TOUCH_PAD_NUM14_CHANNEL_NUM 14 + #define TOUCH_PAD_NUM1_GPIO_NUM 1 #define TOUCH_PAD_NUM2_GPIO_NUM 2 #define TOUCH_PAD_NUM3_GPIO_NUM 3