feat(touch_sens): add example for the touch sensor v3

This commit is contained in:
laokaiyao 2024-06-07 17:25:35 +08:00 committed by Kevin (Lao Kaiyao)
parent 8a6f25738c
commit 82cba6e3ff
11 changed files with 450 additions and 5 deletions

View File

@ -433,6 +433,16 @@ examples/peripherals/touch_sensor/touch_sensor_v2:
disable:
- if: SOC_TOUCH_SENSOR_VERSION != 2
examples/peripherals/touch_sensor/touch_sensor_v3:
disable:
- if: SOC_TOUCH_SENSOR_VERSION != 3
disable_test:
- if: IDF_TARGET == "esp32p4"
temporary: true
reason: the runners do not support the pins for touch sensor
depends_components:
- esp_driver_touch_sens
examples/peripherals/twai/twai_alert_and_recovery:
disable:
- if: SOC_TWAI_SUPPORTED != 1

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(touch_sens_example)

View File

@ -0,0 +1,125 @@
| Supported Targets | ESP32-P4 |
| ----------------- | -------- |
# Capacity Touch Sensor Example (for hardware version 3)
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example is going to demonstrate how to register the touch channels and read the data.
## How to Use Example
### Hardware Required
* A development board with any supported Espressif SOC chip (see `Supported Targets` table above)
* A USB cable for power supply and programming
* (Optional) Touch board with touch buttons on it.
- If you don't have a touch board, you can connect the touch pins with male jump wires and touch it directly for testing.
### Configure the Project
You can determine the touch channel number by ``EXAMPLE_TOUCH_CHANNEL_NUM`` in the example. And adjust the active threshold by ``s_thresh2bm_ratio``.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT build flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
You can see the following output in the monitor if the example runs successfully:
```
W (461) touch: [sample_cfg_id 0] clock precision loss, expect 4000000 hz, got 4006725 hz
W (461) touch: [sample_cfg_id 1] clock precision loss, expect 8000000 hz, got 8013450 hz
W (461) touch: [sample_cfg_id 2] clock precision loss, expect 16000000 hz, got 16026900 hz
Initial benchmark and new threshold are:
[CH 0] 0: 4114, 411 1: 2057, 205 2: 1028, 102
[CH 1] 0: 4643, 464 1: 2322, 232 2: 1160, 116
[CH 2] 0: 4848, 484 1: 2424, 242 2: 1211, 121
[CH 3] 0: 4340, 434 1: 2170, 217 2: 1085, 108
=================================
benchmark [CH 0]: 4115 2056 1028
chan_data [CH 0]: 4115 2056 1028
benchmark [CH 1]: 4644 2322 1160
chan_data [CH 1]: 4644 2322 1160
benchmark [CH 2]: 4848 2423 1211
chan_data [CH 2]: 4848 2423 1211
benchmark [CH 3]: 4337 2168 1084
chan_data [CH 3]: 4337 2168 1084
=================================
benchmark [CH 0]: 4109 2054 1027
chan_data [CH 0]: 4109 2054 1027
benchmark [CH 1]: 4638 2318 1158
chan_data [CH 1]: 4638 2318 1158
benchmark [CH 2]: 4843 2421 1210
chan_data [CH 2]: 4845 2421 1210
benchmark [CH 3]: 4334 2167 1084
chan_data [CH 3]: 4334 2167 1083
...
```
And if you touch and release a button, you will see the following output:
```
...
I (1321) touch_callback: [CH 1] active
=================================
benchmark [CH 0]: 4111 2055 1027
chan_data [CH 0]: 4111 2055 1027
benchmark [CH 1]: 4676 2339 1168
chan_data [CH 1]: 17701 8798 4399
benchmark [CH 2]: 4870 2434 1217
chan_data [CH 2]: 4867 2433 1217
benchmark [CH 3]: 4333 2165 1082
chan_data [CH 3]: 4333 2165 1082
=================================
benchmark [CH 0]: 4109 2053 1027
chan_data [CH 0]: 4108 2053 1027
benchmark [CH 1]: 4676 2339 1168
chan_data [CH 1]: 11256 8817 4363
benchmark [CH 2]: 4868 2434 1217
chan_data [CH 2]: 4862 2429 1214
benchmark [CH 3]: 4332 2165 1082
chan_data [CH 3]: 4330 2164 1081
I (1931) touch_callback: [CH 1] inactive
=================================
benchmark [CH 0]: 4106 2052 1026
chan_data [CH 0]: 4106 2052 1026
benchmark [CH 1]: 4649 2323 1161
chan_data [CH 1]: 4650 2323 1161
benchmark [CH 2]: 4847 2422 1211
chan_data [CH 2]: 4846 2422 1211
benchmark [CH 3]: 4329 2163 1082
chan_data [CH 3]: 4329 2164 1082
...
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "touch_sens_v3_example_main.c"
PRIV_REQUIRES esp_driver_touch_sens
INCLUDE_DIRS ".")

View File

@ -0,0 +1,169 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/touch_sens.h"
#include "soc/lp_analog_peri_struct.h"
#include "esp_check.h"
// Touch version 3 supports multiple sample configurations
#define EXAMPLE_TOUCH_SAMPLE_CFG_NUM TOUCH_SAMPLE_CFG_NUM
#define EXAMPLE_TOUCH_CHANNEL_NUM 4
#define EXAMPLE_TOUCH_CHAN_INIT_SCAN_TIMES 3
static touch_sensor_handle_t s_sens_handle = NULL;
static touch_channel_handle_t s_chan_handle[EXAMPLE_TOUCH_CHANNEL_NUM] = {};
// Active threshold to benchmark ratio. (i.e., touch will be activated when data >= benchmark * (1 + ratio))
static float s_thresh2bm_ratio[EXAMPLE_TOUCH_CHANNEL_NUM] = {
[0 ... EXAMPLE_TOUCH_CHANNEL_NUM - 1] = 0.02f, // 2%
};
bool example_touch_on_active_callback(touch_sensor_handle_t sens_handle, const touch_active_event_data_t *event, void *user_ctx)
{
ESP_EARLY_LOGI("touch_callback", "[CH %d] active", (int)event->chan_id);
return false;
}
bool example_touch_on_inactive_callback(touch_sensor_handle_t sens_handle, const touch_inactive_event_data_t *event, void *user_ctx)
{
ESP_EARLY_LOGI("touch_callback", "[CH %d] inactive", (int)event->chan_id);
return false;
}
static void example_touch_do_initial_scanning(void)
{
/* Enable the touch sensor to do the initial scanning, so that to initialize the channel data */
ESP_ERROR_CHECK(touch_sensor_enable(s_sens_handle));
/* Scan the enabled touch channels for several times, to make sure the initial channel data is stable */
for (int i = 0; i < EXAMPLE_TOUCH_CHAN_INIT_SCAN_TIMES; i++) {
ESP_ERROR_CHECK(touch_sensor_trigger_oneshot_scanning(s_sens_handle, 2000));
}
/* Disable the touch channel to rollback the state */
ESP_ERROR_CHECK(touch_sensor_disable(s_sens_handle));
/* (Optional) Read the initial channel benchmark and reconfig the channel active threshold accordingly */
printf("Initial benchmark and new threshold are:\n");
for (int i = 0; i < EXAMPLE_TOUCH_CHANNEL_NUM; i++) {
/* Read the initial benchmark of the touch channel */
uint32_t benchmark[EXAMPLE_TOUCH_SAMPLE_CFG_NUM] = {};
ESP_ERROR_CHECK(touch_channel_read_data(s_chan_handle[i], TOUCH_CHAN_DATA_TYPE_BENCHMARK, benchmark));
/* Calculate the proper active thresholds regarding the initial benchmark */
printf("[CH %d]", i);
touch_channel_config_t chan_cfg = {};
for (int j = 0; j < EXAMPLE_TOUCH_SAMPLE_CFG_NUM; j++) {
chan_cfg.active_thresh[j] = (uint32_t)(benchmark[j] * s_thresh2bm_ratio[j]);
printf(" %d: %"PRIu32", %"PRIu32"\t", j, benchmark[j], chan_cfg.active_thresh[j]);
}
printf("\n");
/* Update the channel configuration */
ESP_ERROR_CHECK(touch_sensor_reconfig_channel(s_chan_handle[i], &chan_cfg));
}
}
void app_main(void)
{
/* Use the default sample configurations */
touch_sensor_sample_config_t sample_cfg[EXAMPLE_TOUCH_SAMPLE_CFG_NUM] = {
TOUCH_SENSOR_DEFAULT_SAMPLE_CONFIG0(),
#if EXAMPLE_TOUCH_SAMPLE_CFG_NUM > 1
TOUCH_SENSOR_DEFAULT_SAMPLE_CONFIG1(),
#endif
#if EXAMPLE_TOUCH_SAMPLE_CFG_NUM > 2
TOUCH_SENSOR_DEFAULT_SAMPLE_CONFIG2(),
#endif
};
/* Allocate new touch controller handle */
touch_sensor_config_t sens_cfg = TOUCH_SENSOR_DEFAULT_BASIC_CONFIG(EXAMPLE_TOUCH_SAMPLE_CFG_NUM, sample_cfg);
ESP_ERROR_CHECK(touch_sensor_new_controller(&sens_cfg, &s_sens_handle));
/* Configure the touch sensor filter */
touch_sensor_filter_config_t filter_cfg = TOUCH_SENSOR_DEFAULT_FILTER_CONFIG();
ESP_ERROR_CHECK(touch_sensor_config_filter(s_sens_handle, &filter_cfg));
/* Allocate new touch channel on the touch controller */
touch_channel_config_t chan_cfg = {
/** Set the touch channel active threshold of each sample configuration.
*
* @How to Determine:
* As the actual threshold is affected by various factors in real application,
* we need to run the touch app first to get the `benchmark` and the `smooth_data` that being touched.
*
* @Formula:
* threshold = benchmark * coeff, (coeff for example, 0.1%~20%)
* Please adjust the coeff to guarantee the threshold < smooth_data - benchmark
*
* @Typical Practice:
* Normally, we can't determine a fixed threshold at the beginning,
* but we can give them estimated values first and update them after an initial scanning (like this example),
* Step1: set an estimated value for each sample configuration first. (i.e., here)
* Step2: then reconfig the threshold after the initial scanning.(see `example_touch_do_initial_scanning`)
* Step3: adjust the `s_thresh2bm_ratio` to a proper value to trigger the active callback
*/
.active_thresh = {
5000, // estimated active threshold of sample configuration 0
#if EXAMPLE_TOUCH_SAMPLE_CFG_NUM > 1
2500, // estimated active threshold of sample configuration 1
#endif
#if EXAMPLE_TOUCH_SAMPLE_CFG_NUM > 2
1000, // estimated active threshold of sample configuration 2
#endif
},
};
for (int i = 0; i < EXAMPLE_TOUCH_CHANNEL_NUM; i++) {
ESP_ERROR_CHECK(touch_sensor_new_channel(s_sens_handle, i, &chan_cfg, &s_chan_handle[i]));
}
/* Do the initial scanning to initialize the touch channel data
* Without this step, the channel data in the first read will be invalid
*/
example_touch_do_initial_scanning();
/* Register the touch sensor callbacks, here only take `active` and `deactivate` event for example */
touch_event_callbacks_t callbacks = {
.on_active = example_touch_on_active_callback,
.on_inactive = example_touch_on_inactive_callback,
.on_measure_done = NULL,
.on_scan_done = NULL,
.on_timeout = NULL,
.on_proximity_meas_done = NULL,
};
ESP_ERROR_CHECK(touch_sensor_register_callbacks(s_sens_handle, &callbacks, NULL));
/* Enable the touch sensor */
ESP_ERROR_CHECK(touch_sensor_enable(s_sens_handle));
/* Start continuous scanning, you can also trigger oneshot scanning manually */
ESP_ERROR_CHECK(touch_sensor_start_continuous_scanning(s_sens_handle));
uint32_t benchmark[EXAMPLE_TOUCH_SAMPLE_CFG_NUM] = {};
uint32_t chan_data[EXAMPLE_TOUCH_SAMPLE_CFG_NUM] = {};
while (1) {
printf("=================================\n");
for (int i = 0; i < EXAMPLE_TOUCH_CHANNEL_NUM; i++) {
/* Read and print the benchmark of each sample configuration */
ESP_ERROR_CHECK(touch_channel_read_data(s_chan_handle[i], TOUCH_CHAN_DATA_TYPE_BENCHMARK, benchmark));
printf("benchmark [CH %d]:", i);
for (int j = 0; j < EXAMPLE_TOUCH_SAMPLE_CFG_NUM; j++) {
printf(" %"PRIu32, benchmark[j]);
}
printf("\n");
/* Read and print the channel data of each sample configuration */
ESP_ERROR_CHECK(touch_channel_read_data(s_chan_handle[i], TOUCH_CHAN_DATA_TYPE_SMOOTH, chan_data));
printf("chan_data [CH %d]:", i);
for (int j = 0; j < EXAMPLE_TOUCH_SAMPLE_CFG_NUM; j++) {
printf(" %"PRIu32, chan_data[j]);
}
printf("\n\n");
}
/* Read and display the data every 300 ms */
vTaskDelay(pdMS_TO_TICKS(300));
}
}

View File

@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32p4
@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='esp32p4 runners do not support touch pins')
@pytest.mark.generic
def test_touch_sens_v3(dut: Dut) -> None:
dut.expect_exact('Initial benchmark and new threshold are:')
dut.expect(r'\[CH [0-9]+\] 0: [0-9]+, [0-9]+')
dut.expect(r'benchmark \[CH [0-9]+\]: [0-9]+')
dut.expect(r'chan_data \[CH [0-9]+\]: [0-9]+')

View File

@ -50,7 +50,7 @@ void example_deep_sleep_register_touch_wakeup(void)
// If use touch pad wake up, should set touch sensor FSM mode at 'TOUCH_FSM_MODE_TIMER'.
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
// Set reference voltage for charging/discharging
// In this case, the high reference valtage will be 2.4V - 1V = 1.4V
// In this case, the high reference voltage will be 2.4V - 1V = 1.4V
// The low reference voltage will be 0.5
// The larger the range, the larger the pulse count value.
touch_pad_set_voltage(TOUCH_HVOLT_2V4, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V);
@ -104,6 +104,8 @@ void example_deep_sleep_register_touch_wakeup(void)
#endif
printf("Enabling touch pad wakeup\n");
ESP_ERROR_CHECK(esp_sleep_enable_touchpad_wakeup());
#if SOC_PM_SUPPORT_RTC_PERIPH_PD
ESP_ERROR_CHECK(esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON));
#endif
}
#endif // CONFIG_EXAMPLE_TOUCH_WAKEUP

View File

@ -9,5 +9,9 @@ if(IDF_TARGET IN_LIST TOUCH_ELEMENT_COMPATIBLE_TARGETS)
list(APPEND srcs "touch_wakeup.c")
endif()
if(IDF_TARGET STREQUAL "esp32p4")
list(APPEND srcs "touch_sens_wakeup.c")
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ".")

View File

@ -1,15 +1,20 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
// TODO: [ESP32P4] add P4 when runner is ready
#define EXAMPLE_TOUCH_LSLEEP_WAKEUP_SUPPORT (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3)
void example_wait_gpio_inactive(void);
esp_err_t example_register_gpio_wakeup(void);
@ -18,7 +23,7 @@ esp_err_t example_register_timer_wakeup(void);
esp_err_t example_register_uart_wakeup(void);
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#if EXAMPLE_TOUCH_LSLEEP_WAKEUP_SUPPORT
void example_register_touch_wakeup(void);
#endif

View File

@ -50,7 +50,7 @@ static void light_sleep_task(void *args)
* Otherwise the chip may fall sleep again before running uart task */
vTaskDelay(1);
break;
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#if EXAMPLE_TOUCH_LSLEEP_WAKEUP_SUPPORT
case ESP_SLEEP_WAKEUP_TOUCHPAD:
wakeup_reason = "touch";
break;
@ -83,7 +83,7 @@ void app_main(void)
example_register_timer_wakeup();
/* Enable wakeup from light sleep by uart */
example_register_uart_wakeup();
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#if EXAMPLE_TOUCH_LSLEEP_WAKEUP_SUPPORT
/* Enable wakeup from light sleep by touch element */
example_register_touch_wakeup();
#endif

View File

@ -0,0 +1,107 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "freertos/FreeRTOS.h"
#include "esp_log.h"
#include "driver/touch_sens.h"
static const char *TAG = "touch_wakeup";
#define EXAMPLE_TOUCH_SAMPLE_CFG_NUM 1
#define EXAMPLE_TOUCH_CHANNEL_NUM 5
#define EXAMPLE_TOUCH_CHAN_INIT_SCAN_TIMES 3
// Active threshold to benchmark ratio. (i.e., touch will be activated when data >= benchmark * (1 + ratio))
static float s_thresh2bm_ratio[EXAMPLE_TOUCH_CHANNEL_NUM] = {
[0 ... EXAMPLE_TOUCH_CHANNEL_NUM - 1] = 0.02f, // 2%
};
static bool example_touch_on_active_cb(touch_sensor_handle_t sens_handle, const touch_active_event_data_t *event, void *user_ctx)
{
ESP_EARLY_LOGW("isr", "ch %d active", (int)event->chan_id);
return false;
}
static bool example_touch_on_inactive_cb(touch_sensor_handle_t sens_handle, const touch_inactive_event_data_t *event, void *user_ctx)
{
ESP_EARLY_LOGW("isr", "ch %d inactive", (int)event->chan_id);
return false;
}
static void example_touch_do_initial_scanning(touch_sensor_handle_t sens_handle, touch_channel_handle_t chan_handle[])
{
/* Enable the touch sensor to do the initial scanning, so that to initialize the channel data */
ESP_ERROR_CHECK(touch_sensor_enable(sens_handle));
/* Scan the enabled touch channels for several times, to make sure the initial channel data is stable */
for (int i = 0; i < EXAMPLE_TOUCH_CHAN_INIT_SCAN_TIMES; i++) {
ESP_ERROR_CHECK(touch_sensor_trigger_oneshot_scanning(sens_handle, 2000));
}
/* Disable the touch channel to rollback the state */
ESP_ERROR_CHECK(touch_sensor_disable(sens_handle));
/* (Optional) Read the initial channel benchmark and reconfig the channel active threshold accordingly */
printf("Initial benchmark and new threshold are:\n");
for (int i = 0; i < EXAMPLE_TOUCH_CHANNEL_NUM; i++) {
/* Read the initial benchmark of the touch channel */
uint32_t benchmark[EXAMPLE_TOUCH_SAMPLE_CFG_NUM] = {};
ESP_ERROR_CHECK(touch_channel_read_data(chan_handle[i], TOUCH_CHAN_DATA_TYPE_BENCHMARK, benchmark));
/* Calculate the proper active thresholds regarding the initial benchmark */
printf("[CH %d]", i);
touch_channel_config_t chan_cfg = {};
for (int j = 0; j < EXAMPLE_TOUCH_SAMPLE_CFG_NUM; j++) {
chan_cfg.active_thresh[j] = (uint32_t)(benchmark[j] * s_thresh2bm_ratio[j]);
printf(" %d: %"PRIu32", %"PRIu32"\t", j, benchmark[j], chan_cfg.active_thresh[j]);
}
printf("\n");
/* Update the channel configuration */
ESP_ERROR_CHECK(touch_sensor_reconfig_channel(chan_handle[i], &chan_cfg));
}
}
esp_err_t example_register_touch_wakeup(void)
{
touch_sensor_handle_t sens_handle = NULL;
touch_sensor_sample_config_t sample_cfg[EXAMPLE_TOUCH_SAMPLE_CFG_NUM] = {
TOUCH_SENSOR_DEFAULT_SAMPLE_CONFIG0(),
};
touch_sensor_config_t sens_cfg = TOUCH_SENSOR_DEFAULT_BASIC_CONFIG(EXAMPLE_TOUCH_SAMPLE_CFG_NUM, sample_cfg);
ESP_ERROR_CHECK(touch_sensor_new_controller(&sens_cfg, &sens_handle));
touch_sensor_filter_config_t filter_cfg = TOUCH_SENSOR_DEFAULT_FILTER_CONFIG();
ESP_ERROR_CHECK(touch_sensor_config_filter(sens_handle, &filter_cfg));
touch_channel_handle_t chan_handle[EXAMPLE_TOUCH_CHANNEL_NUM];
touch_channel_config_t chan_cfg = {
.active_thresh = {5000}, // Initial threshold
};
for (int i = 0; i < EXAMPLE_TOUCH_CHANNEL_NUM; i++) {
ESP_ERROR_CHECK(touch_sensor_new_channel(sens_handle, i, &chan_cfg, &chan_handle[i]));
}
/* (Optional) Do the initial scanning to initialize the touch channel data
* Without this step, the channel data in the first read will be invalid
*/
example_touch_do_initial_scanning(sens_handle, chan_handle);
touch_event_callbacks_t callbacks = {
.on_active = example_touch_on_active_cb,
.on_inactive = example_touch_on_inactive_cb,
};
ESP_ERROR_CHECK(touch_sensor_register_callbacks(sens_handle, &callbacks, NULL));
touch_sleep_config_t light_slp_cfg = {
.slp_wakeup_lvl = TOUCH_LIGHT_SLEEP_WAKEUP,
};
ESP_ERROR_CHECK(touch_sensor_config_sleep_wakeup(sens_handle, &light_slp_cfg));
ESP_ERROR_CHECK(touch_sensor_enable(sens_handle));
ESP_ERROR_CHECK(touch_sensor_start_continuous_scanning(sens_handle));
ESP_LOGI(TAG, "touch wakeup source is ready");
return ESP_OK;
}