feat(touch_sens): touch sensor driver-ng on P4

This commit is contained in:
laokaiyao
2024-07-13 15:47:20 +08:00
committed by Kevin (Lao Kaiyao)
parent 172784733c
commit 8a18ae60e0
47 changed files with 2698 additions and 297 deletions
@@ -0,0 +1,21 @@
idf_build_get_property(target IDF_TARGET)
if(${target} STREQUAL "linux")
return() # This component is not supported by the POSIX/Linux simulator
endif()
set(srcs)
set(public_inc)
if(CONFIG_SOC_TOUCH_SENSOR_SUPPORTED)
if(CONFIG_SOC_TOUCH_SENSOR_VERSION EQUAL 3)
list(APPEND srcs "hw_ver3/touch_version_specific.c"
"common/touch_sens_common.c")
list(APPEND public_inc "include" "hw_ver3/include")
endif()
endif()
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES esp_driver_gpio
INCLUDE_DIRS ${public_inc}
)
+25
View File
@@ -0,0 +1,25 @@
menu "ESP-Driver:Touch Sensor Configurations"
depends on SOC_TOUCH_SENSOR_SUPPORTED
config TOUCH_CTRL_FUNC_IN_IRAM
bool "Place touch sensor control functions into IRAM"
default n
help
Place touch sensor oneshot scanning and continuous scanning functions into IRAM,
so that these function can be IRAM-safe and able to be called when the flash cache is disabled.
Enabling this option can improve driver performance as well.
config TOUCH_ISR_IRAM_SAFE
bool "Touch sensor ISR IRAM-Safe"
default n
help
Ensure the touch sensor interrupt is IRAM-Safe by allowing the interrupt handler to be
executable when the cache is disabled (e.g. SPI Flash write).
config TOUCH_ENABLE_DEBUG_LOG
bool "Enable debug log"
default n
help
Whether to enable the debug log message for touch driver.
Note that, this option only controls the touch driver log, won't affect other drivers.
endmenu # Touch Sensor Configuration
@@ -0,0 +1,428 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <inttypes.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "soc/soc_caps.h"
#include "soc/rtc.h"
#include "soc/clk_tree_defs.h"
#include "soc/touch_sensor_periph.h"
#include "driver/rtc_io.h"
#include "driver/touch_sens.h"
#if SOC_TOUCH_SENSOR_VERSION <= 2
#include "esp_private/rtc_ctrl.h"
#else
#include "soc/interrupts.h"
#include "esp_intr_alloc.h"
#endif
#if CONFIG_TOUCH_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "esp_check.h"
#include "touch_sens_private.h"
#define TOUCH_CHANNEL_CHECK(num) ESP_RETURN_ON_FALSE(num >= TOUCH_MIN_CHAN_ID && num <= TOUCH_MAX_CHAN_ID, \
ESP_ERR_INVALID_ARG, TAG, "The channel number is out of supported range");
static const char *TAG = "touch";
touch_sensor_handle_t g_touch = NULL;
static void touch_channel_pin_init(int id)
{
gpio_num_t pin = touch_sensor_channel_io_map[id];
rtc_gpio_init(pin);
rtc_gpio_set_direction(pin, RTC_GPIO_MODE_DISABLED);
rtc_gpio_pulldown_dis(pin);
rtc_gpio_pullup_dis(pin);
}
static void s_touch_free_resource(touch_sensor_handle_t sens_handle)
{
if (!sens_handle) {
return;
}
if (sens_handle->mutex) {
vSemaphoreDeleteWithCaps(sens_handle->mutex);
sens_handle->mutex = NULL;
}
free(g_touch);
g_touch = NULL;
}
esp_err_t touch_sensor_new_controller(const touch_sensor_config_t *sens_cfg, touch_sensor_handle_t *ret_sens_handle)
{
#if CONFIG_TOUCH_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
esp_err_t ret = ESP_OK;
TOUCH_NULL_POINTER_CHECK(sens_cfg);
TOUCH_NULL_POINTER_CHECK(ret_sens_handle);
ESP_RETURN_ON_FALSE(!g_touch, ESP_ERR_INVALID_STATE, TAG, "Touch sensor has been allocated");
g_touch = (touch_sensor_handle_t)heap_caps_calloc(1, sizeof(struct touch_sensor_s), TOUCH_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(g_touch, ESP_ERR_NO_MEM, TAG, "No memory for touch sensor struct");
g_touch->mutex = xSemaphoreCreateMutexWithCaps(TOUCH_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(g_touch->mutex, ESP_ERR_NO_MEM, err, TAG, "No memory for mutex semaphore");
touch_priv_enable_module(true);
ESP_GOTO_ON_ERROR(touch_priv_config_controller(g_touch, sens_cfg), err, TAG, "Failed to configure the touch controller");
#if SOC_TOUCH_SENSOR_VERSION <= 2
ESP_GOTO_ON_ERROR(rtc_isr_register(touch_priv_default_intr_handler, NULL, TOUCH_LL_INTR_MASK_ALL, 0), err, TAG, "Failed to register interrupt handler");
#else
ESP_GOTO_ON_ERROR(esp_intr_alloc(ETS_LP_TOUCH_INTR_SOURCE, TOUCH_INTR_ALLOC_FLAGS, touch_priv_default_intr_handler, NULL, &(g_touch->intr_handle)),
err, TAG, "Failed to register interrupt handler");
#endif
*ret_sens_handle = g_touch;
return ret;
err:
touch_priv_enable_module(false);
s_touch_free_resource(g_touch);
return ret;
}
esp_err_t touch_sensor_del_controller(touch_sensor_handle_t sens_handle)
{
TOUCH_NULL_POINTER_CHECK(sens_handle);
ESP_RETURN_ON_FALSE(g_touch == sens_handle, ESP_ERR_INVALID_ARG, TAG, "The input touch sensor handle is unmatched");
esp_err_t ret = ESP_OK;
// Take the semaphore to make sure the touch has stopped
xSemaphoreTake(sens_handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(!sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Touch sensor has not disabled");
FOR_EACH_TOUCH_CHANNEL(i) {
ESP_GOTO_ON_FALSE(!sens_handle->ch[i], ESP_ERR_INVALID_STATE, err, TAG, "There are still some touch channels not deleted");
}
ESP_GOTO_ON_ERROR(touch_priv_deinit_controller(sens_handle), err, TAG, "Failed to deinitialize the controller");
#if SOC_TOUCH_SENSOR_VERSION <= 2
ESP_GOTO_ON_ERROR(rtc_isr_deregister(touch_priv_default_intr_handler, NULL), err, TAG, "Failed to deregister the interrupt handler");
#else
ESP_GOTO_ON_ERROR(esp_intr_free(sens_handle->intr_handle), err, TAG, "Failed to deregister the interrupt handler");
#endif
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
touch_ll_intr_disable(TOUCH_LL_INTR_MASK_ALL);
touch_ll_clear_active_channel_status();
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
touch_priv_enable_module(false);
s_touch_free_resource(sens_handle);
err:
if (g_touch && g_touch->mutex) {
xSemaphoreGive(g_touch->mutex);
}
return ret;
}
esp_err_t touch_sensor_new_channel(touch_sensor_handle_t sens_handle, int chan_id,
const touch_channel_config_t *chan_cfg,
touch_channel_handle_t *ret_chan_handle)
{
TOUCH_NULL_POINTER_CHECK(sens_handle);
TOUCH_NULL_POINTER_CHECK(chan_cfg);
TOUCH_NULL_POINTER_CHECK(ret_chan_handle);
TOUCH_CHANNEL_CHECK(chan_id);
ESP_RETURN_ON_FALSE(g_touch == sens_handle, ESP_ERR_INVALID_ARG, TAG, "The input touch sensor handle is unmatched");
esp_err_t ret = ESP_OK;
xSemaphoreTake(sens_handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(!sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err2, TAG, "Please disable the touch sensor first");
ESP_GOTO_ON_FALSE(!sens_handle->ch[chan_id], ESP_ERR_INVALID_STATE, err2, TAG, "The channel %d has been registered", chan_id);
sens_handle->ch[chan_id] = (touch_channel_handle_t)heap_caps_calloc(1, sizeof(struct touch_channel_s), TOUCH_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(sens_handle->ch[chan_id], ESP_ERR_NO_MEM, err2, TAG, "No memory for touch channel");
sens_handle->ch[chan_id]->id = chan_id;
sens_handle->ch[chan_id]->base = sens_handle;
sens_handle->ch[chan_id]->is_prox_chan = false;
/* Init the channel */
ESP_GOTO_ON_ERROR(touch_priv_config_channel(sens_handle->ch[chan_id], chan_cfg),
err1, TAG, "Failed to configure the touch channel %d", chan_id);
touch_channel_pin_init(chan_id);
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
#if SOC_TOUCH_SENSOR_VERSION == 2
touch_ll_reset_chan_benchmark(1 << chan_id);
#endif
sens_handle->chan_mask |= 1 << chan_id;
touch_ll_set_channel_mask(sens_handle->chan_mask);
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
*ret_chan_handle = sens_handle->ch[chan_id];
xSemaphoreGive(sens_handle->mutex);
return ret;
err1:
free(sens_handle->ch[chan_id]);
sens_handle->ch[chan_id] = NULL;
err2:
xSemaphoreGive(sens_handle->mutex);
return ret;
}
esp_err_t touch_sensor_del_channel(touch_channel_handle_t chan_handle)
{
TOUCH_NULL_POINTER_CHECK(chan_handle);
esp_err_t ret = ESP_OK;
touch_sensor_handle_t sens_handle = chan_handle->base;
xSemaphoreTake(sens_handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(!sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Please disable the touch sensor first");
#if SOC_TOUCH_SENSOR_VERSION == 2
if (sens_handle->guard_chan == chan_handle || (BIT(chan_handle->id) & sens_handle->shield_chan_mask)) {
ESP_GOTO_ON_ERROR(touch_sensor_config_waterproof(sens_handle, NULL), err, TAG, "Failed to disable waterproof on this channel");
}
if (sens_handle->sleep_chan == chan_handle) {
ESP_GOTO_ON_ERROR(touch_sensor_config_sleep_channel(sens_handle, NULL), err, TAG, "Failed to disable sleep function on this channel");
}
#endif
int id = chan_handle->id;
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
sens_handle->chan_mask &= ~(1UL << id);
touch_ll_set_channel_mask(sens_handle->chan_mask);
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
free(g_touch->ch[id]);
g_touch->ch[id] = NULL;
err:
xSemaphoreGive(sens_handle->mutex);
return ret;
}
esp_err_t touch_sensor_reconfig_controller(touch_sensor_handle_t sens_handle, const touch_sensor_config_t *sens_cfg)
{
TOUCH_NULL_POINTER_CHECK(sens_handle);
TOUCH_NULL_POINTER_CHECK(sens_cfg);
ESP_RETURN_ON_FALSE(sens_cfg->meas_interval_us >= 0, ESP_ERR_INVALID_ARG, TAG, "interval_us should be a positive value");
esp_err_t ret = ESP_OK;
xSemaphoreTake(sens_handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(!sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Please disable the touch sensor first");
ESP_GOTO_ON_ERROR(touch_priv_config_controller(sens_handle, sens_cfg), err, TAG, "Configure touch controller failed");
err:
xSemaphoreGive(sens_handle->mutex);
return ret;
}
esp_err_t touch_sensor_enable(touch_sensor_handle_t sens_handle)
{
TOUCH_NULL_POINTER_CHECK(sens_handle);
esp_err_t ret = ESP_OK;
xSemaphoreTakeRecursive(sens_handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(!sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Touch sensor has already enabled");
ESP_GOTO_ON_FALSE(sens_handle->sample_cfg_num, ESP_ERR_INVALID_STATE, err, TAG, "No sample configuration was added to the touch controller");
sens_handle->is_enabled = true;
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
touch_ll_intr_clear(TOUCH_LL_INTR_MASK_ALL);
touch_ll_intr_enable(TOUCH_LL_INTR_MASK_ALL);
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
#if SOC_TOUCH_SUPPORT_PROX_SENSING
/* Reset the cached data of proximity channel */
FOR_EACH_TOUCH_CHANNEL(i) {
if (sens_handle->ch[i] && sens_handle->ch[i]->is_prox_chan) {
sens_handle->ch[i]->prox_cnt = 0;
memset(sens_handle->ch[i]->prox_val, 0, sizeof(sens_handle->ch[i]->prox_val[0]) * TOUCH_SAMPLE_CFG_NUM);
}
}
#endif
err:
xSemaphoreGiveRecursive(sens_handle->mutex);
return ret;
}
esp_err_t touch_sensor_disable(touch_sensor_handle_t sens_handle)
{
TOUCH_NULL_POINTER_CHECK(sens_handle);
esp_err_t ret = ESP_OK;
xSemaphoreTake(sens_handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Touch sensor has not enabled");
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
touch_ll_intr_disable(TOUCH_LL_INTR_MASK_ALL);
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
sens_handle->is_enabled = false;
err:
xSemaphoreGive(sens_handle->mutex);
return ret;
}
esp_err_t touch_sensor_reconfig_channel(touch_channel_handle_t chan_handle, const touch_channel_config_t *chan_cfg)
{
TOUCH_NULL_POINTER_CHECK(chan_handle);
TOUCH_NULL_POINTER_CHECK(chan_cfg);
esp_err_t ret = ESP_OK;
touch_sensor_handle_t sens_handle = chan_handle->base;
xSemaphoreTake(sens_handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(!sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Please disable the touch sensor first");
ESP_GOTO_ON_ERROR(touch_priv_config_channel(chan_handle, chan_cfg), err, TAG, "Configure touch channel failed");
err:
xSemaphoreGive(sens_handle->mutex);
return ret;
}
esp_err_t touch_sensor_start_continuous_scanning(touch_sensor_handle_t sens_handle)
{
TOUCH_NULL_POINTER_CHECK_ISR(sens_handle);
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE_ISR(sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Please enable the touch sensor first");
ESP_GOTO_ON_FALSE_ISR(!sens_handle->is_started, ESP_ERR_INVALID_STATE, err, TAG, "Continuous scanning has started already");
TOUCH_ENTER_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
sens_handle->is_started = true;
#if SOC_TOUCH_SENSOR_VERSION <= 2
touch_ll_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
touch_ll_start_fsm();
#else
touch_ll_enable_fsm_timer(true);
touch_ll_start_fsm_repeated_timer(false);
#endif
TOUCH_EXIT_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
err:
return ret;
}
esp_err_t touch_sensor_stop_continuous_scanning(touch_sensor_handle_t sens_handle)
{
TOUCH_NULL_POINTER_CHECK_ISR(sens_handle);
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE_ISR(sens_handle->is_started, ESP_ERR_INVALID_STATE, err, TAG, "Continuous scanning not started yet");
TOUCH_ENTER_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
#if SOC_TOUCH_SENSOR_VERSION <= 2
touch_ll_stop_fsm();
touch_ll_set_fsm_mode(TOUCH_FSM_MODE_SW);
#else
touch_ll_stop_fsm_repeated_timer(false);
touch_ll_enable_fsm_timer(false);
#endif
sens_handle->is_started = false;
TOUCH_EXIT_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
err:
return ret;
}
esp_err_t touch_sensor_trigger_oneshot_scanning(touch_sensor_handle_t sens_handle, int timeout_ms)
{
TOUCH_NULL_POINTER_CHECK(sens_handle);
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Please enable the touch sensor first");
ESP_GOTO_ON_FALSE(!sens_handle->is_started, ESP_ERR_INVALID_STATE, err, TAG, "Failed to trigger oneshot scanning because scanning has started");
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
sens_handle->is_started = true;
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
TickType_t ticks = 0;
if (timeout_ms > 0) {
ticks = pdMS_TO_TICKS(timeout_ms);
if (!ticks) {
ESP_LOGW(TAG, "The timeout is too small, use the minimum tick resolution as default: %"PRIu32" ms", portTICK_PERIOD_MS);
ticks = 1;
}
}
xSemaphoreTake(sens_handle->mutex, ticks);
TickType_t end_tick = xTaskGetTickCount() + ticks;
// TODO: extract the following implementation into version specific source file when supporting other targets
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
touch_ll_enable_fsm_timer(false);
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
FOR_EACH_TOUCH_CHANNEL(i) {
if (sens_handle->ch[i]) {
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
touch_ll_channel_sw_measure_mask(BIT(i));
touch_ll_trigger_oneshot_measurement();
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
while (!touch_ll_is_measure_done()) {
if (g_touch->is_meas_timeout) {
g_touch->is_meas_timeout = false;
ESP_LOGW(TAG, "The measurement time on channel %d exceed the limitation", i);
break;
}
if (timeout_ms >= 0) {
ESP_GOTO_ON_FALSE(xTaskGetTickCount() <= end_tick, ESP_ERR_TIMEOUT, err, TAG, "Wait for measurement done timeout");
}
vTaskDelay(1);
}
}
}
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
touch_ll_channel_sw_measure_mask(0);
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
err:
xSemaphoreGive(sens_handle->mutex);
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
sens_handle->is_started = false;
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
return ret;
}
esp_err_t touch_sensor_register_callbacks(touch_sensor_handle_t sens_handle, const touch_event_callbacks_t *callbacks, void *user_ctx)
{
TOUCH_NULL_POINTER_CHECK(sens_handle);
TOUCH_NULL_POINTER_CHECK(callbacks);
#if CONFIG_TOUCH_ISR_IRAM_SAFE
const uint32_t **ptr = (const uint32_t **)callbacks;
for (int i = 0; i < sizeof(touch_event_callbacks_t) / sizeof(uint32_t *); i++) {
ESP_RETURN_ON_FALSE(TOUCH_IRAM_CHECK(ptr[i]), ESP_ERR_INVALID_ARG, TAG, "callback not in IRAM");
}
ESP_RETURN_ON_FALSE(!user_ctx || esp_ptr_internal(user_ctx), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
#endif
esp_err_t ret = ESP_OK;
xSemaphoreTake(sens_handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(!sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Please disable the touch sensor first");
memcpy(&sens_handle->cbs, callbacks, sizeof(touch_event_callbacks_t));
sens_handle->user_ctx = user_ctx;
err:
xSemaphoreGive(sens_handle->mutex);
return ret;
}
esp_err_t touch_channel_read_data(touch_channel_handle_t chan_handle, touch_chan_data_type_t type, uint32_t *data)
{
TOUCH_NULL_POINTER_CHECK_ISR(chan_handle);
TOUCH_NULL_POINTER_CHECK_ISR(data);
return touch_priv_channel_read_data(chan_handle, type, data);
}
esp_err_t touch_sensor_set_benchmark(touch_channel_handle_t chan_handle, const touch_chan_benchmark_op_t *benchmark_op)
{
TOUCH_NULL_POINTER_CHECK_ISR(chan_handle);
TOUCH_NULL_POINTER_CHECK_ISR(benchmark_op);
touch_priv_set_benchmark(chan_handle, benchmark_op);
return ESP_OK;
}
@@ -0,0 +1,191 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief This header file is private for the internal use of the touch driver
* DO NOT use any APIs or types in this file outside of the touch driver
*/
#pragma once
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "soc/soc_caps.h"
#include "hal/touch_sensor_hal.h"
#include "driver/touch_common_types.h"
#include "esp_memory_utils.h"
#include "esp_check.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Helper macros */
#define TOUCH_NULL_POINTER_CHECK(p) ESP_RETURN_ON_FALSE((p), ESP_ERR_INVALID_ARG, TAG, "input parameter '"#p"' is NULL")
#define TOUCH_NULL_POINTER_CHECK_ISR(p) ESP_RETURN_ON_FALSE_ISR((p), ESP_ERR_INVALID_ARG, TAG, "input parameter '"#p"' is NULL")
#define FOR_EACH_TOUCH_CHANNEL(i) for (int i = 0; i < SOC_TOUCH_SENSOR_NUM; i++)
#define TOUCH_IRAM_CHECK(cb) (!(cb) || esp_ptr_in_iram(cb))
/* IRAM safe caps */
#if CONFIG_TOUCH_ISR_IRAM_SAFE
#define TOUCH_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED)
#define TOUCH_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define TOUCH_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED)
#define TOUCH_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif //CONFIG_TOUCH_ISR_IRAM_SAFE
/* DMA caps */
#define TOUCH_DMA_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
/* RTC peripheral spin lock */
extern portMUX_TYPE rtc_spinlock;
#define TOUCH_RTC_LOCK (&rtc_spinlock)
#if SOC_TOUCH_SENSOR_VERSION <= 2
#define TOUCH_PERIPH_LOCK (&rtc_spinlock)
#else
extern portMUX_TYPE g_touch_spinlock;
#define TOUCH_PERIPH_LOCK (&g_touch_spinlock)
#endif
#define TOUCH_ENTER_CRITICAL(spinlock) portENTER_CRITICAL(spinlock)
#define TOUCH_EXIT_CRITICAL(spinlock) portEXIT_CRITICAL(spinlock)
#define TOUCH_ENTER_CRITICAL_SAFE(spinlock) portENTER_CRITICAL_SAFE(spinlock)
#define TOUCH_EXIT_CRITICAL_SAFE(spinlock) portEXIT_CRITICAL_SAFE(spinlock)
/**
* @brief The touch sensor controller instance structure
* @note A touch sensor controller includes multiple channels and sample configurations
*
*/
struct touch_sensor_s {
touch_channel_handle_t ch[SOC_TOUCH_SENSOR_NUM]; /*!< Touch sensor channel handles, will be NULL if the channel is not registered */
uint32_t chan_mask; /*!< Enabled channel mask, corresponding bit will be set if the channel is registered */
uint32_t src_freq_hz; /*!< Source clock frequency */
intr_handle_t intr_handle; /*!< Interrupt handle */
touch_event_callbacks_t cbs; /*!< Event callbacks */
touch_channel_handle_t deep_slp_chan; /*!< The configured channel for depp sleep, will be NULL if not enable the deep sleep */
touch_channel_handle_t guard_chan; /*!< The configured channel for the guard ring, will be NULL if not set */
touch_channel_handle_t shield_chan; /*!< The configured channel for the shield pad, will be NULL if not set */
SemaphoreHandle_t mutex; /*!< Mutex lock to ensure thread safety */
uint8_t sample_cfg_num; /*!< The number of sample configurations that in used */
void *user_ctx; /*!< User context that will pass to the callback function */
bool is_enabled; /*!< Flag to indicate whether the scanning is enabled */
bool is_started; /*!< Flag to indicate whether the scanning has started */
bool is_meas_timeout; /*!< Flag to indicate whether the measurement timeout, will force to stop the current measurement if the timeout is triggered */
bool sleep_en; /*!< Flag to indicate whether the sleep wake-up feature is enabled */
bool waterproof_en; /*!< Flag to indicate whether the water proof feature is enabled */
bool proximity_en; /*!< Flag to indicate whether the proximity sensing feature is enabled */
bool timeout_en; /*!< Flag to indicate whether the measurement timeout feature (hardware timeout) is enabled */
};
/**
* @brief The touch sensor channel instance structure
*
*/
struct touch_channel_s {
touch_sensor_handle_t base; /*!< The touch sensor controller handle */
int id; /*!< Touch channel id, the range is target-specific */
bool is_prox_chan; /*!< Flag to indicate whether this is a proximity channel */
uint32_t prox_cnt; /*!< Cache the proximity measurement count, only takes effect when the channel is a proximity channel.
* When this count reaches `touch_proximity_config_t::scan_times`,
* this field will be cleared and call the `on_proximity_meas_done` callback.
*/
uint32_t prox_val[TOUCH_SAMPLE_CFG_NUM]; /*!< The accumulated proximity value of each sample config.
* The value will accumulate for each scanning until it reaches `touch_proximity_config_t::scan_times`.
* This accumulated proximity value can be read via `touch_channel_read_data` when all scanning finished.
*/
};
extern touch_sensor_handle_t g_touch; /*!< Global touch sensor controller handle for `esp_driver_touch_sens` use only */
/**
* @brief Touch sensor module enable interface
* @note This is a private interface of `esp_driver_touch_sens`
* It should be implemented by each hardware version
*
* @param[in] enable Set true to enable touch sensor module clock
*/
void touch_priv_enable_module(bool enable);
/**
* @brief Touch sensor default interrupt handler
* @note This is a private interface of `esp_driver_touch_sens`
* It should be implemented by each hardware version
*
* @param[in] arg The input argument
*/
void touch_priv_default_intr_handler(void *arg);
/**
* @brief Touch sensor controller configuration interface
* @note This is a private interface of `esp_driver_touch_sens`
* It should be implemented by each hardware version
*
* @param[in] sens_handle The touch sensor controller handle
* @param[in] sens_cfg The touch sensor controller configuration pointer
* @return
* - ESP_OK On success
* - Others Version-specific failure code
*/
esp_err_t touch_priv_config_controller(touch_sensor_handle_t sens_handle, const touch_sensor_config_t *sens_cfg);
/**
* @brief Touch sensor channel configuration interface
* @note This is a private interface of `esp_driver_touch_sens`
* It should be implemented by each hardware version
*
* @param[in] chan_handle The touch sensor channel handle
* @param[in] chan_cfg The touch sensor channel configuration pointer
* @return
* - ESP_OK On success
* - Others Version-specific failure code
*/
esp_err_t touch_priv_config_channel(touch_channel_handle_t chan_handle, const touch_channel_config_t *chan_cfg);
/**
* @brief Touch sensor controller de-initialize interface
* @note This is a private interface of `esp_driver_touch_sens`
* It should be implemented by each hardware version
*
* @param[in] sens_handle The touch sensor handle
* @return
* - ESP_OK On success
* - Others Version-specific failure code
*/
esp_err_t touch_priv_deinit_controller(touch_sensor_handle_t sens_handle);
/**
* @brief Touch sensor channel data read interface
* @note This is a private interface of `esp_driver_touch_sens`
* It should be implemented by each hardware version
*
* @param[in] chan_handle The touch sensor channel handle
* @param[in] type The read data type
* @param[out] data The output data pointer
* @return
* - ESP_OK On success
* - Others Version-specific failure code
*/
esp_err_t touch_priv_channel_read_data(touch_channel_handle_t chan_handle, touch_chan_data_type_t type, uint32_t *data);
/**
* @brief Touch sensor channel benchmark set interface
* @note This is a private interface of `esp_driver_touch_sens`
* It should be implemented by each hardware version
*
* @param[in] chan_handle The channel handle
* @param[in] benchmark_op The benchmark operation
*/
void touch_priv_set_benchmark(touch_channel_handle_t chan_handle, const touch_chan_benchmark_op_t *benchmark_op);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief This file is only applicable to the touch hardware version1
* Version 1 includes ESP32
*/
#error "'esp_driver_touch_sens' does not support for ESP32 yet"
@@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief This file is only applicable to the touch hardware version2
* Version 2 includes ESP32-S2 and ESP32-S3
*/
#error "'esp_driver_touch_sens' does not support for ESP32-S2 and ESP32-S3 yet"
@@ -0,0 +1,503 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief This file is only applicable to the touch hardware version3
* Version 3 includes ESP32-P4
*/
#pragma once
#include "soc/soc_caps.h"
#include "driver/touch_common_types.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#define TOUCH_MIN_CHAN_ID 0 /*!< The minimum available channel id of the touch pad */
#define TOUCH_MAX_CHAN_ID 13 /*!< The maximum available channel id of the touch pad */
/**
* @brief Helper macro to the default configurations of the touch sensor controller
*
*/
#define TOUCH_SENSOR_DEFAULT_BASIC_CONFIG(sample_cfg_number, sample_cfg_array) { \
.power_on_wait_us = 256, \
.meas_interval_us = 32.0, \
.max_meas_time_us = 0, \
.output_mode = TOUCH_PAD_OUT_AS_CLOCK, \
.sample_cfg_num = sample_cfg_number, \
.sample_cfg = sample_cfg_array, \
}
/**
* @brief Helper macro to the default sample configurations
* @note This default configuration is based on clock frequency 16MHz
*
*/
#define TOUCH_SENSOR_DEFAULT_SAMPLE_CONFIG0() { \
.freq_hz = 16000000, \
.charge_times = 500, \
.rc_filter_res = 2, \
.rc_filter_cap = 88, \
.low_drv = 3, \
.high_drv = 0, \
.bias_volt = 5, \
.bypass_shield_output = false, \
}
/**
* @brief Helper macro to the default sample configurations
* @note This default configuration is based on clock frequency 8MHz
*
*/
#define TOUCH_SENSOR_DEFAULT_SAMPLE_CONFIG1() { \
.freq_hz = 8000000, \
.charge_times = 500, \
.rc_filter_res = 3, \
.rc_filter_cap = 29, \
.low_drv = 3, \
.high_drv = 8, \
.bias_volt = 5, \
.bypass_shield_output = false, \
}
/**
* @brief Helper macro to the default sample configurations
* @note This default configuration is based on clock frequency 4MHz
*
*/
#define TOUCH_SENSOR_DEFAULT_SAMPLE_CONFIG2() { \
.freq_hz = 4000000, \
.charge_times = 500, \
.rc_filter_res = 3, \
.rc_filter_cap = 10, \
.low_drv = 7, \
.high_drv = 31, \
.bias_volt = 15, \
.bypass_shield_output = false, \
}
#define TOUCH_SENSOR_DEFAULT_FILTER_CONFIG() { \
.benchmark = { \
.filter_mode = TOUCH_BM_IIR_FILTER_4, \
.jitter_step = 4, \
.update = { \
.pos_thresh = TOUCH_UPDATE_BM_POS_THRESH_1_4, \
.neg_thresh = TOUCH_UPDATE_BM_NEG_THRESH_1_4, \
.neg_limit = 5, \
}, \
}, \
.data = { \
.smooth_filter = TOUCH_SMOOTH_IIR_FILTER_2, \
.active_hysteresis = 2, \
}, \
}
/**
* @brief The data type of the touch channel
*
*/
typedef enum {
TOUCH_CHAN_DATA_TYPE_SMOOTH, /*!< The smooth data of the touch channel */
TOUCH_CHAN_DATA_TYPE_BENCHMARK, /*!< The benchmark of the touch channel */
TOUCH_CHAN_DATA_TYPE_PROXIMITY, /*!< The proximity data of the proximity channel */
} touch_chan_data_type_t;
/**
* @brief The chip sleep level that allows the touch sensor to wake-up
*
*/
typedef enum {
TOUCH_LIGHT_SLEEP_WAKEUP, /*!< Only enable the touch sensor to wake up the chip from light sleep */
TOUCH_DEEP_SLEEP_WAKEUP, /*!< Enable the touch sensor to wake up the chip from deep sleep or light sleep */
} touch_sleep_wakeup_level_t;
/**
* @brief Touch sensor shield channel drive capability level
*
*/
typedef enum {
TOUCH_SHIELD_CAP_40PF, /*!< The max equivalent capacitance in shield channel is 40pf */
TOUCH_SHIELD_CAP_80PF, /*!< The max equivalent capacitance in shield channel is 80pf */
TOUCH_SHIELD_CAP_120PF, /*!< The max equivalent capacitance in shield channel is 120pf */
TOUCH_SHIELD_CAP_160PF, /*!< The max equivalent capacitance in shield channel is 160pf */
TOUCH_SHIELD_CAP_200PF, /*!< The max equivalent capacitance in shield channel is 200pf */
TOUCH_SHIELD_CAP_240PF, /*!< The max equivalent capacitance in shield channel is 240pf */
TOUCH_SHIELD_CAP_280PF, /*!< The max equivalent capacitance in shield channel is 280pf */
TOUCH_SHIELD_CAP_320PF, /*!< The max equivalent capacitance in shield channel is 320pf */
} touch_chan_shield_cap_t;
/**
* @brief Touch channel Infinite Impulse Response (IIR) filter or Jitter filter for benchmark
* @note Recommended filter coefficient selection is `IIR_16`.
*/
typedef enum {
TOUCH_BM_IIR_FILTER_4, /*!< IIR Filter for benchmark, 1/4 raw_value + 3/4 benchmark */
TOUCH_BM_IIR_FILTER_8, /*!< IIR Filter for benchmark, 1/8 raw_value + 7/8 benchmark */
TOUCH_BM_IIR_FILTER_16, /*!< IIR Filter for benchmark, 1/16 raw_value + 15/16 benchmark (typical) */
TOUCH_BM_IIR_FILTER_32, /*!< IIR Filter for benchmark, 1/32 raw_value + 31/32 benchmark */
TOUCH_BM_IIR_FILTER_64, /*!< IIR Filter for benchmark, 1/64 raw_value + 63/64 benchmark */
TOUCH_BM_IIR_FILTER_128, /*!< IIR Filter for benchmark, 1/128 raw_value + 127/128 benchmark */
TOUCH_BM_JITTER_FILTER, /*!< Jitter Filter for benchmark, raw value +/- jitter_step */
} touch_benchmark_filter_mode_t;
/**
* @brief Positive noise limitation of benchmark
* benchmark will only update gradually when
* the smooth data within the positive noise limitation
*
*/
typedef enum {
TOUCH_UPDATE_BM_POS_ALWAYS = -1, /*!< Always update benchmark when (smooth_data - benchmark) > 0 */
TOUCH_UPDATE_BM_POS_THRESH_1_2 = 0, /*!< Only update benchmark when the (smooth_data - benchmark) < 1/2 * activate_threshold */
TOUCH_UPDATE_BM_POS_THRESH_3_8, /*!< Only update benchmark when the (smooth_data - benchmark) < 3/8 * activate_threshold */
TOUCH_UPDATE_BM_POS_THRESH_1_4, /*!< Only update benchmark when the (smooth_data - benchmark) < 1/4 * activate_threshold */
TOUCH_UPDATE_BM_POS_THRESH_1, /*!< Only update benchmark when the (smooth_data - benchmark) < 1 * activate_threshold */
} touch_benchmark_pos_thresh_t;
/**
* @brief Negative noise limitation of benchmark
* benchmark will only update gradually when
* the smooth data within the negative noise limitation
*
*/
typedef enum {
TOUCH_UPDATE_BM_NEG_NEVER = -2, /*!< Never update benchmark when (benchmark - smooth_data) > 0 */
TOUCH_UPDATE_BM_NEG_ALWAYS = -1, /*!< Always update benchmark when (benchmark - smooth_data) > 0 */
TOUCH_UPDATE_BM_NEG_THRESH_1_2 = 0, /*!< Only update benchmark when the (benchmark - smooth_data) < 1/2 * activate_threshold */
TOUCH_UPDATE_BM_NEG_THRESH_3_8, /*!< Only update benchmark when the (benchmark - smooth_data) < 3/8 * activate_threshold */
TOUCH_UPDATE_BM_NEG_THRESH_1_4, /*!< Only update benchmark when the (benchmark - smooth_data) < 1/4 * activate_threshold */
TOUCH_UPDATE_BM_NEG_THRESH_1, /*!< Only update benchmark when the (benchmark - smooth_data) < 1 * activate_threshold */
} touch_benchmark_neg_thresh_t;
/**
* @brief Touch channel Infinite Impulse Response (IIR) filter for smooth data
*
*/
typedef enum {
TOUCH_SMOOTH_NO_FILTER, /*!< No filter adopted for smooth data, smooth data equals raw data */
TOUCH_SMOOTH_IIR_FILTER_2, /*!< IIR filter adopted for smooth data, smooth data equals 1/2 raw data + 1/2 last smooth data (typical) */
TOUCH_SMOOTH_IIR_FILTER_4, /*!< IIR filter adopted for smooth data, smooth data equals 1/4 raw data + 3/4 last smooth data */
TOUCH_SMOOTH_IIR_FILTER_8, /*!< IIR filter adopted for smooth data, smooth data equals 1/8 raw data + 7/8 last smooth data */
} touch_smooth_filter_mode_t;
/**
* @brief Interrupt events
*
*/
typedef enum {
TOUCH_INTR_EVENT_ACTIVE, /*!< Touch channel active event */
TOUCH_INTR_EVENT_INACTIVE, /*!< Touch channel inactive event */
TOUCH_INTR_EVENT_MEASURE_DONE, /*!< Touch channel measure done event */
TOUCH_INTR_EVENT_SCAN_DONE, /*!< All touch channels scan done event */
TOUCH_INTR_EVENT_TIMEOUT, /*!< Touch channel measurement timeout event */
TOUCH_INTR_EVENT_PROXIMITY_DONE, /*!< Proximity channel measurement done event */
} touch_intr_event_t;
/**
* @brief Sample configurations of the touch sensor
*
*/
typedef struct {
uint32_t freq_hz; /*!< The sampling frequency for this configuration in Hz */
uint32_t charge_times; /*!< The charge and discharge times of this sample configuration, the read data are positive correlation to the charge_times */
uint8_t rc_filter_res; /*!< The resistance of the RC filter of this sample configuration, range [0, 3], while 0 = 0K, 1 = 1.5K, 2 = 3K, 3 = 4.5K */
uint8_t rc_filter_cap; /*!< The capacitance of the RC filter of this sample configuration, range [0, 127], while 0 = 0pF, 1 = 20fF, ..., 127 = 2.54pF */
uint8_t low_drv; /*!< Low speed touch driver, only effective when high speed driver is disabled */
uint8_t high_drv; /*!< High speed touch driver */
uint8_t bias_volt; /*!< The Internal LDO voltage, which decide the bias voltage of the sample wave, range [0,15] */
bool bypass_shield_output; /*!< Whether to bypass the shield output, enable when the charging/discharging rate greater than 10MHz */
} touch_sensor_sample_config_t;
/**
* @brief Configurations of the touch sensor controller
*
*/
typedef struct {
uint32_t power_on_wait_us; /*!< The waiting time between the channels power on and able to measure, to ensure the data stability */
float meas_interval_us; /*!< Measurement interval of each channels */
uint32_t max_meas_time_us; /*!< The maximum time of measuring one channel, if the time exceeds this value, the timeout interrupt will be triggered.
* Set to '0' to ignore the measurement time limitation, otherwise please set a proper time considering the configurations
* of this sample configurations below.
*/
touch_out_mode_t output_mode; /*!< Touch channel counting mode of the binarized touch output */
uint32_t sample_cfg_num; /*!< The sample configuration number that used for sampling */
touch_sensor_sample_config_t *sample_cfg; /*!< The array of this sample configuration configurations, the length should be specified in `touch_sensor_config_t::sample_cfg_num` */
} touch_sensor_config_t;
/**
* @brief Configurations of the touch sensor channel
*
*/
typedef struct {
uint32_t active_thresh[TOUCH_SAMPLE_CFG_NUM]; /*!< The active threshold of each sample configuration,
* while the touch channel smooth value minus benchmark value exceed this threshold,
* will be regarded as activated
*/
} touch_channel_config_t;
/**
* @brief Configurations of the touch sensor filter
*
*/
typedef struct {
/**
* @brief Benchmark configuration
*/
struct {
touch_benchmark_filter_mode_t filter_mode; /*!< Benchmark filter mode. IIR filter and Jitter filter can be selected,
* TOUCH_BM_IIR_FILTER_16 is recommended
*/
uint32_t jitter_step; /*!< Jitter filter step size, only takes effect when the `filter_mode` is TOUCH_BM_JITTER_FILTER. Range: 0 ~ 15 */
/**
* @brief Benchmark update strategy
*/
struct {
touch_benchmark_pos_thresh_t pos_thresh; /*!< Select the positive noise threshold. Higher = More noise resistance.
* Range: [-1 ~ 3]. The coefficient of the positive threshold are -1: always; 0: 4/8; 1: 3/8; 2: 2/8; 3: 1;
* Once the data of the channel exceeded the positive threshold (i.e., benchmark + coefficient * touch threshold),
* the benchmark will stop updated to that value.
* -1: ignore the positive threshold and always update the benchmark for positive noise
*/
touch_benchmark_neg_thresh_t neg_thresh; /*!< Select the negative noise threshold. Higher = More noise resistance.
* Range: [-2 ~ 3]. The coefficient of the negative threshold are -2: never; -1: always; 0: 4/8; 1: 3/8; 2: 2/8; 3: 1;
* Once the data of the channel below the negative threshold (i.e., benchmark - coefficient * touch threshold),
* the benchmark will stop updated to that value,
* unless the data keep below the negative threshold for more than the limitation of `neg_limit`
* -1: ignore the negative threshold and always update the benchmark for negative noise
* -2: never update the benchmark for negative noise
*/
uint32_t neg_limit; /*!< Set the time limitation of the negative threshold.
* The benchmark will be updated to the value that below to the negative threshold after the limited time.
* Normally the negative update is used at the beginning, as the initial benchmark is a very large value.
* (the unit of `neg_limit` is the tick of the slow clock source)
*/
} update; /*!< The benchmark update strategy */
} benchmark; /*!< Benchmark filter */
/**
* @brief Data configuration
*/
struct {
touch_smooth_filter_mode_t smooth_filter; /*!< Smooth data IIR filter mode */
uint32_t active_hysteresis; /*!< The hysteresis threshold to judge whether the touch channel is active
* If the channel data exceed the 'touch_channel_config_t::active_thresh + active_hysteresis'
* The channel will be activated. If the channel data is below to
* 'touch_channel_config_t::active_thresh - active_hysteresis' the channel will be inactivated.
*/
uint32_t debounce_cnt; /*!< The debounce count of the touch channel.
* Only when the channel data exceed the `touch_channel_config_t::active_thresh + active_hysteresis` for `debounce_cnt` times
* The channel will be activated. And only if the channel data is below to the `touch_channel_config_t::active_thresh - active_hysteresis`
* for `debounce_cnt` times, the channel will be inactivated.
* (The unit of `debounce_cnt` is the tick of the slow clock source)
*/
} data; /*!< Channel data filter */
} touch_sensor_filter_config_t;
/**
* @brief Touch sensor configuration during the deep sleep
* @note Currently it is the same as the normal controller configuration.
* The deep sleep configuration only takes effect when the chip entered sleep,
* so that to update a more power efficient configuration.
*
*/
typedef touch_sensor_config_t touch_sensor_config_dslp_t;
/**
* @brief Configure the touch sensor sleep function
*
*/
typedef struct {
touch_sleep_wakeup_level_t slp_wakeup_lvl; /*!< The sleep level that can be woke up by touch sensor. */
touch_channel_handle_t deep_slp_chan; /*!< The touch channel handle that supposed to work in the deep sleep. It can wake up the chip
* from deep sleep when this channel is activated.
* Only effective when the `touch_sleep_config_t::slp_wakeup_lvl` is `TOUCH_DEEP_SLEEP_WAKEUP`
*/
uint32_t deep_slp_thresh[TOUCH_SAMPLE_CFG_NUM]; /*!< The active threshold of the deep sleep channel during deep sleep,
* while the sleep channel exceed this threshold, it will be regarded as activated
* Only effective when the `touch_sleep_config_t::slp_wakeup_lvl` is `TOUCH_DEEP_SLEEP_WAKEUP`
*/
touch_sensor_config_dslp_t *deep_slp_sens_cfg; /*!< Specify the touch sensor configuration during the deep sleep.
* Note that these configurations will no take effect immediately,
* they will be set automatically while the chip prepare to enter sleep.
* Set NULL to not change the configurations before entering sleep.
* The sleep configuration mainly aims at lower down the charging and measuring times,
* so that to save power consumption during the sleep.
* Only effective when the `touch_sleep_config_t::slp_wakeup_lvl` is `TOUCH_DEEP_SLEEP_WAKEUP`
*/
} touch_sleep_config_t;
/**
* @brief Configure the touch sensor waterproof function
*
*/
typedef struct {
touch_channel_handle_t guard_chan; /*!< The guard channel of waterproof. Set NULL if you don't need the guard channel.
* Typically, the guard channel is a ring that surrounds the touch panels,
* it is used to detect the large area that covered by water.
* While large area of water covers the touch panel, the touch sensor will be temporary disable to avoid the fake touch.
*/
touch_channel_handle_t shield_chan; /*!< The touch channel that used as the shield channel, can't be NULL.
* Typically, the shield channel uses grid layout which covers the touch area,
* it is used to shield the influence of water droplets covering both the touch panel and the shield channel.
* The shield channel will be paralleled to the current measuring channel (except the guard channel) to reduce the influence of water droplets.
*/
uint32_t shield_drv; /*!< The shield channel driver, which controls the drive capability of shield channel, range: 0 ~ 7
* The larger the parasitic capacitance on the shielding channel, the higher the drive capability needs to be set.
*/
} touch_waterproof_config_t;
/**
* @brief Configure the touch sensor proximity function
*
*/
typedef struct {
touch_channel_handle_t proximity_chan[TOUCH_PROXIMITY_CHAN_NUM]; /*!< The touch channel handles that will be configured as proximity sensing channels */
uint32_t scan_times; /*!< The total scan times of EACH sample configuration, all sample configurations share a same `scan_times`.
* The measurement result of each scanning will be accumulated together to get the final result.
*/
uint32_t charge_times[TOUCH_SAMPLE_CFG_NUM]; /*!< The charge times of EACH scanning, different sample configurations can set different `charge_times`.
* The measurement result of each scanning is positive correlation to the `charge_times`,
* please set a proper value in case of the final accumulated result overflow.
*/
} touch_proximity_config_t;
/**
* @brief Base event structure used in touch event queue
*/
typedef struct {
touch_channel_handle_t chan; /*!< the current triggered touch channel handle */
int chan_id; /*!< the current triggered touch channel number */
uint32_t status_mask; /*!< the current channel triggered status.
* For the bits in the status mask,
* if the bit is set, the corresponding channel is active
* if the bit is cleared, the corresponding channel is inactive
*/
} touch_base_event_data_t;
/**
* @brief Measure done event data
* @note Currently same as base event data
*
*/
typedef touch_base_event_data_t touch_meas_done_event_data_t;
/**
* @brief Scan done event data
* @note Currently same as base event data
*
*/
typedef touch_base_event_data_t touch_scan_done_event_data_t;
/**
* @brief Active event data
* @note Currently same as base event data
*
*/
typedef touch_base_event_data_t touch_active_event_data_t;
/**
* @brief Inactive event data
* @note Currently same as base event data
*
*/
typedef touch_base_event_data_t touch_inactive_event_data_t;
/**
* @brief Proximity sensing measure done event data
* @note Currently same as base event data
*
*/
typedef touch_base_event_data_t touch_prox_done_event_data_t;
/**
* @brief Timeout event data
* @note Currently same as base event data
*
*/
typedef touch_base_event_data_t touch_timeout_event_data_t;
/**
* @brief Touch sensor callbacks
* @note Set NULL for the used callbacks.
*
*/
typedef struct {
/**
* @brief Touch sensor on active event callback.
* Callback when any touch channel is activated.
* @param[in] sens_handle Touch sensor controller handle, created from `touch_sensor_new_controller()`
* @param[in] event Touch sensor active event data
* @param[in] user_ctx User registered context, passed from `touch_sensor_register_callbacks()`
*
* @return Whether a high priority task has been waken up by this callback function
*/
bool (*on_active)(touch_sensor_handle_t sens_handle, const touch_active_event_data_t *event, void *user_ctx);
/**
* @brief Touch sensor on inactive event callback.
* Callback when any touch channel is inactivated.
* @param[in] sens_handle Touch sensor controller handle, created from `touch_sensor_new_controller()`
* @param[in] event Touch sensor inactive event data
* @param[in] user_ctx User registered context, passed from `touch_sensor_register_callbacks()`
*
* @return Whether a high priority task has been waken up by this callback function
*/
bool (*on_inactive)(touch_sensor_handle_t sens_handle, const touch_inactive_event_data_t *event, void *user_ctx);
/**
* @brief Touch sensor on measure done event callback.
* Callback when the measurement of all the sample configurations on the current touch channel is done.
* @param[in] sens_handle Touch sensor controller handle, created from `touch_sensor_new_controller()`
* @param[in] event Touch sensor measure done event data
* @param[in] user_ctx User registered context, passed from `touch_sensor_register_callbacks()`
*
* @return Whether a high priority task has been waken up by this callback function
*/
bool (*on_measure_done)(touch_sensor_handle_t sens_handle, const touch_meas_done_event_data_t *event, void *user_ctx);
/**
* @brief Touch sensor on scan done event callback.
* Callback when finished scanning all the registered touch channels.
* @param[in] sens_handle Touch sensor controller handle, created from `touch_sensor_new_controller()`
* @param[in] event Touch sensor scan done event data
* @param[in] user_ctx User registered context, passed from `touch_sensor_register_callbacks()`
*
* @return Whether a high priority task has been waken up by this callback function
*/
bool (*on_scan_done)(touch_sensor_handle_t sens_handle, const touch_scan_done_event_data_t *event, void *user_ctx);
/**
* @brief Touch sensor on measurement timeout event callback.
* Callback when measure the current touch channel timeout.
* @param[in] sens_handle Touch sensor controller handle, created from `touch_sensor_new_controller()`
* @param[in] event Touch sensor timeout event data
* @param[in] user_ctx User registered context, passed from `touch_sensor_register_callbacks()`
*
* @return Whether a high priority task has been waken up by this callback function
*/
bool (*on_timeout)(touch_sensor_handle_t sens_handle, const touch_timeout_event_data_t *event, void *user_ctx);
/**
* @brief Touch sensor on proximity sensing measurement done event callback.
* Callback when proximity sensing measurement of the current channel is done.
* @param[in] sens_handle Touch sensor controller handle, created from `touch_sensor_new_controller()`
* @param[in] event Touch sensor proximity sensing measure done event data
* @param[in] user_ctx User registered context, passed from `touch_sensor_register_callbacks()`
*
* @return Whether a high priority task has been waken up by this callback function
*/
bool (*on_proximity_meas_done)(touch_sensor_handle_t sens_handle, const touch_prox_done_event_data_t *event, void *user_ctx);
} touch_event_callbacks_t;
/**
* @brief Touch sensor benchmark operation, to set or reset the benchmark of the channel
*
*/
typedef struct {
bool do_reset; /*!< Whether to reset the benchmark to the channel's latest smooth data */
} touch_chan_benchmark_op_t;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,500 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief This file is only applicable to the touch hardware version3
* Version 3 includes ESP32-P4
*/
#include <inttypes.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "soc/soc_caps.h"
#include "soc/clk_tree_defs.h"
#include "soc/touch_sensor_periph.h"
#include "soc/rtc.h"
#include "hal/hal_utils.h"
#include "driver/touch_sens.h"
#include "esp_private/rtc_ctrl.h"
#include "esp_private/periph_ctrl.h"
#include "esp_clk_tree.h"
#include "esp_sleep.h"
#include "../../common/touch_sens_private.h"
#if CONFIG_TOUCH_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "esp_check.h"
static const char *TAG = "touch";
portMUX_TYPE g_touch_spinlock = portMUX_INITIALIZER_UNLOCKED;
/******************************************************************************
* Scope: touch driver private *
******************************************************************************/
void touch_priv_enable_module(bool enable)
{
TOUCH_ENTER_CRITICAL(TOUCH_RTC_LOCK);
touch_ll_enable_module_clock(enable);
touch_ll_enable_out_gate(enable);
#if SOC_TOUCH_SENSOR_VERSION >= 2
// Reset the benchmark after finished the scanning
touch_ll_reset_chan_benchmark(TOUCH_LL_FULL_CHANNEL_MASK);
#endif
TOUCH_EXIT_CRITICAL(TOUCH_RTC_LOCK);
}
void IRAM_ATTR touch_priv_default_intr_handler(void *arg)
{
/* If the touch controller object has not been allocated, return directly */
if (!g_touch) {
return;
}
bool need_yield = false;
uint32_t status = touch_ll_get_intr_status_mask();
g_touch->is_meas_timeout = false;
touch_ll_intr_clear(status);
touch_base_event_data_t data;
touch_ll_get_active_channel_mask(&data.status_mask);
data.chan = g_touch->ch[touch_ll_get_current_meas_channel()];
/* If the channel is not registered, return directly */
if (!data.chan) {
return;
}
data.chan_id = data.chan->id;
if (status & TOUCH_LL_INTR_MASK_DONE) {
if (g_touch->cbs.on_measure_done) {
need_yield |= g_touch->cbs.on_measure_done(g_touch, &data, g_touch->user_ctx);
}
}
if (status & TOUCH_LL_INTR_MASK_SCAN_DONE) {
if (g_touch->cbs.on_scan_done) {
need_yield |= g_touch->cbs.on_scan_done(g_touch, &data, g_touch->user_ctx);
}
}
if (status & TOUCH_LL_INTR_MASK_PROX_DONE) {
data.chan->prox_cnt++;
/* The proximity sensing result only accurate when the scanning times equal to the sample_cfg_num */
if (data.chan->prox_cnt == g_touch->sample_cfg_num) {
data.chan->prox_cnt = 0;
for (uint32_t i = 0; i < g_touch->sample_cfg_num; i++) {
touch_ll_read_chan_data(data.chan_id, i, TOUCH_LL_READ_BENCHMARK, &data.chan->prox_val[i]);
}
if (g_touch->cbs.on_proximity_meas_done) {
need_yield |= g_touch->cbs.on_proximity_meas_done(g_touch, &data, g_touch->user_ctx);
}
}
}
if (status & TOUCH_LL_INTR_MASK_ACTIVE) {
/* When the guard ring activated, disable the scanning of other channels to avoid fake touch */
TOUCH_ENTER_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
if (g_touch->waterproof_en && data.chan == g_touch->guard_chan) {
touch_ll_enable_scan_mask(~BIT(data.chan->id), false);
}
TOUCH_EXIT_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
if (g_touch->cbs.on_active) {
need_yield |= g_touch->cbs.on_active(g_touch, &data, g_touch->user_ctx);
}
}
if (status & TOUCH_LL_INTR_MASK_INACTIVE) {
/* When the guard ring inactivated, enable the scanning of other channels again */
TOUCH_ENTER_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
if (g_touch->waterproof_en && data.chan == g_touch->guard_chan) {
touch_ll_enable_scan_mask(g_touch->chan_mask & (~BIT(g_touch->shield_chan->id)), true);
}
TOUCH_EXIT_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
if (g_touch->cbs.on_inactive) {
need_yield |= g_touch->cbs.on_inactive(g_touch, &data, g_touch->user_ctx);
}
}
if (status & TOUCH_LL_INTR_MASK_TIMEOUT) {
g_touch->is_meas_timeout = true;
touch_ll_force_done_curr_measurement();
if (g_touch->cbs.on_timeout) {
need_yield |= g_touch->cbs.on_timeout(g_touch, &data, g_touch->user_ctx);
}
}
if (need_yield) {
portYIELD_FROM_ISR();
}
}
static esp_err_t s_touch_convert_to_hal_config(touch_sensor_handle_t sens_handle, const touch_sensor_config_t *sens_cfg, touch_hal_config_t *hal_cfg)
{
TOUCH_NULL_POINTER_CHECK(sens_cfg);
TOUCH_NULL_POINTER_CHECK(hal_cfg);
TOUCH_NULL_POINTER_CHECK(hal_cfg->sample_cfg);
ESP_RETURN_ON_FALSE(sens_cfg->sample_cfg_num && sens_cfg->sample_cfg, ESP_ERR_INVALID_ARG, TAG,
"at least one sample configuration required");
ESP_RETURN_ON_FALSE(sens_cfg->sample_cfg_num <= TOUCH_SAMPLE_CFG_NUM, ESP_ERR_INVALID_ARG, TAG,
"at most %d sample configurations supported", (int)(TOUCH_SAMPLE_CFG_NUM));
/* Get the source clock frequency for the first time */
if (!sens_handle->src_freq_hz) {
/* Touch sensor actually uses dynamic fast clock LP_DYN_FAST_CLK, but it will only switch to the slow clock during sleep,
* This driver only designed for wakeup case (sleep case should use ULP driver), so we only need to consider RTC_FAST here */
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_RTC_FAST, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &sens_handle->src_freq_hz),
TAG, "get clock frequency failed");
ESP_LOGD(TAG, "touch rtc clock source: RTC_FAST, frequency: %"PRIu32" Hz", sens_handle->src_freq_hz);
}
uint32_t src_freq_hz = sens_handle->src_freq_hz;
uint32_t src_freq_mhz = src_freq_hz / 1000000;
hal_cfg->power_on_wait_ticks = (uint32_t)sens_cfg->power_on_wait_us * src_freq_mhz;
hal_cfg->meas_interval_ticks = (uint32_t)(sens_cfg->meas_interval_us * src_freq_mhz);
hal_cfg->timeout_ticks = (uint32_t)sens_cfg->max_meas_time_us * src_freq_mhz;
ESP_RETURN_ON_FALSE(hal_cfg->timeout_ticks <= TOUCH_LL_TIMEOUT_MAX, ESP_ERR_INVALID_ARG, TAG,
"max_meas_time_ms should within %"PRIu32, TOUCH_LL_TIMEOUT_MAX / src_freq_mhz);
hal_cfg->sample_cfg_num = sens_cfg->sample_cfg_num;
hal_cfg->output_mode = sens_cfg->output_mode;
hal_utils_clk_info_t clk_info = {
.src_freq_hz = sens_handle->src_freq_hz,
.min_integ = 1,
.max_integ = TOUCH_LL_CLK_DIV_MAX,
.round_opt = HAL_DIV_ROUND,
};
for (uint32_t div_num, smp_cfg_id = 0; smp_cfg_id < sens_cfg->sample_cfg_num; smp_cfg_id++) {
const touch_sensor_sample_config_t *sample_cfg = &(sens_cfg->sample_cfg[smp_cfg_id]);
uint32_t actual_freq_hz = 0;
/* Allow 10% overflow to increase the robustness when the sample frequency is close to the source clock,
because the source clock is from RTC FAST whose frequency is floating up and down among startups */
if (sample_cfg->freq_hz > sens_handle->src_freq_hz && sample_cfg->freq_hz < sens_handle->src_freq_hz * 1.1) {
ESP_LOGW(TAG, "[sample_cfg_id %"PRIu32"] sample frequency exceed the src clock but still within 10%%", smp_cfg_id);
div_num = 1;
actual_freq_hz = sens_handle->src_freq_hz;
} else {
clk_info.exp_freq_hz = sample_cfg->freq_hz;
actual_freq_hz = hal_utils_calc_clk_div_integer(&clk_info, &div_num);
}
/* Check the actual frequency and its precision */
ESP_RETURN_ON_FALSE(actual_freq_hz, ESP_ERR_INVALID_ARG, TAG,
"[sample_cfg_id %"PRIu32"] sample frequency should within range %"PRIu32" ~ %"PRIu32" hz",
smp_cfg_id, sens_handle->src_freq_hz / TOUCH_LL_CLK_DIV_MAX, sens_handle->src_freq_hz);
ESP_LOGD(TAG, "[sample_cfg_id %"PRIu32"] clock divider %"PRIu32, smp_cfg_id, div_num);
if (actual_freq_hz != clk_info.exp_freq_hz) {
ESP_LOGW(TAG, "[sample_cfg_id %"PRIu32"] clock precision loss, expect %"PRIu32" hz, got %"PRIu32" hz",
smp_cfg_id, clk_info.exp_freq_hz, actual_freq_hz);
}
/* Assign the hal configurations */
hal_cfg->sample_cfg[smp_cfg_id].div_num = div_num;
hal_cfg->sample_cfg[smp_cfg_id].charge_times = sample_cfg->charge_times;
hal_cfg->sample_cfg[smp_cfg_id].rc_filter_res = sample_cfg->rc_filter_res;
hal_cfg->sample_cfg[smp_cfg_id].rc_filter_cap = sample_cfg->rc_filter_cap;
hal_cfg->sample_cfg[smp_cfg_id].low_drv = sample_cfg->low_drv;
hal_cfg->sample_cfg[smp_cfg_id].high_drv = sample_cfg->high_drv;
hal_cfg->sample_cfg[smp_cfg_id].bias_volt = sample_cfg->bias_volt;
}
return ESP_OK;
}
esp_err_t touch_priv_config_controller(touch_sensor_handle_t sens_handle, const touch_sensor_config_t *sens_cfg)
{
#if CONFIG_TOUCH_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
/* Check and convert the configuration to hal configurations */
touch_hal_sample_config_t sample_cfg[TOUCH_SAMPLE_CFG_NUM] = {};
touch_hal_config_t hal_cfg = {
.sample_cfg = sample_cfg,
};
ESP_RETURN_ON_ERROR(s_touch_convert_to_hal_config(sens_handle, sens_cfg, &hal_cfg),
TAG, "parse the configuration failed due to the invalid configuration");
sens_handle->sample_cfg_num = sens_cfg->sample_cfg_num;
/* Configure the hardware */
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
touch_hal_config_controller(&hal_cfg);
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
return ESP_OK;
}
esp_err_t touch_priv_config_channel(touch_channel_handle_t chan_handle, const touch_channel_config_t *chan_cfg)
{
uint8_t sample_cfg_num = chan_handle->base->sample_cfg_num;
// Check the validation of the channel active threshold
for (uint8_t smp_cfg_id = 0; smp_cfg_id < sample_cfg_num; smp_cfg_id++) {
ESP_RETURN_ON_FALSE(chan_cfg->active_thresh[smp_cfg_id] <= TOUCH_LL_ACTIVE_THRESH_MAX, ESP_ERR_INVALID_ARG,
TAG, "the active threshold out of range 0~%d", TOUCH_LL_ACTIVE_THRESH_MAX);
}
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
for (uint8_t smp_cfg_id = 0; smp_cfg_id < sample_cfg_num; smp_cfg_id++) {
touch_ll_set_chan_active_threshold(chan_handle->id, smp_cfg_id, chan_cfg->active_thresh[smp_cfg_id]);
}
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
return ESP_OK;
}
esp_err_t touch_priv_deinit_controller(touch_sensor_handle_t sens_handle)
{
/* Disable the additional functions */
if (sens_handle->proximity_en) {
touch_sensor_config_proximity_sensing(sens_handle, NULL);
}
if (sens_handle->sleep_en) {
touch_sensor_config_sleep_wakeup(sens_handle, NULL);
}
if (sens_handle->waterproof_en) {
touch_sensor_config_waterproof(sens_handle, NULL);
}
return ESP_OK;
}
esp_err_t touch_priv_channel_read_data(touch_channel_handle_t chan_handle, touch_chan_data_type_t type, uint32_t *data)
{
ESP_RETURN_ON_FALSE_ISR(type >= TOUCH_CHAN_DATA_TYPE_SMOOTH && type <= TOUCH_CHAN_DATA_TYPE_PROXIMITY,
ESP_ERR_INVALID_ARG, TAG, "The channel data type is invalid");
#if CONFIG_TOUCH_CTRL_FUNC_IN_IRAM
ESP_RETURN_ON_FALSE_ISR(esp_ptr_internal(data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
#endif
uint8_t sample_cfg_num = chan_handle->base->sample_cfg_num;
if (type < TOUCH_CHAN_DATA_TYPE_PROXIMITY) {
uint32_t internal_type = 0;
switch (type) {
default: // fall through
case TOUCH_CHAN_DATA_TYPE_SMOOTH:
internal_type = TOUCH_LL_READ_SMOOTH;
break;
case TOUCH_CHAN_DATA_TYPE_BENCHMARK:
internal_type = TOUCH_LL_READ_BENCHMARK;
break;
}
if (type <= TOUCH_CHAN_DATA_TYPE_BENCHMARK) {
TOUCH_ENTER_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
if (chan_handle != chan_handle->base->deep_slp_chan) {
for (int i = 0; i < sample_cfg_num; i++) {
touch_ll_read_chan_data(chan_handle->id, i, internal_type, &data[i]);
}
} else {
for (int i = 0; i < sample_cfg_num; i++) {
touch_ll_sleep_read_chan_data(internal_type, i, &data[i]);
}
}
TOUCH_EXIT_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
}
} else {
if (!chan_handle->is_prox_chan) {
ESP_EARLY_LOGW(TAG, "This is not a proximity sensing channel");
}
TOUCH_ENTER_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
/* Get the proximity value from the stored data.
* The proximity value are updated in the isr when proximity scanning is done */
for (int i = 0; i < sample_cfg_num; i++) {
data[i] = chan_handle->prox_val[i];
}
TOUCH_EXIT_CRITICAL_SAFE(TOUCH_PERIPH_LOCK);
}
return ESP_OK;
}
void touch_priv_set_benchmark(touch_channel_handle_t chan_handle, const touch_chan_benchmark_op_t *benchmark_op)
{
if (benchmark_op->do_reset) {
touch_ll_reset_chan_benchmark(BIT(chan_handle->id));
}
}
/******************************************************************************
* Scope: public APIs *
******************************************************************************/
esp_err_t touch_sensor_config_filter(touch_sensor_handle_t sens_handle, const touch_sensor_filter_config_t *filter_cfg)
{
TOUCH_NULL_POINTER_CHECK(sens_handle);
if (filter_cfg) {
ESP_RETURN_ON_FALSE(filter_cfg->benchmark.update.neg_limit >= filter_cfg->data.debounce_cnt,
ESP_ERR_INVALID_ARG, TAG, "The neg_limit should be greater than debounce_cnt");
}
esp_err_t ret = ESP_OK;
xSemaphoreTake(sens_handle->mutex, portMAX_DELAY);
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
if (filter_cfg) {
touch_ll_filter_enable(true);
/* Configure the benchmark filter and update strategy */
touch_ll_filter_set_filter_mode(filter_cfg->benchmark.filter_mode);
if (filter_cfg->benchmark.filter_mode == TOUCH_BM_JITTER_FILTER) {
touch_ll_filter_set_jitter_step(filter_cfg->benchmark.jitter_step);
}
touch_ll_filter_set_pos_noise_thresh(filter_cfg->benchmark.update.pos_thresh);
touch_ll_filter_set_neg_noise_thresh(filter_cfg->benchmark.update.neg_thresh, filter_cfg->benchmark.update.neg_limit);
/* Configure the touch data filter */
touch_ll_filter_set_smooth_mode(filter_cfg->data.smooth_filter);
touch_ll_filter_set_active_hysteresis(filter_cfg->data.active_hysteresis);
touch_ll_filter_set_debounce(filter_cfg->data.debounce_cnt);
} else {
touch_ll_filter_enable(false);
}
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
xSemaphoreGive(sens_handle->mutex);
return ret;
}
esp_err_t touch_sensor_config_sleep_wakeup(touch_sensor_handle_t sens_handle, const touch_sleep_config_t *sleep_cfg)
{
TOUCH_NULL_POINTER_CHECK(sens_handle);
esp_err_t ret = ESP_OK;
int dp_slp_chan_id = -1;
touch_hal_sample_config_t sample_cfg[TOUCH_SAMPLE_CFG_NUM] = {};
touch_hal_config_t hal_cfg = {
.sample_cfg = sample_cfg,
};
touch_hal_config_t *hal_cfg_ptr = NULL;
xSemaphoreTake(sens_handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(!sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Please disable the touch sensor first");
ESP_GOTO_ON_FALSE(sleep_cfg->slp_wakeup_lvl == TOUCH_LIGHT_SLEEP_WAKEUP || sleep_cfg->slp_wakeup_lvl == TOUCH_DEEP_SLEEP_WAKEUP,
ESP_ERR_INVALID_ARG, err, TAG, "Invalid sleep level");
if (sleep_cfg) {
/* Enabled touch sensor as wake-up source */
ESP_GOTO_ON_ERROR(esp_sleep_enable_touchpad_wakeup(), err, TAG, "Failed to enable touch sensor wakeup");
#if SOC_PM_SUPPORT_RC_FAST_PD
ESP_GOTO_ON_ERROR(esp_sleep_pd_config(ESP_PD_DOMAIN_RC_FAST, ESP_PD_OPTION_ON), err, TAG, "Failed to keep touch sensor module clock during the sleep");
#endif
/* If set the deep sleep channel (i.e., enable deep sleep wake-up),
configure the deep sleep related settings. */
if (sleep_cfg->slp_wakeup_lvl == TOUCH_DEEP_SLEEP_WAKEUP) {
ESP_GOTO_ON_FALSE(sleep_cfg->deep_slp_chan, ESP_ERR_INVALID_ARG, err, TAG, "deep sleep waken channel can't be NULL");
dp_slp_chan_id = sleep_cfg->deep_slp_chan->id;
/* Check and convert the configuration to hal configurations */
if (sleep_cfg->deep_slp_sens_cfg) {
hal_cfg_ptr = &hal_cfg;
ESP_GOTO_ON_ERROR(s_touch_convert_to_hal_config(sens_handle, (const touch_sensor_config_t *)sleep_cfg->deep_slp_sens_cfg, hal_cfg_ptr),
err, TAG, "parse the configuration failed due to the invalid configuration");
}
sens_handle->sleep_en = true;
sens_handle->deep_slp_chan = sleep_cfg->deep_slp_chan;
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
/* Set sleep threshold */
for (uint8_t smp_cfg_id = 0; smp_cfg_id < TOUCH_SAMPLE_CFG_NUM; smp_cfg_id++) {
touch_ll_sleep_set_threshold(smp_cfg_id, sleep_cfg->deep_slp_thresh[smp_cfg_id]);
}
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
}
} else {
/* Disable the touch sensor as wake-up source */
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TOUCHPAD);
#if SOC_PM_SUPPORT_RC_FAST_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_RC_FAST, ESP_PD_OPTION_AUTO);
#endif
sens_handle->sleep_en = false;
}
/* Save or update the sleep config */
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
touch_hal_save_sleep_config(dp_slp_chan_id, hal_cfg_ptr);
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
err:
xSemaphoreGive(sens_handle->mutex);
return ret;
}
// Water proof can be enabled separately
esp_err_t touch_sensor_config_waterproof(touch_sensor_handle_t sens_handle, const touch_waterproof_config_t *wp_cfg)
{
TOUCH_NULL_POINTER_CHECK(sens_handle);
esp_err_t ret = ESP_OK;
xSemaphoreTake(sens_handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(!sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Please disable the touch sensor first");
if (wp_cfg) {
// Check the validation of the waterproof configuration
TOUCH_NULL_POINTER_CHECK(wp_cfg->shield_chan);
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
sens_handle->waterproof_en = true;
sens_handle->guard_chan = wp_cfg->guard_chan;
sens_handle->shield_chan = wp_cfg->shield_chan;
touch_ll_waterproof_set_guard_chan(wp_cfg->guard_chan ? wp_cfg->guard_chan->id : TOUCH_LL_NULL_CHANNEL);
touch_ll_waterproof_set_shield_chan_mask(BIT(wp_cfg->shield_chan->id));
// need to disable the scanning of the shield channel
touch_ll_enable_scan_mask(BIT(wp_cfg->shield_chan->id), false);
touch_ll_waterproof_set_shield_driver(wp_cfg->shield_drv);
touch_ll_waterproof_enable(true);
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
} else {
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
touch_ll_waterproof_enable(false);
touch_ll_waterproof_set_guard_chan(TOUCH_LL_NULL_CHANNEL);
touch_ll_waterproof_set_shield_chan_mask(0);
touch_ll_enable_scan_mask(BIT(wp_cfg->shield_chan->id), true);
touch_ll_waterproof_set_shield_driver(0);
sens_handle->guard_chan = NULL;
sens_handle->shield_chan = NULL;
sens_handle->waterproof_en = false;
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
}
err:
xSemaphoreGive(sens_handle->mutex);
return ret;
}
esp_err_t touch_sensor_config_proximity_sensing(touch_sensor_handle_t sens_handle, const touch_proximity_config_t *prox_cfg)
{
TOUCH_NULL_POINTER_CHECK(sens_handle);
esp_err_t ret = ESP_OK;
xSemaphoreTake(sens_handle->mutex, portMAX_DELAY);
ESP_GOTO_ON_FALSE(!sens_handle->is_enabled, ESP_ERR_INVALID_STATE, err, TAG, "Please disable the touch sensor first");
TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK);
/* Reset proximity sensing part of all channels */
FOR_EACH_TOUCH_CHANNEL(i) {
if (sens_handle->ch[i] && sens_handle->ch[i]->is_prox_chan) {
sens_handle->ch[i]->is_prox_chan = false;
sens_handle->ch[i]->prox_cnt = 0;
for (int i = 0; i < TOUCH_SAMPLE_CFG_NUM; i++) {
sens_handle->ch[i]->prox_val[i] = 0;
}
}
}
if (prox_cfg) {
sens_handle->proximity_en = true;
uint8_t sample_cfg_num = sens_handle->sample_cfg_num;
for (int i = 0; i < TOUCH_PROXIMITY_CHAN_NUM; i++) {
if (prox_cfg->proximity_chan[i]) {
prox_cfg->proximity_chan[i]->is_prox_chan = true;
touch_ll_set_proximity_sensing_channel(i, prox_cfg->proximity_chan[i]->id);
} else {
touch_ll_set_proximity_sensing_channel(i, TOUCH_LL_NULL_CHANNEL);
}
}
touch_ll_proximity_set_total_scan_times(prox_cfg->scan_times * sample_cfg_num);
for (uint8_t smp_cfg_id = 0; smp_cfg_id < sample_cfg_num; smp_cfg_id++) {
touch_ll_proximity_set_charge_times(smp_cfg_id, prox_cfg->charge_times[smp_cfg_id]);
}
} else {
for (int i = 0; i < TOUCH_PROXIMITY_CHAN_NUM; i++) {
touch_ll_set_proximity_sensing_channel(i, TOUCH_LL_NULL_CHANNEL);
}
sens_handle->proximity_en = false;
}
TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK);
err:
xSemaphoreGive(sens_handle->mutex);
return ret;
}
@@ -0,0 +1,30 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "soc/soc_caps.h"
#include "hal/touch_sensor_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#define TOUCH_TOTAL_CHAN_NUM SOC_TOUCH_SENSOR_NUM /*!< The total channel number of the touch sensor */
#define TOUCH_SAMPLE_CFG_NUM SOC_TOUCH_SAMPLE_CFG_NUM /*!< The supported max sample configuration number */
#if SOC_TOUCH_SUPPORT_PROX_SENSING
#define TOUCH_PROXIMITY_CHAN_NUM SOC_TOUCH_PROXIMITY_CHANNEL_NUM /*!< The supported proximity channel number in proximity sensing mode */
#endif
typedef struct touch_sensor_s *touch_sensor_handle_t; /*!< The handle of touch sensor controller */
typedef struct touch_channel_s *touch_channel_handle_t; /*!< The handle of touch channel */
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,294 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "driver/touch_common_types.h"
#include "driver/touch_version_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Allocate a new touch sensor controller
* @note The touch sensor driver will be in INIT state after this function is called successfully.
*
* @param[in] sens_cfg Touch sensor controller configurations
* @param[out] ret_sens_handle The return handle of the touch controller instance
* @return
* - ESP_OK On success
* - ESP_ERR_NO_MEM No memory for the touch sensor controller
* - ESP_ERR_INVALID_ARG Invalid argument or NULL pointer
* - ESP_ERR_INVALID_STATE The touch sensor controller is already allocated
*/
esp_err_t touch_sensor_new_controller(const touch_sensor_config_t *sens_cfg, touch_sensor_handle_t *ret_sens_handle);
/**
* @brief Delete the touch sensor controller
* @note This function can be called when the touch sensor controller is NOT enabled (i.e. INIT state).
*
* @param[in] sens_handle Touch sensor controller handle
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG Invalid argument or NULL pointer
* - ESP_ERR_INVALID_STATE Controller not disabled or still some touch channels not deleted
*/
esp_err_t touch_sensor_del_controller(touch_sensor_handle_t sens_handle);
/**
* @brief Allocate a new touch channel from the touch sensor controller
* @note This function can be called when the touch sensor controller is NOT enabled (i.e. INIT state).
*
* @param[in] sens_handle Touch sensor controller handle
* @param[in] chan_id Touch channel index
* @param[in] chan_cfg Touch channel configurations
* @param[out] ret_chan_handle The return handle of the touch channel instance
* @return
* - ESP_OK On success
* - ESP_ERR_NO_MEM No memory for the touch sensor channel
* - ESP_ERR_INVALID_ARG Invalid argument or NULL pointer
* - ESP_ERR_INVALID_STATE The touch sensor controller has not disabled or this channel has been allocated
*/
esp_err_t touch_sensor_new_channel(touch_sensor_handle_t sens_handle, int chan_id,
const touch_channel_config_t *chan_cfg,
touch_channel_handle_t *ret_chan_handle);
/**
* @brief Delete the touch channel
* @note This function can be called when the touch sensor controller is NOT enabled (i.e. INIT state).
*
* @param[in] chan_handle Touch channel handle
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG Invalid argument or NULL pointer
* - ESP_ERR_INVALID_STATE The touch sensor controller has not disabled
*/
esp_err_t touch_sensor_del_channel(touch_channel_handle_t chan_handle);
/**
* @brief Re-configure the touch sensor controller
* @note This function can be called when the touch sensor controller is NOT enabled (i.e. INIT state).
* @note The re-configuration only applies to the touch controller,
* so it requires the controller handle that allocated from ``touch_sensor_new_controller``,
* meanwhile, it won't affect the touch channels, no matter the channels are allocated or not.
*
* @param[in] sens_handle Touch sensor controller handle
* @param[in] sens_cfg Touch sensor controller configurations
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG Invalid argument or NULL pointer
* - ESP_ERR_INVALID_STATE The touch sensor controller has not disabled
*/
esp_err_t touch_sensor_reconfig_controller(touch_sensor_handle_t sens_handle, const touch_sensor_config_t *sens_cfg);
/**
* @brief Re-configure the touch channel
* @note This function can be called when the touch sensor controller is NOT enabled (i.e. INIT state).
* @note The re-configuration only applies to a particular touch channel,
* so it requires the channel handle that allocated from ``touch_sensor_new_channel``
*
* @param[in] chan_handle Touch channel handle
* @param[in] chan_cfg Touch channel configurations
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG Invalid argument or NULL pointer
* - ESP_ERR_INVALID_STATE The touch sensor controller has not disabled
*/
esp_err_t touch_sensor_reconfig_channel(touch_channel_handle_t chan_handle, const touch_channel_config_t *chan_cfg);
/**
* @brief Configure the touch sensor filter
* @note This function is allowed to be called after the touch sensor is enabled
*
* @param[in] sens_handle Touch sensor controller handle
* @param[in] filter_cfg Filter configurations, set NULL to disable the filter
* @return
* - ESP_OK: Configure the filter success
* - ESP_ERR_INVALID_ARG: The sensor handle is NULL
*/
esp_err_t touch_sensor_config_filter(touch_sensor_handle_t sens_handle, const touch_sensor_filter_config_t *filter_cfg);
/**
* @brief Enable the touch sensor controller
* @note This function can be called when the touch sensor controller is NOT enabled (i.e. INIT state).
* And the touch sensor driver will be in ENABLED state after this function is called successfully.
* @note Enable the touch sensor will power on the registered touch channels
*
* @param[in] sens_handle Touch sensor controller handle
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG Invalid argument or NULL pointer
* - ESP_ERR_INVALID_STATE The touch sensor controller has already enabled
*/
esp_err_t touch_sensor_enable(touch_sensor_handle_t sens_handle);
/**
* @brief Disable the touch sensor controller
* @note This function can only be called after the touch sensor controller is enabled (i.e. ENABLED state).
* And the touch sensor driver will be in INIT state after this function is called successfully.
* @note Disable the touch sensor will power off the registered touch channels
*
* @param[in] sens_handle Touch sensor controller handle
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG Invalid argument or NULL pointer
* - ESP_ERR_INVALID_STATE The touch sensor controller has already disabled
*/
esp_err_t touch_sensor_disable(touch_sensor_handle_t sens_handle);
/**
* @brief Start the scanning of the registered touch channels continuously
* @note This function can only be called after the touch sensor controller is enabled (i.e. ENABLED state).
* And the touch sensor driver will be in SCANNING state after this function is called successfully.
* And it can also be called in ISR/callback context.
* @note The hardware FSM (Finite-State Machine) of touch sensor will be driven by
* its hardware timer continuously and repeatedly.
* i.e., the registered channels will be scanned one by one repeatedly.
*
* @param[in] sens_handle Touch sensor controller handle
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG Invalid argument or NULL pointer
* - ESP_ERR_INVALID_STATE The touch sensor controller is not enabled or the continuous scanning has started
*/
esp_err_t touch_sensor_start_continuous_scanning(touch_sensor_handle_t sens_handle);
/**
* @brief Stop the continuous scanning of the registered touch channels immediately
* @note This function can only be called after the continuous scanning started (i.e. SCANNING state).
* And the touch sensor driver will be in ENABLED state after this function is called successfully.
* And it can also be called in ISR/callback context.
* @note Stop the hardware timer and reset the FSM (Finite-State Machine) of the touch sensor controller
*
* @param[in] sens_handle Touch sensor controller handle
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG Invalid argument or NULL pointer
* - ESP_ERR_INVALID_STATE The continuous scanning has stopped
*/
esp_err_t touch_sensor_stop_continuous_scanning(touch_sensor_handle_t sens_handle);
/**
* @brief Trigger an oneshot scanning for all the registered channels
* @note This function can only be called after the touch sensor controller is enabled (i.e. ENABLED state).
* And the touch sensor driver will be in SCANNING state after this function is called successfully,
* and then switch back to ENABLED state after the scanning is done or timeout.
* @note The block time of this function depends on various factors,
* In common practice, recommend to set the timeout to a second-level timeout or wait forever,
* because oneshot scanning can't last for so long.
*
* @param[in] sens_handle Touch sensor controller handle
* @param[in] timeout_ms Set a positive value or zero means scan timeout in milliseconds
* Set a negative value means wait forever until finished scanning
* @return
* - ESP_OK On success
* - ESP_ERR_TIMEOUT Timeout to finish the oneshot scanning
* - ESP_ERR_INVALID_ARG Invalid argument
* - ESP_ERR_INVALID_STATE The touch sensor controller is not enabled or the continuous scanning has started
*/
esp_err_t touch_sensor_trigger_oneshot_scanning(touch_sensor_handle_t sens_handle, int timeout_ms);
/**
* @brief Register the touch sensor callbacks
* @note This function can be called when the touch sensor controller is NOT enabled (i.e. INIT state).
*
* @param[in] sens_handle Touch sensor controller handle
* @param[in] callbacks Callback functions
* @param[in] user_ctx User context that will be passed to the callback functions
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG NULL pointer
* - ESP_ERR_INVALID_STATE Touch sensor controller has not disabled
*/
esp_err_t touch_sensor_register_callbacks(touch_sensor_handle_t sens_handle, const touch_event_callbacks_t *callbacks, void *user_ctx);
/**
* @brief Set the touch sensor benchmark for all the registered channels
* @note This function can be called no matter the touch sensor controller is enabled or not (i.e. ENABLED or SCANNING state).
* And it can also be called in ISR/callback context.
*
* @param[in] chan_handle Touch channel handle
* @param[in] benchmark_op The benchmark operations
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG NULL pointer
*/
esp_err_t touch_sensor_set_benchmark(touch_channel_handle_t chan_handle, const touch_chan_benchmark_op_t *benchmark_op);
/**
* @brief Read the touch channel data according to the types
* @note This function can be called no matter the touch sensor controller is enabled or not (i.e. ENABLED or SCANNING state).
* And it can also be called in ISR/callback context.
* @note Specially, `TOUCH_CHAN_DATA_TYPE_PROXIMITY` data type will be read from the cached data in the driver,
* because the proximity data need to be accumulated in several scan times that specified by `touch_proximity_config_t::scan_times`.
* For other data types, the data are read from the hardware registers directly (not cached in the driver).
* The channel data in the register will be updated once the measurement of this channels is done,
* And keep locked until the next measurement is done.
*
* @param[in] chan_handle Touch channel handle
* @param[in] type Specify the data type to read
* @param[out] data The data array pointer. If the target supports multiple sample configurations (SOC_TOUCH_SAMPLE_CFG_NUM), the array length should be equal to
* the frequency hopping number that specified in `touch_sensor_config_t::sample_cfg_num`, otherwise the array length should be 1.
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG Invalid argument or NULL pointer
*/
esp_err_t touch_channel_read_data(touch_channel_handle_t chan_handle, touch_chan_data_type_t type, uint32_t *data);
#if SOC_TOUCH_SUPPORT_WATERPROOF
/**
* @brief Configure the touch sensor water proof channels
* @note Once waterproof is enabled, the other touch channels won't be updated unless the shield channels is activated.
*
* @param[in] sens_handle Touch sensor controller handle
* @param[in] wp_cfg Water proof channel configurations, set NULL to disable the water proof function
* @return
* - ESP_OK: Configure the water proof success
* - ESP_ERR_INVALID_ARG: The sensor handle is NULL
* - ESP_ERR_INVALID_STATE: The touch sensor is enabled
*/
esp_err_t touch_sensor_config_waterproof(touch_sensor_handle_t sens_handle, const touch_waterproof_config_t *wp_cfg);
#endif
#if SOC_TOUCH_SUPPORT_PROX_SENSING
/**
* @brief Configure the touch sensor proximity sensing channels
*
* @param[in] sens_handle Touch sensor controller handle
* @param[in] prox_cfg Proximity channels configurations, set NULL to disable the proximity sensing
* @return
* - ESP_OK: Configure the proximity channel success
* - ESP_ERR_INVALID_ARG: The sensor handle is NULL
* - ESP_ERR_INVALID_STATE: The touch sensor is enabled
*/
esp_err_t touch_sensor_config_proximity_sensing(touch_sensor_handle_t sens_handle, const touch_proximity_config_t *prox_cfg);
#endif
#if SOC_TOUCH_SUPPORT_SLEEP_WAKEUP
/**
* @brief Configure the touch sensor to wake-up the chip from sleep
* @note Call this function to enable/disable the touch sensor wake-up the chip from deep/light sleep
* To only enable the touch sensor wake-up the chip from light sleep, set the `touch_sleep_config_t::deep_slp_chan` to NULL.
* During the light sleep, all enabled touch channels will keep working, and any one of them can wake-up the chip from light sleep.
* To enable the touch sensor wake-up the chip from both light and deep sleep, set the `touch_sleep_config_t::deep_slp_chan` to an enabled channel.
* During the deep sleep, only this specified channels can work and wake-up the chip from the deep sleep,
* and other enabled channels can only wake-up the chip from light sleep.
*
* @param[in] sens_handle Touch sensor controller handle
* @param[in] sleep_cfg Sleep wake-up configurations, set NULL to disable the touch sensor wake-up the chip from sleep
* @return
* - ESP_OK: Configure the sleep channel success
* - ESP_ERR_INVALID_ARG: The sensor handle is NULL
* - ESP_ERR_INVALID_STATE: The touch sensor is enabled
*/
esp_err_t touch_sensor_config_sleep_wakeup(touch_sensor_handle_t sens_handle, const touch_sleep_config_t *sleep_cfg);
#endif
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,10 @@
[mapping:touch_sens_driver]
archive: libesp_driver_touch_sens.a
entries:
if TOUCH_CTRL_FUNC_IN_IRAM = y:
touch_sens_common: touch_sensor_start_continuous_scanning (noflash)
touch_sens_common: touch_sensor_stop_continuous_scanning (noflash)
touch_sens_common: touch_channel_read_data (noflash)
touch_sens_common: touch_sensor_set_benchmark (noflash)
touch_sens_version_specific: touch_priv_channel_read_data (noflash)
touch_sens_version_specific: touch_priv_set_benchmark (noflash)