diff --git a/README.md b/README.md index e36d805..d8f38e9 100644 --- a/README.md +++ b/README.md @@ -15,20 +15,6 @@ 1. After start/reset first touch not work. 2. Multi touch not supported. -## Note - -Enable the following settings in menuconfig: - -```text -CONFIG_GPIO_CTRL_FUNC_IN_IRAM -CONFIG_RMT_ENCODER_FUNC_IN_IRAM -CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM -CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM -CONFIG_RMT_RECV_FUNC_IN_IRAM -CONFIG_RMT_TX_ISR_CACHE_SAFE -CONFIG_RMT_RX_ISR_CACHE_SAFE -``` - ## Using In an existing project, run the following command to install the components: @@ -58,6 +44,7 @@ void zh_ttp229_event_handler(void *arg, esp_event_base_t event_base, int32_t eve void app_main(void) { esp_log_level_set("zh_ttp229", ESP_LOG_ERROR); + esp_log_level_set("zh_vector", ESP_LOG_ERROR); esp_event_loop_create_default(); esp_event_handler_instance_register(ZH_TTP229, ESP_EVENT_ANY_ID, &zh_ttp229_event_handler, NULL, NULL); zh_ttp229_init_config_t config = ZH_TTP229_INIT_CONFIG_DEFAULT(); diff --git a/include/zh_ttp229.h b/include/zh_ttp229.h index 2b92b1c..7f51c68 100644 --- a/include/zh_ttp229.h +++ b/include/zh_ttp229.h @@ -7,14 +7,14 @@ * This module provides a FreeRTOS-based driver for the TTP229 touch * controller. It uses the RMT transmitter to generate SCL clock pulses * and the RMT receiver to capture SDO data, enabling reading of touch - * status from 8 or 16 pad configurations. Each touch event triggers an - * ISR that initiates RMT transfer and posts an event with the touched - * pad number. + * status from 8 or 16 pad configurations. A positive edge on SDO triggers + * an ISR that starts the RMT transfer; a dedicated FreeRTOS task then + * decodes the touched pad number and posts an event with it. * * Key features: * - Supports 8-pad and 16-pad TTP229 configurations * - RMT-based SPI-like communication (no GPIO bit-banging) - * - FreeRTOS task for debouncing and event dispatching + * - FreeRTOS task for decoding, debouncing and event dispatching * - Per-device unique numbering for multi-device support * - Error statistics tracking (RMT, queue, stack) */ @@ -22,6 +22,7 @@ #pragma once #include "esp_log.h" +#include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/rmt_tx.h" @@ -34,11 +35,11 @@ { \ .task_priority = 1, \ .stack_size = configMINIMAL_STACK_SIZE, \ - .queue_size = 1, \ + .queue_size = 5, \ .work_mode = ZH_TTP229_8_PAD, \ - .rmt_tx_start_delay = 0, \ + .rmt_tx_start_delay = 15, \ .debounce_time = 100, \ - .device_number = 0, \ + .device_number = 1, \ .scl_gpio = GPIO_NUM_MAX, \ .sdo_gpio = GPIO_NUM_MAX} @@ -48,7 +49,11 @@ extern "C" #endif /** - * @brief External task handle for the TTP229 ISR processing task. + * @brief Handle of the shared TTP229 event processing task. + * + * Created when the first device is initialized and set back to NULL + * when the last device is deinitialized or the first device + * initialization fails. */ extern TaskHandle_t zh_ttp229; @@ -124,42 +129,44 @@ extern "C" /** * @brief Initialize the TTP229 touch pad driver. * - * Allocates a handle, validates the configuration, initializes RMT - * TX/RMT channels, installs the GPIO ISR handler, and creates the - * processing task. Supports multiple devices via unique device - * numbers. + * Allocates a handle, validates the configuration, creates the shared + * queue and the processing task on the first device, initializes GPIO + * with the ISR handler, and creates the RMT TX/RX channels. Supports + * multiple devices via unique device numbers. * * @param[in] config Pointer to initialization configuration (must not be NULL) - * @param[out] handle Pointer to unique touch pad handle (must not be NULL) + * @param[out] handle Pointer to a handle location that must be NULL on input (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if config or handle is NULL, or if any configuration value is out of range - * @return ESP_ERR_INVALID_STATE if the device is already initialized - * @return ESP_ERR_NO_MEM if memory allocation fails - * @return ESP_FAIL on resource allocation or peripheral failure + * @return ESP_ERR_INVALID_STATE if *handle is not NULL + * @return ESP_ERR_NO_MEM if handle allocation fails + * @return ESP_FAIL if creating the shared resources fails, or on GPIO or RMT initialization failure */ esp_err_t zh_ttp229_init(const zh_ttp229_init_config_t *config, zh_ttp229_handle_t **handle); /** * @brief Deinitialize a TTP229 touch pad instance. * - * Disables RMT channels, removes the GPIO ISR handler, frees the - * handle, and removes the device from the internal device list. - * The processing task and shared resources are deleted only when - * the last device is deinitialized. + * Removes the device from the internal device list, removes the GPIO + * ISR handler, disables and deletes the RMT channels and the encoder, + * resets the GPIO pins, and frees the handle. The processing task, + * the queue, and the device list are deleted only when the last + * device is deinitialized. * * @param[in,out] handle Pointer to unique touch pad handle (must not be NULL) * * @return ESP_OK on success - * @return ESP_ERR_INVALID_ARG if handle is NULL or points to a device not found in the internal list + * @return ESP_ERR_INVALID_ARG if handle or *handle is NULL + * @return ESP_FAIL if the device is not found in the internal device list, or if deleting the device list entries or the shared resources fails */ esp_err_t zh_ttp229_deinit(zh_ttp229_handle_t **handle); /** * @brief Retrieve a pointer to the current error statistics. * - * The returned pointer remains valid until zh_ttp229_reset_stats() - * is called or the module is deinitialized. + * The returned pointer refers to a module static structure and + * remains valid for the lifetime of the application. * * @return Pointer to the static zh_ttp229_stats_t structure */ diff --git a/zh_ttp229.c b/zh_ttp229.c index 4c325e9..1546a85 100644 --- a/zh_ttp229.c +++ b/zh_ttp229.c @@ -13,12 +13,19 @@ static const char *TAG = "zh_ttp229"; return err; \ } +#define ZH_ERROR_CHECK_VOID(cond, cleanup, msg, ...) \ + if (!(cond)) \ + { \ + ZH_LOGE(msg, ESP_FAIL, ##__VA_ARGS__); \ + cleanup; \ + return; \ + } + #define ZH_ERROR_CHECK_CONT(cond, cleanup, msg, ...) \ if (!(cond)) \ { \ ZH_LOGE(msg, ESP_FAIL, ##__VA_ARGS__); \ cleanup; \ - continue; \ } /** @@ -30,11 +37,15 @@ static const char *TAG = "zh_ttp229"; struct _zh_ttp229_handle_t { gpio_num_t sdo_gpio; /*!< SDO (Serial Data Out) GPIO pin for touch data */ + gpio_num_t scl_gpio; /*!< SCL (Serial Clock) GPIO pin for clock generation */ uint8_t device_number; /*!< Unique device number assigned at initialization */ uint8_t rmt_tx_start_delay; /*!< Time between SDO low-level pulse and RMT TX start (in microseconds) */ + uint8_t scl_symbols_count; /*!< Number of SCL clock symbols to transmit (8 or 16) */ uint16_t debounce_time; /*!< Button debounce time in milliseconds */ - zh_ttp229_work_mode_t work_mode; /*!< Touch pad configuration (8 or 16 pads) */ - rmt_symbol_word_t rx_symbols; /*!< Received RMT symbol containing duration1 (pad data) */ + TickType_t last_event_tick; /*!< Tick count of the last posted event, (TickType_t)-1 before first event */ + volatile bool rx_needs_reset; /*!< Flag requesting RMT RX channel reset, set from ISR context */ + rmt_symbol_word_t scl_symbols[16]; /*!< Precomputed SCL clock symbols for RMT TX (up to 16 pads) */ + rmt_symbol_word_t rx_symbols; /*!< Single RMT RX symbol buffer captured from the SDO line */ rmt_channel_handle_t tx_channel; /*!< RMT TX channel handle (SCL clock generation) */ rmt_channel_handle_t rx_channel; /*!< RMT RX channel handle (SDO data capture) */ rmt_encoder_handle_t copy_encoder; /*!< Copy encoder handle for RMT TX */ @@ -44,23 +55,25 @@ struct _zh_ttp229_handle_t * @brief Queue entry passed from RMT RX callback to the ISR processing task. * * Contains a pointer to the TTP229 device handle for the device - * that triggered the interrupt. + * that triggered the interrupt and the first RMT symbol captured + * from the SDO line. */ typedef struct { - zh_ttp229_handle_t *handle; /*!< Pointer to the TTP229 device handle */ + zh_ttp229_handle_t *handle; /*!< Pointer to the TTP229 device handle */ + rmt_symbol_word_t rx_symbol; /*!< RMT symbol word captured from the SDO line */ } zh_ttp229_queue_t; TaskHandle_t zh_ttp229 = NULL; /*!< FreeRTOS task handle for the ISR processing task */ -static QueueHandle_t _queue_handle = NULL; /*!< Internal event queue */ +static QueueHandle_t _queue_handle = NULL; /*!< Internal event queue for RMT RX notifications */ static zh_ttp229_stats_t _stats = {0}; /*!< Accumulated error and runtime statistics */ -static zh_vector_t *_vector = NULL; /*!< Vector of device_number values for all initialized TTP229 devices */ +static zh_vector_t *_vector = NULL; /*!< Vector of device number values for all initialized TTP229 devices */ /** * @brief Validate the user-provided initialization configuration. * * Checks all parameter ranges, ensures work_mode is valid, and - * verifies device_number uniqueness across initialized devices. + * verifies device number uniqueness across initialized devices. * * @param config Pointer to the user configuration * @param handle Pointer to the allocated handle (partially filled) @@ -73,11 +86,12 @@ static esp_err_t _zh_ttp229_validate_config(const zh_ttp229_init_config_t *confi /** * @brief Configure GPIO for SDO input with positive-edge interrupt. * - * Installs the ISR service if not yet installed, configures SDO as - * input with GPIO_INTR_POSEDGE, and registers the ISR handler. + * Checks that both GPIO pins are valid and different, installs the ISR + * service if not yet installed, configures SDO as input with + * GPIO_INTR_POSEDGE, and registers the ISR handler. * * @param config Pointer to the user configuration - * @param handle Pointer to the handle (stores sdo_gpio) + * @param handle Pointer to the handle (stores sdo_gpio and scl_gpio) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if GPIO pins are invalid or identical @@ -124,12 +138,23 @@ static esp_err_t _zh_ttp229_resources_init(const zh_ttp229_init_config_t *config */ static esp_err_t _zh_ttp229_task_init(const zh_ttp229_init_config_t *config); +/** + * @brief Roll back shared resources (queue, task) if the failed init was + * the first (and only) device. + * + * Must be called BEFORE zh_vector_delete_back(&_vector) — otherwise the + * vector size will no longer reflect "first device". + */ +static void _zh_ttp229_rollback_task_and_resources(void); + /** * @brief GPIO ISR handler triggered on SDO positive edge. * - * Disables further interrupts, starts RMT receive on SDO, generates - * SCL clock pulses via RMT transmit on SCL, and yields to higher - * priority tasks. + * Disables further GPIO interrupts, arms RMT receive on SDO, emits the + * SDO start pulse, and issues the SCL clock pulses via RMT transmit on + * SCL. On a transmit failure the RX reset flag is set and a notify entry + * is enqueued so the processing task can recover the RX channel; GPIO + * interrupts are re-enabled on any error path. * * @param arg Pointer to the zh_ttp229_handle_t for this device */ @@ -138,10 +163,12 @@ static void _zh_ttp229_isr_handler(void *arg); /** * @brief FreeRTOS task that processes RMT RX data and posts events. * - * Receives queue entries from the RMT RX callback, decodes the pad - * number from rx_symbols.duration1, posts a ZH_TTP229 event for - * detected touches, debounces by delaying, then re-enables GPIO - * interrupts. Runs until the queue is deleted. + * Receives queue entries from the RMT RX callback, resets the RX channel + * when flagged, decodes the pad number from the entry's rx_symbol + * duration1, and posts a ZH_TTP229 event for a detected touch. Touches + * within the debounce window are dropped by comparing tick counts rather + * than delaying. Re-enables GPIO interrupts after each entry and runs + * until the queue is deleted. * * @param pvParameter Unused */ @@ -150,8 +177,10 @@ static void _zh_ttp229_isr_processing_task(void *pvParameter); /** * @brief RMT RX done callback that enqueues data to the processing task. * - * Sends a zh_ttp229_queue_t entry to _queue_handle. Increments - * queue overflow error counter if the send fails. + * Returns early when the queue is not yet created. Otherwise copies the + * received symbol into a zh_ttp229_queue_t entry and sends it to + * _queue_handle. On a full queue the overflow error counter is incremented + * and GPIO interrupts are re-enabled so the device is not left stalled. * * @param channel RMT channel handle (unused) * @param edata RX done event data (unused) @@ -171,31 +200,24 @@ esp_err_t zh_ttp229_init(const zh_ttp229_init_config_t *config, zh_ttp229_handle ZH_ERROR_CHECK(*handle == NULL, ESP_ERR_INVALID_STATE, NULL, "Touch pad initialization failed. Touch pad is already initialized."); *handle = heap_caps_calloc(1, sizeof(zh_ttp229_handle_t), MALLOC_CAP_8BIT); ZH_ERROR_CHECK(*handle != NULL, ESP_ERR_NO_MEM, NULL, "Touch pad initialization failed. Failed to allocate touch pad handle."); - ZH_ERROR_CHECK(_zh_ttp229_validate_config(config, *handle) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. Initial configuration check failed."); + ZH_ERROR_CHECK(_zh_ttp229_validate_config(config, *handle) == ESP_OK, ESP_ERR_INVALID_ARG, heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. Initial configuration check failed."); if (_vector == NULL) { ZH_ERROR_CHECK(zh_vector_init(&_vector, (uint16_t)sizeof(uint8_t)) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. Failed to create vector."); } ZH_ERROR_CHECK(zh_vector_push_back(&_vector, &config->device_number) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. Failed to add vector data."); - ZH_ERROR_CHECK(_zh_ttp229_resources_init(config) == ESP_OK, ESP_FAIL, zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. Resources initialization failed."); - // clang-format off - ZH_ERROR_CHECK(_zh_ttp229_task_init(config) == ESP_OK, ESP_FAIL, - zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. Processing task initialization failed."); - ZH_ERROR_CHECK(_zh_ttp229_rmt_init(config, *handle) == ESP_OK, ESP_FAIL, - zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. RMT initialization failed."); - // clang-format on - ZH_ERROR_CHECK(_zh_ttp229_gpio_init(config, *handle) == ESP_OK, ESP_FAIL, - {ZH_ERROR_CHECK(rmt_disable((*handle)->tx_channel) == ESP_OK, ESP_FAIL, NULL, "TX channel disable failed.")}; - {ZH_ERROR_CHECK(rmt_disable((*handle)->rx_channel) == ESP_OK, ESP_FAIL, NULL, "RX channel disable failed.")}; - {ZH_ERROR_CHECK(rmt_del_encoder((*handle)->copy_encoder) == ESP_OK, ESP_FAIL, NULL, "Delete encoder failed.")}; - {ZH_ERROR_CHECK(rmt_del_channel((*handle)->tx_channel) == ESP_OK, ESP_FAIL, NULL, "Delete TX channel failed.")}; - {ZH_ERROR_CHECK(rmt_del_channel((*handle)->rx_channel) == ESP_OK, ESP_FAIL, NULL, "Delete RX channel failed.")}; - zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. GPIO initialization failed."); + ZH_ERROR_CHECK(_zh_ttp229_resources_init(config) == ESP_OK, ESP_FAIL, _zh_ttp229_rollback_task_and_resources(); zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. Resources initialization failed."); + ZH_ERROR_CHECK(_zh_ttp229_task_init(config) == ESP_OK, ESP_FAIL, _zh_ttp229_rollback_task_and_resources(); zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. Processing task initialization failed."); + (*handle)->debounce_time = config->debounce_time; + (*handle)->last_event_tick = (TickType_t)-1; + ZH_ERROR_CHECK(_zh_ttp229_gpio_init(config, *handle) == ESP_OK, ESP_FAIL, _zh_ttp229_rollback_task_and_resources(); zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. GPIO initialization failed."); + ZH_ERROR_CHECK(_zh_ttp229_rmt_init(config, *handle) == ESP_OK, ESP_FAIL, gpio_isr_handler_remove((*handle)->sdo_gpio); gpio_reset_pin((*handle)->sdo_gpio); gpio_reset_pin((*handle)->scl_gpio); + _zh_ttp229_rollback_task_and_resources(); zh_vector_delete_back(&_vector); heap_caps_free(*handle); *handle = NULL, "Touch pad initialization failed. RMT initialization failed."); if (_stats.min_stack_size == 0) { _stats.min_stack_size = config->stack_size; } - (*handle)->debounce_time = config->debounce_time; + (*handle)->rx_needs_reset = false; ZH_LOGI("Touch pad initialization completed successfully."); return ESP_OK; } @@ -204,31 +226,34 @@ esp_err_t zh_ttp229_deinit(zh_ttp229_handle_t **handle) { ZH_LOGI("Touch pad deinitialization started."); ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Touch pad deinitialization failed. Invalid argument."); - ZH_ERROR_CHECK(rmt_disable((*handle)->tx_channel) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. TX channel disable failed."); - ZH_ERROR_CHECK(rmt_disable((*handle)->rx_channel) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. RX channel disable failed."); - ZH_ERROR_CHECK(rmt_del_encoder((*handle)->copy_encoder) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. Delete encoder failed."); - ZH_ERROR_CHECK(rmt_del_channel((*handle)->tx_channel) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. Delete TX channel failed."); - ZH_ERROR_CHECK(rmt_del_channel((*handle)->rx_channel) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. Delete RX channel failed."); - ZH_ERROR_CHECK(gpio_isr_handler_remove((*handle)->sdo_gpio) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. Remove GPIO isr handler failed."); - ZH_ERROR_CHECK(gpio_reset_pin((*handle)->sdo_gpio) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. Reset GPIO failed."); int32_t index = 0; - ZH_ERROR_CHECK(zh_vector_find_item(&_vector, &(*handle)->device_number, &index) == ESP_ERR_NOT_FOUND, ESP_ERR_INVALID_ARG, NULL, "Touch pad deinitialization failed. Failed to find vector item."); - ZH_ERROR_CHECK(zh_vector_delete_item(&_vector, (uint16_t)index) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. Vector delete item failed."); + ZH_ERROR_CHECK(zh_vector_find_item(&_vector, &(*handle)->device_number, &index) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. Failed to find vector item."); + ZH_ERROR_CHECK_CONT(zh_vector_delete_item(&_vector, (uint16_t)index) == ESP_OK, NULL, "Touch pad deinitialization failed. Vector delete item failed."); + ZH_ERROR_CHECK_CONT(gpio_isr_handler_remove((*handle)->sdo_gpio) == ESP_OK, NULL, "Touch pad deinitialization failed. Remove GPIO isr handler failed."); + ZH_ERROR_CHECK_CONT(rmt_disable((*handle)->tx_channel) == ESP_OK, NULL, "Touch pad deinitialization failed. TX channel disable failed."); + ZH_ERROR_CHECK_CONT(rmt_disable((*handle)->rx_channel) == ESP_OK, NULL, "Touch pad deinitialization failed. RX channel disable failed."); + ZH_ERROR_CHECK_CONT(rmt_del_encoder((*handle)->copy_encoder) == ESP_OK, NULL, "Touch pad deinitialization failed. Delete encoder failed."); + ZH_ERROR_CHECK_CONT(rmt_del_channel((*handle)->tx_channel) == ESP_OK, NULL, "Touch pad deinitialization failed. Delete TX channel failed."); + ZH_ERROR_CHECK_CONT(rmt_del_channel((*handle)->rx_channel) == ESP_OK, NULL, "Touch pad deinitialization failed. Delete RX channel failed."); + ZH_ERROR_CHECK_CONT(gpio_reset_pin((*handle)->sdo_gpio) == ESP_OK, NULL, "Touch pad deinitialization failed. Reset GPIO failed."); + ZH_ERROR_CHECK_CONT(gpio_reset_pin((*handle)->scl_gpio) == ESP_OK, NULL, "Touch pad deinitialization failed. Reset GPIO failed."); + (*handle)->rx_channel = NULL; + (*handle)->tx_channel = NULL; uint16_t vector_size = 0; - ZH_ERROR_CHECK(zh_vector_get_size(&_vector, &vector_size) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. Failed to get vector size."); + ZH_ERROR_CHECK(zh_vector_get_size(&_vector, &vector_size) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "Touch pad deinitialization failed. Failed to get vector size."); if (vector_size == 0) { - if (_queue_handle != NULL) - { - vQueueDelete(_queue_handle); - _queue_handle = NULL; - } if (zh_ttp229 != NULL) { vTaskDelete(zh_ttp229); zh_ttp229 = NULL; } - ZH_ERROR_CHECK(zh_vector_free(&_vector) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. Free vector failed."); + if (_queue_handle != NULL) + { + vQueueDelete(_queue_handle); + _queue_handle = NULL; + } + ZH_ERROR_CHECK(zh_vector_free(&_vector) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "Touch pad deinitialization failed. Free vector failed."); } heap_caps_free(*handle); *handle = NULL; @@ -266,14 +291,13 @@ static esp_err_t _zh_ttp229_validate_config(const zh_ttp229_init_config_t *confi } handle->rmt_tx_start_delay = config->rmt_tx_start_delay; handle->device_number = config->device_number; - handle->work_mode = config->work_mode; return ESP_OK; } static esp_err_t _zh_ttp229_gpio_init(const zh_ttp229_init_config_t *config, zh_ttp229_handle_t *handle) { - ZH_ERROR_CHECK(config->scl_gpio < GPIO_NUM_MAX && config->sdo_gpio < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG, NULL, "Invalid GPIO number.") - ZH_ERROR_CHECK(config->scl_gpio != config->sdo_gpio, ESP_ERR_INVALID_ARG, NULL, "SCL GPIO and SDO GPIO is same.") + ZH_ERROR_CHECK(config->scl_gpio < GPIO_NUM_MAX && config->sdo_gpio < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG, NULL, "Invalid GPIO number."); + ZH_ERROR_CHECK(config->scl_gpio != config->sdo_gpio, ESP_ERR_INVALID_ARG, NULL, "SCL GPIO and SDO GPIO is same."); gpio_config_t sdo_config = { .mode = GPIO_MODE_INPUT, .pin_bit_mask = (1ULL << config->sdo_gpio), @@ -281,10 +305,11 @@ static esp_err_t _zh_ttp229_gpio_init(const zh_ttp229_init_config_t *config, zh_ ZH_ERROR_CHECK(gpio_config(&sdo_config) == ESP_OK, ESP_FAIL, NULL, "GPIO initialization failed."); esp_err_t err = gpio_install_isr_service(ESP_INTR_FLAG_LOWMED); ZH_ERROR_CHECK(err == ESP_OK || err == ESP_ERR_INVALID_STATE, ESP_FAIL, - {ZH_ERROR_CHECK(gpio_reset_pin(config->sdo_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset GPIO failed.")}, "Failed install isr service."); + {ZH_ERROR_CHECK_CONT(gpio_reset_pin(config->sdo_gpio) == ESP_OK, NULL, "Reset GPIO failed.")}, "Failed install isr service."); ZH_ERROR_CHECK(gpio_isr_handler_add(config->sdo_gpio, _zh_ttp229_isr_handler, handle) == ESP_OK, ESP_FAIL, - {ZH_ERROR_CHECK(gpio_reset_pin(config->sdo_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset GPIO failed.")}, "Interrupt initialization failed."); + {ZH_ERROR_CHECK_CONT(gpio_reset_pin(config->sdo_gpio) == ESP_OK, NULL, "Reset GPIO failed.")}, "Interrupt initialization failed."); handle->sdo_gpio = config->sdo_gpio; + handle->scl_gpio = config->scl_gpio; return ESP_OK; } @@ -293,50 +318,60 @@ static esp_err_t _zh_ttp229_rmt_init(const zh_ttp229_init_config_t *config, zh_t rmt_tx_channel_config_t tx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT, .gpio_num = config->scl_gpio, -#if defined CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) .mem_block_symbols = 64, #else .mem_block_symbols = 48, #endif .resolution_hz = 1000000, - .trans_queue_depth = 4, + .trans_queue_depth = 1, .flags.init_level = 1, }; ZH_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &handle->tx_channel) == ESP_OK, ESP_FAIL, NULL, "TX channel creation failed."); rmt_copy_encoder_config_t copy_encoder_config = {}; ZH_ERROR_CHECK(rmt_new_copy_encoder(©_encoder_config, &handle->copy_encoder) == ESP_OK, ESP_FAIL, - {ZH_ERROR_CHECK(rmt_del_channel(handle->tx_channel) == ESP_OK, ESP_FAIL, NULL, "Delete channel failed.")}, "Copy encoder creation failed."); + {ZH_ERROR_CHECK_CONT(rmt_del_channel(handle->tx_channel) == ESP_OK, NULL, "Delete channel failed.")}, "Copy encoder creation failed."); ZH_ERROR_CHECK(rmt_enable(handle->tx_channel) == ESP_OK, ESP_FAIL, - {ZH_ERROR_CHECK(rmt_del_encoder(handle->copy_encoder) == ESP_OK, ESP_FAIL, NULL, "Delete encoder failed.")}; - {ZH_ERROR_CHECK(rmt_del_channel(handle->tx_channel) == ESP_OK, ESP_FAIL, NULL, "Delete channel failed.")}, "Enable TX channel failed."); + {ZH_ERROR_CHECK_CONT(rmt_del_encoder(handle->copy_encoder) == ESP_OK, NULL, "Delete encoder failed.")}; + {ZH_ERROR_CHECK_CONT(rmt_del_channel(handle->tx_channel) == ESP_OK, NULL, "Delete channel failed.")}, "Enable TX channel failed."); rmt_rx_channel_config_t rx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT, .gpio_num = config->sdo_gpio, -#if defined CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) .mem_block_symbols = 64, #else .mem_block_symbols = 48, #endif .resolution_hz = 1000000, }; + handle->scl_symbols_count = (uint8_t)config->work_mode; + for (uint8_t i = 0; i < handle->scl_symbols_count; ++i) + { + handle->scl_symbols[i] = (rmt_symbol_word_t){ + .duration0 = 2, + .level0 = 0, + .duration1 = 2, + .level1 = 1, + }; + } ZH_ERROR_CHECK(rmt_new_rx_channel(&rx_chan_config, &handle->rx_channel) == ESP_OK, ESP_FAIL, - {ZH_ERROR_CHECK(rmt_disable(handle->tx_channel) == ESP_OK, ESP_FAIL, NULL, "TX channel disable failed.")}; - {ZH_ERROR_CHECK(rmt_del_encoder(handle->copy_encoder) == ESP_OK, ESP_FAIL, NULL, "Delete encoder failed.")}; - {ZH_ERROR_CHECK(rmt_del_channel(handle->tx_channel) == ESP_OK, ESP_FAIL, NULL, "Delete TX channel failed.")}, "TX channel creation failed."); + {ZH_ERROR_CHECK_CONT(rmt_disable(handle->tx_channel) == ESP_OK, NULL, "TX channel disable failed.")}; + {ZH_ERROR_CHECK_CONT(rmt_del_encoder(handle->copy_encoder) == ESP_OK, NULL, "Delete encoder failed.")}; + {ZH_ERROR_CHECK_CONT(rmt_del_channel(handle->tx_channel) == ESP_OK, NULL, "Delete TX channel failed.")}, "RX channel creation failed."); ZH_ERROR_CHECK(rmt_enable(handle->rx_channel) == ESP_OK, ESP_FAIL, - {ZH_ERROR_CHECK(rmt_disable(handle->tx_channel) == ESP_OK, ESP_FAIL, NULL, "TX channel disable failed.")}; - {ZH_ERROR_CHECK(rmt_del_encoder(handle->copy_encoder) == ESP_OK, ESP_FAIL, NULL, "Delete encoder failed.")}; - {ZH_ERROR_CHECK(rmt_del_channel(handle->tx_channel) == ESP_OK, ESP_FAIL, NULL, "Delete TX channel failed.")}; - {ZH_ERROR_CHECK(rmt_del_channel(handle->rx_channel) == ESP_OK, ESP_FAIL, NULL, "Delete RX channel failed.")}, "RX channel creation failed."); + {ZH_ERROR_CHECK_CONT(rmt_disable(handle->tx_channel) == ESP_OK, NULL, "TX channel disable failed.")}; + {ZH_ERROR_CHECK_CONT(rmt_del_encoder(handle->copy_encoder) == ESP_OK, NULL, "Delete encoder failed.")}; + {ZH_ERROR_CHECK_CONT(rmt_del_channel(handle->tx_channel) == ESP_OK, NULL, "Delete TX channel failed.")}; + {ZH_ERROR_CHECK_CONT(rmt_del_channel(handle->rx_channel) == ESP_OK, NULL, "Delete RX channel failed.")}, "RX channel creation failed."); rmt_rx_event_callbacks_t cbs = { .on_recv_done = _zh_ttp229_rmt_rx_done_callback, }; ZH_ERROR_CHECK(rmt_rx_register_event_callbacks(handle->rx_channel, &cbs, handle) == ESP_OK, ESP_FAIL, - {ZH_ERROR_CHECK(rmt_disable(handle->tx_channel) == ESP_OK, ESP_FAIL, NULL, "TX channel disable failed.")}; - {ZH_ERROR_CHECK(rmt_disable(handle->rx_channel) == ESP_OK, ESP_FAIL, NULL, "RX channel disable failed.")}; - {ZH_ERROR_CHECK(rmt_del_encoder(handle->copy_encoder) == ESP_OK, ESP_FAIL, NULL, "Delete encoder failed.")}; - {ZH_ERROR_CHECK(rmt_del_channel(handle->tx_channel) == ESP_OK, ESP_FAIL, NULL, "Delete TX channel failed.")}; - {ZH_ERROR_CHECK(rmt_del_channel(handle->rx_channel) == ESP_OK, ESP_FAIL, NULL, "Delete RX channel failed.")}, "TX callback registar failed."); + {ZH_ERROR_CHECK_CONT(rmt_disable(handle->tx_channel) == ESP_OK, NULL, "TX channel disable failed.")}; + {ZH_ERROR_CHECK_CONT(rmt_disable(handle->rx_channel) == ESP_OK, NULL, "RX channel disable failed.")}; + {ZH_ERROR_CHECK_CONT(rmt_del_encoder(handle->copy_encoder) == ESP_OK, NULL, "Delete encoder failed.")}; + {ZH_ERROR_CHECK_CONT(rmt_del_channel(handle->tx_channel) == ESP_OK, NULL, "Delete TX channel failed.")}; + {ZH_ERROR_CHECK_CONT(rmt_del_channel(handle->rx_channel) == ESP_OK, NULL, "Delete RX channel failed.")}, "TX callback register failed."); return ESP_OK; } @@ -364,30 +399,84 @@ static esp_err_t _zh_ttp229_task_init(const zh_ttp229_init_config_t *config) return ESP_OK; } -static void IRAM_ATTR _zh_ttp229_isr_handler(void *arg) +static void _zh_ttp229_rollback_task_and_resources(void) +{ + if (_vector != NULL) + { + uint16_t vector_size = 0; + ZH_ERROR_CHECK_VOID(zh_vector_get_size(&_vector, &vector_size) == ESP_OK, NULL, "Failed to get vector size."); + if (vector_size == 1) + { + if (zh_ttp229 != NULL) + { + vTaskDelete(zh_ttp229); + zh_ttp229 = NULL; + } + if (_queue_handle != NULL) + { + vQueueDelete(_queue_handle); + _queue_handle = NULL; + } + } + } +} + +static void _zh_ttp229_isr_handler(void *arg) { zh_ttp229_handle_t *ttp229_handle = (zh_ttp229_handle_t *)arg; + if (ttp229_handle->rx_channel == NULL || ttp229_handle->tx_channel == NULL) + { + return; + } gpio_intr_disable(ttp229_handle->sdo_gpio); rmt_receive_config_t rx_receive_config = {.signal_range_min_ns = 100, .signal_range_max_ns = 2000000}; if (rmt_receive(ttp229_handle->rx_channel, &ttp229_handle->rx_symbols, sizeof(ttp229_handle->rx_symbols), &rx_receive_config) != ESP_OK) { ++_stats.rmt_driver_error; + ttp229_handle->rx_needs_reset = true; + if (_queue_handle != NULL) + { + zh_ttp229_queue_t notify = {0}; + notify.handle = ttp229_handle; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + if (xQueueSendFromISR(_queue_handle, ¬ify, &xHigherPriorityTaskWoken) != pdTRUE) + { + ++_stats.queue_overflow_error; + gpio_intr_enable(ttp229_handle->sdo_gpio); + } + if (xHigherPriorityTaskWoken == pdTRUE) + { + portYIELD_FROM_ISR(pdTRUE); + } + } + return; } gpio_set_direction(ttp229_handle->sdo_gpio, GPIO_MODE_OUTPUT); gpio_set_level(ttp229_handle->sdo_gpio, 0); gpio_set_level(ttp229_handle->sdo_gpio, 1); gpio_set_direction(ttp229_handle->sdo_gpio, GPIO_MODE_INPUT); - rmt_symbol_word_t scl_symbols[ttp229_handle->work_mode]; - for (uint8_t i = 0; i < (uint8_t)ttp229_handle->work_mode; ++i) - { - scl_symbols[i] = (rmt_symbol_word_t){.duration0 = 2, .level0 = 0, .duration1 = 2, .level1 = 1}; - } rmt_transmit_config_t tx_transmit_config = {.loop_count = 0, .flags.eot_level = 1, .flags.queue_nonblocking = 1}; - if (rmt_transmit(ttp229_handle->tx_channel, ttp229_handle->copy_encoder, scl_symbols, sizeof(scl_symbols), &tx_transmit_config) != ESP_OK) + if (rmt_transmit(ttp229_handle->tx_channel, ttp229_handle->copy_encoder, ttp229_handle->scl_symbols, ttp229_handle->scl_symbols_count * sizeof(rmt_symbol_word_t), &tx_transmit_config) != ESP_OK) { ++_stats.rmt_driver_error; + ttp229_handle->rx_needs_reset = true; + if (_queue_handle != NULL) + { + zh_ttp229_queue_t notify = {0}; + notify.handle = ttp229_handle; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + if (xQueueSendFromISR(_queue_handle, ¬ify, &xHigherPriorityTaskWoken) != pdTRUE) + { + ++_stats.queue_overflow_error; + gpio_intr_enable(ttp229_handle->sdo_gpio); + } + if (xHigherPriorityTaskWoken == pdTRUE) + { + portYIELD_FROM_ISR(pdTRUE); + } + } + return; } - portYIELD_FROM_ISR(); } static void _zh_ttp229_isr_processing_task(void *pvParameter) @@ -397,127 +486,118 @@ static void _zh_ttp229_isr_processing_task(void *pvParameter) while (xQueueReceive(_queue_handle, &ttp229_queue, portMAX_DELAY) == pdTRUE) { zh_ttp229_handle_t *ttp229_handle = ttp229_queue.handle; + if (ttp229_handle->rx_needs_reset == true) + { + ttp229_handle->rx_needs_reset = false; + ZH_ERROR_CHECK_CONT(rmt_disable(ttp229_handle->rx_channel) == ESP_OK, ++_stats.rmt_driver_error, "Failed to disable RX channel."); + ZH_ERROR_CHECK_CONT(rmt_enable(ttp229_handle->rx_channel) == ESP_OK, ++_stats.rmt_driver_error, "Failed to enable RX channel."); + } + const uint16_t duration1 = ttp229_queue.rx_symbol.duration1; + const uint16_t base = ttp229_handle->rmt_tx_start_delay; uint8_t pad_number = 0; - if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay - 1)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 1))) + if (duration1 == (base - 1) || duration1 == base || duration1 == (base + 1)) { pad_number = 1; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 3)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 4)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 5))) + else if (duration1 == (base + 3) || duration1 == (base + 4) || duration1 == (base + 5)) { pad_number = 2; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 7)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 8)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 9))) + else if (duration1 == (base + 7) || duration1 == (base + 8) || duration1 == (base + 9)) { pad_number = 3; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 11)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 12)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 13))) + else if (duration1 == (base + 11) || duration1 == (base + 12) || duration1 == (base + 13)) { pad_number = 4; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 15)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 16)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 17))) + else if (duration1 == (base + 15) || duration1 == (base + 16) || duration1 == (base + 17)) { pad_number = 5; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 19)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 20)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 21))) + else if (duration1 == (base + 19) || duration1 == (base + 20) || duration1 == (base + 21)) { pad_number = 6; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 23)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 24)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 25))) + else if (duration1 == (base + 23) || duration1 == (base + 24) || duration1 == (base + 25)) { pad_number = 7; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 27)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 28)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 29))) + else if (duration1 == (base + 27) || duration1 == (base + 28) || duration1 == (base + 29)) { pad_number = 8; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 31)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 32)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 33))) + else if (duration1 == (base + 31) || duration1 == (base + 32) || duration1 == (base + 33)) { pad_number = 9; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 35)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 36)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 37))) + else if (duration1 == (base + 35) || duration1 == (base + 36) || duration1 == (base + 37)) { pad_number = 10; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 39)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 40)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 41))) + else if (duration1 == (base + 39) || duration1 == (base + 40) || duration1 == (base + 41)) { pad_number = 11; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 43)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 44)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 45))) + else if (duration1 == (base + 43) || duration1 == (base + 44) || duration1 == (base + 45)) { pad_number = 12; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 47)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 48)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 49))) + else if (duration1 == (base + 47) || duration1 == (base + 48) || duration1 == (base + 49)) { pad_number = 13; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 51)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 52)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 53))) + else if (duration1 == (base + 51) || duration1 == (base + 52) || duration1 == (base + 53)) { pad_number = 14; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 55)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 56)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 57))) + else if (duration1 == (base + 55) || duration1 == (base + 56) || duration1 == (base + 57)) { pad_number = 15; } - else if ((ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 59)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 60)) || - (ttp229_handle->rx_symbols.duration1 == (ttp229_handle->rmt_tx_start_delay + 61))) + else if (duration1 == (base + 59) || duration1 == (base + 60) || duration1 == (base + 61)) { pad_number = 16; } - if (pad_number != 0) + if (pad_number > ttp229_handle->scl_symbols_count) { + pad_number = 0; + } + TickType_t now = xTaskGetTickCount(); + TickType_t debounce_ticks = pdMS_TO_TICKS(ttp229_handle->debounce_time); + bool first_event = (ttp229_handle->last_event_tick == (TickType_t)-1) ? true : false; + bool debounce_elapsed = first_event || ((now - ttp229_handle->last_event_tick) >= debounce_ticks) ? true : false; + if (pad_number != 0 && debounce_elapsed == true) + { + ttp229_handle->last_event_tick = now; zh_ttp229_event_on_isr_t ttp229_data = {0}; ttp229_data.pad_number = pad_number; ttp229_data.device_number = ttp229_handle->device_number; - ZH_ERROR_CHECK_CONT(esp_event_post(ZH_TTP229, 0, &ttp229_data, sizeof(zh_ttp229_event_on_isr_t), 1000 / portTICK_PERIOD_MS) == ESP_OK, ++_stats.event_post_error, "Touch pad isr processing failed. Failed to post interrupt event."); + ZH_ERROR_CHECK_CONT(esp_event_post(ZH_TTP229, 0, &ttp229_data, sizeof(zh_ttp229_event_on_isr_t), pdMS_TO_TICKS(10)) == ESP_OK, ++_stats.event_post_error, "Touch pad isr processing failed. Failed to post interrupt event."); } - vTaskDelay(ttp229_handle->debounce_time / portTICK_PERIOD_MS); gpio_intr_enable(ttp229_handle->sdo_gpio); _stats.min_stack_size = (uint32_t)uxTaskGetStackHighWaterMark(NULL); } vTaskDelete(NULL); } -static bool IRAM_ATTR _zh_ttp229_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data) +static bool _zh_ttp229_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data) { + (void)channel; (void)edata; zh_ttp229_handle_t *ttp229_handle = (zh_ttp229_handle_t *)user_data; + if (_queue_handle == NULL) + { + return false; + } zh_ttp229_queue_t ttp229_queue = {0}; ttp229_queue.handle = ttp229_handle; + ttp229_queue.rx_symbol = ttp229_handle->rx_symbols; BaseType_t xHigherPriorityTaskWoken = pdFALSE; if (xQueueSendFromISR(_queue_handle, &ttp229_queue, &xHigherPriorityTaskWoken) != pdTRUE) { ++_stats.queue_overflow_error; + gpio_intr_enable(ttp229_handle->sdo_gpio); } if (xHigherPriorityTaskWoken == pdTRUE) {