diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ab5c51..cf47d6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1 +1,6 @@ -idf_component_register(SRCS "main.c" INCLUDE_DIRS "include") \ No newline at end of file +if(${IDF_VERSION_MAJOR} STREQUAL 6) + set(requires esp_driver_rmt esp_driver_gpio esp_event) +else() + set(requires driver esp_event) +endif() +idf_component_register(SRCS "zh_ttp229.c" INCLUDE_DIRS "include" REQUIRES ${requires}) \ No newline at end of file diff --git a/README.md b/README.md index 3df3e4d..8bd3461 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,94 @@ -# esp_component_template +# ESP32 ESP-IDF component for TTP229 touch pad (via RMT driver) -esp_component_template \ No newline at end of file +## Tested on + +1. [ESP32 ESP-IDF v6.0.0](https://docs.espressif.com/projects/esp-idf/en/v6.0/esp32/index.html) + +## SAST Tools + +[PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code. + +## Features + +1. Support up to 4 touch pads on one device. +2. Support 8 or 16 pad work mode. + +## Note + +1. After start/reset first touch not work. +2. Multi touch not supported. + +## Attention + +1. For correct operation, please enable the following settings in the 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: + +```text +cd ../your_project/components +git clone https://github.com/aZholtikov/zh_ttp229 +``` + +In the application, add the component: + +```c +#include "zh_ttp229.h" +``` + +## Examples + +One touch pad on device: + +```c +#include "zh_ttp229.h" + +#define TTP229_NUMBER 0x01 + +zh_ttp229_handle_t ttp229_handle = {0}; + +void zh_ttp229_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + +void app_main(void) +{ + esp_log_level_set("zh_ttp229", 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(); + config.scl_gpio = GPIO_NUM_25; + config.sdo_gpio = GPIO_NUM_27; + config.work_mode = ZH_TTP229_16_PAD; + config.device_number = TTP229_NUMBER; + config.debounce_time = 300; + // The time between the low-level pulse in the interrupt and the actual start time of the RMT signal generator (in microseconds). + // It is determined experimentally or using a logic analyzer. + config.rmt_tx_start_delay = 19; + zh_ttp229_init(&config, &ttp229_handle); + for (;;) + { + const zh_ttp229_stats_t *stats = zh_ttp229_get_stats(); + printf("Number of RMT driver error: %ld.\n", stats->rmt_driver_error); + printf("Number of event post error: %ld.\n", stats->event_post_error); + printf("Number of queue overflow error: %ld.\n", stats->queue_overflow_error); + printf("Minimum free stack size: %ld.\n", stats->min_stack_size); + vTaskDelay(60000 / portTICK_PERIOD_MS); + } +} + +void zh_ttp229_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + zh_ttp229_event_on_isr_t *ttp229_event = event_data; + printf("Touch pad number %d pad number %d pressed.\n", ttp229_event->device_number, ttp229_event->pad_number); +} +``` diff --git a/TTP229 datasheet.pdf b/TTP229 datasheet.pdf new file mode 100644 index 0000000..cf50521 Binary files /dev/null and b/TTP229 datasheet.pdf differ diff --git a/include/main.h b/include/main.h deleted file mode 100644 index e69de29..0000000 diff --git a/include/zh_ttp229.h b/include/zh_ttp229.h new file mode 100644 index 0000000..0819b33 --- /dev/null +++ b/include/zh_ttp229.h @@ -0,0 +1,143 @@ +/** + * @file zh_ttp229.h + */ + +#pragma once + +#include "esp_log.h" +#include "driver/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/rmt_tx.h" +#include "driver/rmt_rx.h" +#include "driver/gpio.h" +#include "esp_event.h" + +/** + * @brief TTP229 touch pad initial default values. + */ +#define ZH_TTP229_INIT_CONFIG_DEFAULT() \ + { \ + .task_priority = 1, \ + .stack_size = configMINIMAL_STACK_SIZE, \ + .queue_size = 1, \ + .work_mode = ZH_TTP229_8_PAD, \ + .rmt_tx_start_delay = 0, \ + .debounce_time = 100, \ + .device_number = 0, \ + .scl_gpio = GPIO_NUM_MAX, \ + .sdo_gpio = GPIO_NUM_MAX} + +#ifdef __cplusplus +extern "C" +{ +#endif + + extern TaskHandle_t zh_ttp229; /*!< Unique task handle. */ + + /** + * @brief Enumeration of TTP229 touch pad work mode. + */ + typedef enum + { + ZH_TTP229_8_PAD = 8, + ZH_TTP229_16_PAD = 16, + } zh_ttp229_work_mode_t; + + /** + * @brief Structure for initial initialization of TTP229 touch pad. + */ + typedef struct + { + uint16_t stack_size; /*!< Stack size for task for the TTP229 touch pad isr processing processing. @note The minimum size is configMINIMAL_STACK_SIZE. */ + uint16_t debounce_time; /*!< Touch pad button debounce time. @note Minimum value is 10 (in milliseconds). */ + uint8_t task_priority; /*!< Task priority for the TTP229 touch pad isr processing. @note Minimum value is 1. */ + uint8_t queue_size; /*!< Queue size for task for the touch pad processing. @note Minimum value is 1. */ + uint8_t device_number; /*!< Touch pad unique number. @note Must be greater than 0. */ + uint8_t rmt_tx_start_delay; /*!< The time between the low-level pulse in the interrupt and the actual start time of the RMT signal generator (in microseconds). @note Minimum value is 15. */ + gpio_num_t sdo_gpio; /*!< SDO GPIO. */ + gpio_num_t scl_gpio; /*!< SCL GPIO. */ + zh_ttp229_work_mode_t work_mode; /*!< Touch pad work mode. */ + } zh_ttp229_init_config_t; + + /** + * @brief TTP229 touch pad handle. + */ + typedef struct + { + bool is_initialized; /*!< Touch pad initialization flag. */ + gpio_num_t sdo_gpio; /*!< SDO GPIO. */ + uint8_t device_number; /*!< Touch pad unique number. */ + uint8_t rmt_tx_start_delay; /*!< The time between the low-level pulse in the interrupt and the actual start time of the RMT signal generator (in microseconds). */ + uint16_t debounce_time; /*!< Touch pad button debounce_time. */ + zh_ttp229_work_mode_t work_mode; /*!< Touch pad work mode. */ + rmt_symbol_word_t rx_symbols; /*!< Received symbols. */ + rmt_channel_handle_t tx_channel; /*!< Unique RMT TX device handle. */ + rmt_channel_handle_t rx_channel; /*!< Unique RMT RX device handle. */ + rmt_encoder_handle_t copy_encoder; /*!< Unique copy encoder handle. */ + } zh_ttp229_handle_t; + + /** + * @brief Structure for error statistics storage. + */ + typedef struct + { + uint32_t rmt_driver_error; /*!< Number of RMT driver error. */ + uint32_t event_post_error; /*!< Number of event post error. */ + uint32_t queue_overflow_error; /*!< Number of queue overflow error. */ + uint32_t min_stack_size; /*!< Minimum free stack size. */ + } zh_ttp229_stats_t; + + ESP_EVENT_DECLARE_BASE(ZH_TTP229); + + /** + * @brief Structure for sending data to the event handler when cause an interrupt. + * + * @note Should be used with ZH_TTP229 event base. + */ + typedef struct + { + uint8_t pad_number; /*!< The pad that caused the interrupt. */ + uint8_t device_number; /*!< Touch pad unique number. */ + } zh_ttp229_event_on_isr_t; + + /** + * @brief Initialize TTP229 touch pad. + * + * @param[in] config Pointer to TTP229 initialized configuration structure. Can point to a temporary variable. + * @param[out] handle Pointer to unique TTP229 handle. + * + * @attention I2C driver must be initialized first. + * + * @note Before initialize the expander recommend initialize zh_ttp229_init_config_t structure with default values. + * + * @code zh_ttp229_init_config_t config = ZH_TTP229_INIT_CONFIG_DEFAULT() @endcode + * + * @return ESP_OK if success or an error code otherwise. + */ + esp_err_t zh_ttp229_init(const zh_ttp229_init_config_t *config, zh_ttp229_handle_t *handle); + + /** + * @brief Deinitialize TTP229 touch pad. + * + * @param[in] handle Pointer to unique TTP229 handle. + * + * @return ESP_OK if success or an error code otherwise. + */ + esp_err_t zh_ttp229_deinit(zh_ttp229_handle_t *handle); + + /** + * @brief Get error statistics. + * + * @return Pointer to the statistics structure. + */ + const zh_ttp229_stats_t *zh_ttp229_get_stats(void); + + /** + * @brief Reset error statistics. + */ + void zh_ttp229_reset_stats(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/main.c b/main.c deleted file mode 100644 index e69de29..0000000 diff --git a/version.txt b/version.txt index e69de29..afaf360 100644 --- a/version.txt +++ b/version.txt @@ -0,0 +1 @@ +1.0.0 \ No newline at end of file diff --git a/zh_ttp229.c b/zh_ttp229.c new file mode 100644 index 0000000..aa88fdb --- /dev/null +++ b/zh_ttp229.c @@ -0,0 +1,388 @@ +#include "zh_ttp229.h" + +static const char *TAG = "zh_ttp229"; + +#define ZH_LOGI(msg, ...) ESP_LOGI(TAG, msg, ##__VA_ARGS__) +#define ZH_LOGE(msg, err, ...) ESP_LOGE(TAG, "[%s:%d:%s] " msg, __FILE__, __LINE__, esp_err_to_name(err), ##__VA_ARGS__) + +#define ZH_ERROR_CHECK(cond, err, cleanup, msg, ...) \ + if (!(cond)) \ + { \ + ZH_LOGE(msg, err, ##__VA_ARGS__); \ + cleanup; \ + return err; \ + } + +typedef struct +{ + zh_ttp229_handle_t *handle; +} zh_ttp229_queue_t; + +TaskHandle_t zh_ttp229 = NULL; +static QueueHandle_t _queue_handle = NULL; + +volatile static uint8_t _pad_counter = {0}; +volatile static uint8_t _pad_number_matrix[4] = {0}; +static zh_ttp229_stats_t _stats = {0}; + +static esp_err_t _zh_ttp229_validate_config(const zh_ttp229_init_config_t *config, zh_ttp229_handle_t *handle); +static esp_err_t _zh_ttp229_gpio_init(const zh_ttp229_init_config_t *config, zh_ttp229_handle_t *handle); +static esp_err_t _zh_ttp229_rmt_init(const zh_ttp229_init_config_t *config, zh_ttp229_handle_t *handle); +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); +static void _zh_ttp229_isr_handler(void *arg); +static void _zh_ttp229_isr_processing_task(void *pvParameter); +static bool _zh_ttp229_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data); + +ESP_EVENT_DEFINE_BASE(ZH_TTP229); + +esp_err_t zh_ttp229_init(const zh_ttp229_init_config_t *config, zh_ttp229_handle_t *handle) // -V2008 +{ + ZH_LOGI("Touch pad initialization started."); + ZH_ERROR_CHECK(config != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Touch pad initialization failed. Invalid argument."); + ZH_ERROR_CHECK(handle->is_initialized == false, ESP_ERR_INVALID_STATE, NULL, "Touch pad initialization failed. Touch pad is already initialized."); + ZH_ERROR_CHECK(_pad_counter < sizeof(_pad_number_matrix), ESP_ERR_INVALID_ARG, NULL, "Touch pad initialization failed. Maximum quantity reached."); + ZH_ERROR_CHECK(_zh_ttp229_validate_config(config, handle) == ESP_OK, ESP_FAIL, NULL, "Touch pad initialization failed. Initial configuration check failed."); + ZH_ERROR_CHECK(_zh_ttp229_resources_init(config) == ESP_OK, ESP_FAIL, NULL, "Touch pad initialization failed. Resources initialization failed."); + ZH_ERROR_CHECK(_zh_ttp229_task_init(config) == ESP_OK, ESP_FAIL, + vQueueDelete(_queue_handle), "Touch pad initialization failed. Processing task initialization failed."); + ZH_ERROR_CHECK(_zh_ttp229_rmt_init(config, handle) == ESP_OK, ESP_FAIL, + vQueueDelete(_queue_handle); + vTaskDelete(zh_ttp229), "Touch pad initialization failed. RMT initialization failed."); + 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."); + vQueueDelete(_queue_handle); + vTaskDelete(zh_ttp229), "Encoder initialization failed. GPIO initialization failed."); + if (_stats.min_stack_size == 0) + { + _stats.min_stack_size = config->stack_size; + } + handle->debounce_time = config->debounce_time; + handle->is_initialized = true; + ++_pad_counter; + for (uint8_t i = 0; i < sizeof(_pad_number_matrix); ++i) + { + if (_pad_number_matrix[i] == 0) + { + _pad_number_matrix[i] = handle->device_number; + break; + } + } + ZH_LOGI("Touch pad initialization completed successfully."); + return ESP_OK; +} + +esp_err_t zh_ttp229_deinit(zh_ttp229_handle_t *handle) // -V2008 +{ + ZH_LOGI("Touch pad deinitialization started."); + ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Touch pad deinitialization failed. Invalid argument."); + ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Touch pad deinitialization failed. Touch pad not initialized."); + 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((gpio_num_t)handle->sdo_gpio) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. Remove GPIO isr handler failed."); + ZH_ERROR_CHECK(gpio_reset_pin((gpio_num_t)handle->sdo_gpio) == ESP_OK, ESP_FAIL, NULL, "Touch pad deinitialization failed. Reset GPIO failed."); + if (_pad_counter == 1) + { + vQueueDelete(_queue_handle); + vTaskDelete(zh_ttp229); + } + handle->is_initialized = false; + --_pad_counter; + for (uint8_t i = 0; i < sizeof(_pad_number_matrix); ++i) + { + if (_pad_number_matrix[i] == handle->device_number) + { + _pad_number_matrix[i] = 0; + break; + } + } + ZH_LOGI("Touch pad deinitialization completed successfully."); + return ESP_OK; +} + +const zh_ttp229_stats_t *zh_ttp229_get_stats(void) +{ + return &_stats; +} + +void zh_ttp229_reset_stats(void) +{ + ZH_LOGI("Error statistic reset started."); + _stats.rmt_driver_error = 0; + _stats.event_post_error = 0; + _stats.queue_overflow_error = 0; + _stats.min_stack_size = 0; + ZH_LOGI("Error statistic reset successfully."); +} + +static esp_err_t _zh_ttp229_validate_config(const zh_ttp229_init_config_t *config, zh_ttp229_handle_t *handle) // -V2008 +{ + ZH_ERROR_CHECK(config->task_priority >= 1 && config->stack_size >= configMINIMAL_STACK_SIZE, ESP_ERR_INVALID_ARG, NULL, "Invalid task settings."); + ZH_ERROR_CHECK(config->queue_size >= 1, ESP_ERR_INVALID_ARG, NULL, "Invalid queue size."); + ZH_ERROR_CHECK(config->debounce_time >= 10, ESP_ERR_INVALID_ARG, NULL, "Invalid debounce time."); + ZH_ERROR_CHECK(config->rmt_tx_start_delay >= 15, ESP_ERR_INVALID_ARG, NULL, "Invalid RMT TX start delay."); + ZH_ERROR_CHECK(config->work_mode == ZH_TTP229_8_PAD || config->work_mode == ZH_TTP229_16_PAD, ESP_ERR_INVALID_ARG, NULL, "Invalid touch pad work mode."); + ZH_ERROR_CHECK(config->device_number > 0, ESP_ERR_INVALID_ARG, NULL, "Invalid touch pad number."); + for (uint8_t i = 0; i < sizeof(_pad_number_matrix); ++i) + { + ZH_ERROR_CHECK(config->device_number != _pad_number_matrix[i], ESP_ERR_INVALID_ARG, NULL, "Touch pad number already present."); + } + 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) // -V2008 +{ + 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), + .intr_type = GPIO_INTR_POSEDGE}; + 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((gpio_num_t)config->sdo_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset GPIO failed."), "Failed install isr service."); + ZH_ERROR_CHECK(gpio_isr_handler_add((gpio_num_t)config->sdo_gpio, _zh_ttp229_isr_handler, handle) == ESP_OK, ESP_FAIL, + ZH_ERROR_CHECK(gpio_reset_pin((gpio_num_t)config->sdo_gpio) == ESP_OK, ESP_FAIL, NULL, "Reset GPIO failed."), "Interrupt initialization failed."); + handle->sdo_gpio = config->sdo_gpio; + return ESP_OK; +} + +static esp_err_t _zh_ttp229_rmt_init(const zh_ttp229_init_config_t *config, zh_ttp229_handle_t *handle) // -V2008 +{ + rmt_tx_channel_config_t tx_chan_config = { + .clk_src = RMT_CLK_SRC_DEFAULT, + .gpio_num = config->scl_gpio, + .mem_block_symbols = 64, + .resolution_hz = 1000000, + .trans_queue_depth = 4, + .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(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."); + rmt_rx_channel_config_t rx_chan_config = { + .clk_src = RMT_CLK_SRC_DEFAULT, + .gpio_num = config->sdo_gpio, + .mem_block_symbols = 64, + .resolution_hz = 1000000, + }; + 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(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."); + 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."); + return ESP_OK; +} + +static esp_err_t _zh_ttp229_resources_init(const zh_ttp229_init_config_t *config) +{ + if (_pad_counter == 0) + { + _queue_handle = xQueueCreate(config->queue_size, sizeof(zh_ttp229_queue_t)); + ZH_ERROR_CHECK(_queue_handle != NULL, ESP_FAIL, NULL, "Failed to create queue."); + } + return ESP_OK; +} + +static esp_err_t _zh_ttp229_task_init(const zh_ttp229_init_config_t *config) +{ + if (_pad_counter == 0) + { + ZH_ERROR_CHECK(xTaskCreatePinnedToCore(&_zh_ttp229_isr_processing_task, "zh_ttp229_isr_processing", config->stack_size, NULL, config->task_priority, &zh_ttp229, tskNO_AFFINITY) == pdPASS, + ESP_FAIL, NULL, "Failed to create isr processing task."); + } + return ESP_OK; +} + +static void IRAM_ATTR _zh_ttp229_isr_handler(void *arg) +{ + zh_ttp229_handle_t *ttp229_handle = (zh_ttp229_handle_t *)arg; + 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; + } + 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 < 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) + { + ++_stats.rmt_driver_error; + } + portYIELD_FROM_ISR(); +} + +static void IRAM_ATTR _zh_ttp229_isr_processing_task(void *pvParameter) // -V2008 +{ + zh_ttp229_queue_t ttp229_queue = {0}; + while (xQueueReceive(_queue_handle, &ttp229_queue, portMAX_DELAY) == pdTRUE) + { + zh_ttp229_handle_t *ttp229_handle = ttp229_queue.handle; + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + 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))) + { + pad_number = 16; + } + if (pad_number != 0) + { + zh_ttp229_event_on_isr_t ttp229_data = {0}; + ttp229_data.pad_number = pad_number; + ttp229_data.device_number = ttp229_handle->device_number; + esp_err_t err = esp_event_post(ZH_TTP229, 0, &ttp229_data, sizeof(zh_ttp229_event_on_isr_t), 1000 / portTICK_PERIOD_MS); + if (err != ESP_OK) + { + ++_stats.event_post_error; + ZH_LOGE("Touch pad isr processing failed. Failed to post interrupt event.", err); + } + } + 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) +{ + zh_ttp229_handle_t *ttp229_handle = (zh_ttp229_handle_t *)user_data; + zh_ttp229_queue_t ttp229_queue = {0}; + ttp229_queue.handle = ttp229_handle; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + if (xQueueSendFromISR(_queue_handle, &ttp229_queue, &xHigherPriorityTaskWoken) != pdTRUE) + { + ++_stats.queue_overflow_error; + } + if (xHigherPriorityTaskWoken == pdTRUE) + { + return true; + } + return false; +} \ No newline at end of file