diff --git a/components/esp_driver_touch_sens/common/touch_sens_common.c b/components/esp_driver_touch_sens/common/touch_sens_common.c index 46c1fac6e5..f996073a92 100644 --- a/components/esp_driver_touch_sens/common/touch_sens_common.c +++ b/components/esp_driver_touch_sens/common/touch_sens_common.c @@ -32,7 +32,7 @@ #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, \ +#define TOUCH_CHANNEL_CHECK(num) ESP_RETURN_ON_FALSE((int)(num) >= (int)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"; diff --git a/components/esp_driver_touch_sens/hw_ver1/include/driver/touch_version_types.h b/components/esp_driver_touch_sens/hw_ver1/include/driver/touch_version_types.h index 08db408e59..877a698f80 100644 --- a/components/esp_driver_touch_sens/hw_ver1/include/driver/touch_version_types.h +++ b/components/esp_driver_touch_sens/hw_ver1/include/driver/touch_version_types.h @@ -19,9 +19,6 @@ extern "C" { #endif -#define TOUCH_MIN_CHAN_ID 0 /*!< The minimum available channel id of the touch pad */ -#define TOUCH_MAX_CHAN_ID 9 /*!< The maximum available channel id of the touch pad */ - /** * @brief Helper macro to the default configurations of the touch sensor controller * diff --git a/components/esp_driver_touch_sens/hw_ver2/include/driver/touch_version_types.h b/components/esp_driver_touch_sens/hw_ver2/include/driver/touch_version_types.h index a0c1f595a5..6d92c5273d 100644 --- a/components/esp_driver_touch_sens/hw_ver2/include/driver/touch_version_types.h +++ b/components/esp_driver_touch_sens/hw_ver2/include/driver/touch_version_types.h @@ -19,9 +19,6 @@ extern "C" { #endif -#define TOUCH_MIN_CHAN_ID 1 /*!< The minimum available channel id of the touch pad */ -#define TOUCH_MAX_CHAN_ID 14 /*!< The maximum available channel id of the touch pad */ - #define TOUCH_SHIELD_CHAN_ID 14 /*!< The touch channel that can be used as the shield channel */ /** diff --git a/components/esp_driver_touch_sens/hw_ver3/include/driver/touch_version_types.h b/components/esp_driver_touch_sens/hw_ver3/include/driver/touch_version_types.h index cb9e0bfb62..078fd7d1c1 100644 --- a/components/esp_driver_touch_sens/hw_ver3/include/driver/touch_version_types.h +++ b/components/esp_driver_touch_sens/hw_ver3/include/driver/touch_version_types.h @@ -19,9 +19,6 @@ 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 * diff --git a/components/esp_driver_touch_sens/include/driver/touch_sens_types.h b/components/esp_driver_touch_sens/include/driver/touch_sens_types.h index 97e6bf9886..5d7e2d548d 100644 --- a/components/esp_driver_touch_sens/include/driver/touch_sens_types.h +++ b/components/esp_driver_touch_sens/include/driver/touch_sens_types.h @@ -22,6 +22,9 @@ extern "C" { #define TOUCH_PROXIMITY_CHAN_NUM SOC_TOUCH_PROXIMITY_CHANNEL_NUM /*!< The supported proximity channel number in proximity sensing mode */ #endif +#define TOUCH_MIN_CHAN_ID SOC_TOUCH_MIN_CHAN_ID /*!< The minimum available channel id of the touch pad */ +#define TOUCH_MAX_CHAN_ID SOC_TOUCH_MAX_CHAN_ID /*!< The maximum available channel id of the touch pad */ + /** * @brief The chip sleep level that allows the touch sensor to wake-up * diff --git a/components/hal/esp32p4/include/hal/touch_sensor_ll.h b/components/hal/esp32p4/include/hal/touch_sensor_ll.h index 76639412b9..95592ad332 100644 --- a/components/hal/esp32p4/include/hal/touch_sensor_ll.h +++ b/components/hal/esp32p4/include/hal/touch_sensor_ll.h @@ -584,6 +584,18 @@ static inline void touch_ll_sample_cfg_set_engaged_num(uint8_t sample_cfg_num) LP_ANA_PERI.touch_scan_ctrl2.freq_scan_cnt_limit = sample_cfg_num ? sample_cfg_num : 1; } +/** + * Get the engaged sample configuration number + * + * @return The engaged sample configuration number, range 0~3. + */ +static inline uint32_t touch_ll_sample_cfg_get_engaged_num(void) +{ + uint32_t sample_cfg_num = LP_ANA_PERI.touch_scan_ctrl2.freq_scan_cnt_limit; + return sample_cfg_num ? sample_cfg_num : 1; +} + + /** * Set capacitance and resistance of the RC filter of the sampling frequency. * diff --git a/components/hal/esp32s2/include/hal/touch_sensor_ll.h b/components/hal/esp32s2/include/hal/touch_sensor_ll.h index 2ce086cc6f..a1d70d4197 100644 --- a/components/hal/esp32s2/include/hal/touch_sensor_ll.h +++ b/components/hal/esp32s2/include/hal/touch_sensor_ll.h @@ -1682,6 +1682,29 @@ static inline void touch_ll_sleep_read_data(uint32_t *raw_data) *raw_data = SENS.sar_touch_status[touch_num - 1].touch_pad_data; } +/** + * Get the data of the touch channel according to the types + * + * @param sample_cfg_id The sample configuration index + * @param type data type + * 0/1: TOUCH_LL_READ_RAW, the raw data of the touch channel + * 2: TOUCH_LL_READ_BENCHMARK, benchmark value of touch channel, + * the benchmark value is the maximum during the first measurement period + * 3: TOUCH_LL_READ_SMOOTH, the smoothed data that obtained by filtering the raw data. + * @param smooth_data pointer to smoothed data + */ +__attribute__((always_inline)) +static inline void touch_ll_sleep_read_chan_data(uint8_t type, uint32_t *data) +{ + SENS.sar_touch_conf.touch_data_sel = type; + if (type == TOUCH_LL_READ_RAW) { + uint32_t touch_num = RTCCNTL.touch_slp_thres.touch_slp_pad; + *data = SENS.sar_touch_status[touch_num - 1].touch_pad_data; + } else { + *data = SENS.sar_touch_slp_status.touch_slp_data; + } +} + /** * Select touch sensor dbias to save power in sleep mode. * diff --git a/components/hal/esp32s3/include/hal/touch_sensor_ll.h b/components/hal/esp32s3/include/hal/touch_sensor_ll.h index d57129e5c0..3a9e1824f7 100644 --- a/components/hal/esp32s3/include/hal/touch_sensor_ll.h +++ b/components/hal/esp32s3/include/hal/touch_sensor_ll.h @@ -1718,6 +1718,29 @@ static inline void touch_ll_sleep_read_data(uint32_t *raw_data) *raw_data = SENS.sar_touch_status[touch_num - 1].touch_pad_data; } +/** + * Get the data of the touch channel according to the types + * + * @param sample_cfg_id The sample configuration index + * @param type data type + * 0/1: TOUCH_LL_READ_RAW, the raw data of the touch channel + * 2: TOUCH_LL_READ_BENCHMARK, benchmark value of touch channel, + * the benchmark value is the maximum during the first measurement period + * 3: TOUCH_LL_READ_SMOOTH, the smoothed data that obtained by filtering the raw data. + * @param smooth_data pointer to smoothed data + */ +__attribute__((always_inline)) +static inline void touch_ll_sleep_read_chan_data(uint8_t type, uint32_t *data) +{ + SENS.sar_touch_conf.touch_data_sel = type; + if (type == TOUCH_LL_READ_RAW) { + uint32_t touch_num = RTCCNTL.touch_slp_thres.touch_slp_pad; + *data = SENS.sar_touch_status[touch_num - 1].touch_pad_data; + } else { + *data = SENS.sar_touch_slp_status.touch_slp_data; + } +} + /** * Select touch sensor dbias to save power in sleep mode. diff --git a/components/soc/esp32/include/soc/Kconfig.soc_caps.in b/components/soc/esp32/include/soc/Kconfig.soc_caps.in index 7d90fdcc62..0ba999f747 100644 --- a/components/soc/esp32/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32/include/soc/Kconfig.soc_caps.in @@ -735,6 +735,14 @@ config SOC_TOUCH_SENSOR_NUM int default 10 +config SOC_TOUCH_MIN_CHAN_ID + int + default 0 + +config SOC_TOUCH_MAX_CHAN_ID + int + default 9 + config SOC_TOUCH_SUPPORT_SLEEP_WAKEUP bool default y diff --git a/components/soc/esp32/include/soc/soc_caps.h b/components/soc/esp32/include/soc/soc_caps.h index 1273854c26..1937908170 100644 --- a/components/soc/esp32/include/soc/soc_caps.h +++ b/components/soc/esp32/include/soc/soc_caps.h @@ -336,6 +336,8 @@ /*-------------------------- TOUCH SENSOR CAPS -------------------------------*/ #define SOC_TOUCH_SENSOR_VERSION (1U) /*!= SOC_TOUCH_MAX_CHAN_ID || \ + channel < SOC_TOUCH_MIN_CHAN_ID) { \ + return ESP_ERR_INVALID_ARG; \ + } \ +} \ + +esp_err_t lp_core_touch_pad_read_raw_data(int touch_num, uint32_t *raw_data) +{ + /* Check Arguments */ + if (!raw_data) { + return ESP_ERR_INVALID_ARG; + } + LP_CORE_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); + + /* Read raw touch data */ + uint32_t engaged_sampler_cnt = touch_ll_sample_cfg_get_engaged_num(); + for (int i = 0; i < engaged_sampler_cnt; i++) { + // Only have smooth data on V3, no raw data + touch_ll_read_chan_data((int)touch_num, i + 1, TOUCH_LL_READ_SMOOTH, &raw_data[i]); + } + + return ESP_OK; +} + +esp_err_t lp_core_touch_pad_read_benchmark(int touch_num, uint32_t *benchmark) +{ + /* Check Arguments */ + if (!benchmark) { + return ESP_ERR_INVALID_ARG; + } + LP_CORE_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); + + /* Read benchmark data */ + uint32_t engaged_sampler_cnt = touch_ll_sample_cfg_get_engaged_num(); + for (int i = 0; i < engaged_sampler_cnt; i++) { + touch_ll_read_chan_data((int)touch_num, i + 1, TOUCH_LL_READ_BENCHMARK, &benchmark[i]); + } + + return ESP_OK; +} + +esp_err_t lp_core_touch_pad_filter_read_smooth(int touch_num, uint32_t *smooth_data) +{ + /* Check Arguments */ + if (!smooth_data) { + return ESP_ERR_INVALID_ARG; + } + LP_CORE_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); + + /* Read smoothened touch sensor data */ + uint32_t engaged_sampler_cnt = touch_ll_sample_cfg_get_engaged_num(); + for (int i = 0; i < engaged_sampler_cnt; i++) { + touch_ll_read_chan_data((int)touch_num, i + 1, TOUCH_LL_READ_SMOOTH, &smooth_data[i]); + } + + return ESP_OK; +} + +esp_err_t lp_core_touch_pad_reset_benchmark(int touch_num, uint32_t mask) +{ + (void) mask; // Currently not support, reserved for future use + /* Check Arguments */ + LP_CORE_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); + + /* Reset benchmark */ + touch_ll_reset_chan_benchmark(BIT(touch_num)); + + return ESP_OK; +} + +esp_err_t lp_core_touch_pad_sleep_channel_read_data(int touch_num, uint32_t *raw_data) +{ + /* Check Arguments */ + if (!raw_data) { + return ESP_ERR_INVALID_ARG; + } + LP_CORE_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); + + /* Read raw touch data */ + uint32_t engaged_sampler_cnt = touch_ll_sample_cfg_get_engaged_num(); + for (int i = 0; i < engaged_sampler_cnt; i++) { + // Only have smooth data on V3, no raw data + touch_ll_sleep_read_chan_data(TOUCH_LL_READ_SMOOTH, i + 1, &raw_data[i]); + } + + return ESP_OK; +} + +esp_err_t lp_core_touch_pad_sleep_channel_read_benchmark(int touch_num, uint32_t *benchmark) +{ + /* Check Arguments */ + if (!benchmark) { + return ESP_ERR_INVALID_ARG; + } + LP_CORE_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); + + /* Read benchmark data */ + uint32_t engaged_sampler_cnt = touch_ll_sample_cfg_get_engaged_num(); + for (int i = 0; i < engaged_sampler_cnt; i++) { + touch_ll_sleep_read_chan_data(TOUCH_LL_READ_BENCHMARK, i + 1, &benchmark[i]); + } + + return ESP_OK; +} + +esp_err_t lp_core_touch_pad_sleep_channel_read_smooth(int touch_num, uint32_t *smooth_data) +{ + /* Check Arguments */ + if (!smooth_data) { + return ESP_ERR_INVALID_ARG; + } + LP_CORE_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); + + /* Read smoothened touch sensor data */ + uint32_t engaged_sampler_cnt = touch_ll_sample_cfg_get_engaged_num(); + for (int i = 0; i < engaged_sampler_cnt; i++) { + touch_ll_sleep_read_chan_data(TOUCH_LL_READ_SMOOTH, i + 1, &smooth_data[i]); + } + + return ESP_OK; +} + +esp_err_t lp_core_touch_pad_sleep_channel_reset_benchmark(uint32_t mask) +{ + (void) mask; // Currently not support, reserved for future use + /* Reset benchmark */ + touch_ll_sleep_reset_benchmark(); + + return ESP_OK; +} diff --git a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_touch.c b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_touch.c index 8ced246058..aafc715380 100644 --- a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_touch.c +++ b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_touch.c @@ -1,20 +1,18 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "ulp_riscv_touch_ulp_core.h" #include "soc/soc_caps.h" -#include "soc/touch_sensor_pins.h" -#include "hal/touch_sensor_hal.h" +#include "hal/touch_sensor_ll.h" /* Check Touch Channel correctness */ #define ULP_RISCV_TOUCH_CHANNEL_CHECK_AND_RETURN(channel) \ { \ - if (channel >= SOC_TOUCH_SENSOR_NUM || \ - channel < 0 || \ - channel == SOC_TOUCH_DENOISE_CHANNEL) { \ + if (channel >= SOC_TOUCH_MAX_CHAN_ID || \ + channel < SOC_TOUCH_MIN_CHAN_ID) { \ return ESP_ERR_INVALID_ARG; \ } \ } \ @@ -28,7 +26,7 @@ esp_err_t ulp_riscv_touch_pad_read_raw_data(touch_pad_t touch_num, uint32_t *raw ULP_RISCV_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); /* Read raw touch data */ - *raw_data = touch_hal_read_raw_data(touch_num); + touch_ll_read_chan_data((int)touch_num, TOUCH_LL_READ_RAW, raw_data); return ESP_OK; } @@ -42,7 +40,7 @@ esp_err_t ulp_riscv_touch_pad_read_benchmark(touch_pad_t touch_num, uint32_t *be ULP_RISCV_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); /* Read benchmark data */ - touch_hal_read_benchmark(touch_num, benchmark); + touch_ll_read_chan_data((int)touch_num, TOUCH_LL_READ_BENCHMARK, benchmark); return ESP_OK; } @@ -56,7 +54,7 @@ esp_err_t ulp_riscv_touch_pad_filter_read_smooth(touch_pad_t touch_num, uint32_t ULP_RISCV_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); /* Read smoothened touch sensor data */ - touch_hal_filter_read_smooth(touch_num, smooth_data); + touch_ll_read_chan_data((int)touch_num, TOUCH_LL_READ_SMOOTH, smooth_data); return ESP_OK; } @@ -69,7 +67,7 @@ esp_err_t ulp_riscv_touch_pad_reset_benchmark(touch_pad_t touch_num) } /* Reset benchmark */ - touch_hal_reset_benchmark(touch_num); + touch_ll_reset_chan_benchmark(BIT(touch_num)); return ESP_OK; } @@ -83,7 +81,7 @@ esp_err_t ulp_riscv_touch_pad_sleep_channel_read_data(touch_pad_t touch_num, uin ULP_RISCV_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); /* Read raw touch data */ - touch_hal_sleep_read_data(raw_data); + touch_ll_sleep_read_chan_data(TOUCH_LL_READ_RAW, raw_data); return ESP_OK; } @@ -97,7 +95,7 @@ esp_err_t ulp_riscv_touch_pad_sleep_channel_read_benchmark(touch_pad_t touch_num ULP_RISCV_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); /* Read benchmark data */ - touch_hal_sleep_read_benchmark(benchmark); + touch_ll_sleep_read_chan_data(TOUCH_LL_READ_BENCHMARK, benchmark); return ESP_OK; } @@ -111,7 +109,7 @@ esp_err_t ulp_riscv_touch_pad_sleep_channel_read_smooth(touch_pad_t touch_num, u ULP_RISCV_TOUCH_CHANNEL_CHECK_AND_RETURN(touch_num); /* Read smoothened touch sensor data */ - touch_hal_sleep_read_smooth(smooth_data); + touch_ll_sleep_read_chan_data(TOUCH_LL_READ_SMOOTH, smooth_data); return ESP_OK; } @@ -119,7 +117,7 @@ esp_err_t ulp_riscv_touch_pad_sleep_channel_read_smooth(touch_pad_t touch_num, u esp_err_t ulp_riscv_touch_pad_sleep_channel_reset_benchmark(void) { /* Reset benchmark */ - touch_hal_sleep_reset_benchmark(); + touch_ll_sleep_reset_benchmark(); return ESP_OK; } diff --git a/examples/system/.build-test-rules.yml b/examples/system/.build-test-rules.yml index 3f20bd2150..a5abdf6387 100644 --- a/examples/system/.build-test-rules.yml +++ b/examples/system/.build-test-rules.yml @@ -334,6 +334,12 @@ examples/system/ulp/lp_core/lp_spi: depends_components: - ulp +examples/system/ulp/lp_core/lp_touch: + enable: + - if: SOC_TOUCH_SENSOR_SUPPORTED == 1 and (SOC_DEEP_SLEEP_SUPPORTED == 1 and SOC_LP_CORE_SUPPORTED == 1) + depends_components: + - ulp + examples/system/ulp/lp_core/lp_uart/lp_uart_char_seq_wakeup: disable: - if: (SOC_ULP_LP_UART_SUPPORTED != 1) diff --git a/examples/system/ulp/lp_core/lp_touch/CMakeLists.txt b/examples/system/ulp/lp_core/lp_touch/CMakeLists.txt new file mode 100644 index 0000000000..a104305f69 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_touch/CMakeLists.txt @@ -0,0 +1,8 @@ +# 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) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(lp_touch_example) diff --git a/examples/system/ulp/lp_core/lp_touch/README.md b/examples/system/ulp/lp_core/lp_touch/README.md new file mode 100644 index 0000000000..868e774980 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_touch/README.md @@ -0,0 +1,58 @@ +| Supported Targets | ESP32-P4 | +| ----------------- | -------- | + +# LP Core Touch Sensor example: + +This example demonstrates how to program the LP coprocessor to read touch pad sensors. + +LP Core program written in C can be found across `lp_core/main.c`. The build system compiles and links this program, converts it into binary format, and embeds it into the .rodata section of the ESP-IDF application. + +At runtime, the application running inside the main CPU loads ULP program into the `LP SRAM` memory region using `ulp_lp_core_load_binary` function. The main code then configures the ULP wakeup period and starts the coprocessor by using `ulp_lp_core_run`. Once the LP core program is started, it runs periodically, with the period set by the main program. The main program enables LP Core wakeup source and puts the chip into deep sleep mode. + +The LP core program scans all touch pad sensors periodically. When the program finds a touch pad is active (touched), it captures the touch pad number in a variable and wakes up the main CPU. The main CPU reports which touch pad sensor was touched. + +## How to use the example + +### Hardware Required + +* A development board with ESP32-P4 + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(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 + +``` +Not an LP core wakeup. Cause = 0 +Initializing... +Touch [CH 0] enabled on GPIO2 +Touch [CH 1] enabled on GPIO3 +Touch [CH 2] enabled on GPIO4 +Touch [CH 3] enabled on GPIO5 +================================= +Initial benchmark and new threshold are: +Touch [CH 0] 0: 5177, 77 1: 5131, 76 2: 2541, 38 +Touch [CH 1] 0: 5021, 75 1: 5043, 75 2: 2471, 37 +Touch [CH 2] 0: 5103, 76 1: 5064, 75 2: 2495, 37 +Touch [CH 3] 0: 4981, 74 1: 4999, 74 2: 2440, 36 +LP core loaded with firmware successfully +Entering in deep sleep + +// Touched channel 0, then wake-up by the LP core + +LP core woke up the main CPU +T0 touched + +Entering in deep sleep +``` diff --git a/examples/system/ulp/lp_core/lp_touch/main/CMakeLists.txt b/examples/system/ulp/lp_core/lp_touch/main/CMakeLists.txt new file mode 100644 index 0000000000..e078fa5ccc --- /dev/null +++ b/examples/system/ulp/lp_core/lp_touch/main/CMakeLists.txt @@ -0,0 +1,26 @@ +# Register the component +idf_component_register(SRCS "lp_touch_main.c" + INCLUDE_DIRS "" + REQUIRES ulp esp_driver_touch_sens) + +# +# ULP support additions to component CMakeLists.txt. +# +# 1. The LP Core app name must be unique (if multiple components use LP Core). +set(ulp_app_name lp_core_${COMPONENT_NAME}) + +# +# 2. Specify all C files. +# Files should be placed into a separate directory (in this case, lp_core/), +# which should not be added to COMPONENT_SRCS. +set(ulp_lp_core_sources "lp_core/main.c") + +# +# 3. List all the component source files which include automatically +# generated ULP export file, ${ulp_app_name}.h: +set(ulp_exp_dep_srcs "lp_touch_main.c") + +# +# 4. Call function to build ULP binary and embed in project using the argument +# values above. +ulp_embed_binary(${ulp_app_name} "${ulp_lp_core_sources}" "${ulp_exp_dep_srcs}") diff --git a/examples/system/ulp/lp_core/lp_touch/main/idf_component.yml b/examples/system/ulp/lp_core/lp_touch/main/idf_component.yml new file mode 100644 index 0000000000..b24e46a93d --- /dev/null +++ b/examples/system/ulp/lp_core/lp_touch/main/idf_component.yml @@ -0,0 +1,3 @@ +dependencies: + touch_sens_examples_common: + path: ${IDF_PATH}/examples/peripherals/touch_sensor/touch_sens_examples_common diff --git a/examples/system/ulp/lp_core/lp_touch/main/lp_core/main.c b/examples/system/ulp/lp_core/lp_touch/main/lp_core/main.c new file mode 100644 index 0000000000..98f8237c31 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_touch/main/lp_core/main.c @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/* + * LP touch sensor example + * This code runs on LP coprocessor + */ + +#include +#include "soc/soc_caps.h" +#include "ulp_lp_core_utils.h" +#include "ulp_lp_core_print.h" +#include "ulp_lp_core_touch.h" + +/* This variable will be exported as a public symbol, visible from main CPU: */ +uint32_t touch_data = 0; + +/* Touch pad threshold */ +#define TOUCH_PAD_THRESHOLD 100U +#define EXAMPLE_TOUCH_CHANNEL_NUM 4 + +static int s_channel_id[EXAMPLE_TOUCH_CHANNEL_NUM] = { + SOC_TOUCH_MIN_CHAN_ID, + SOC_TOUCH_MIN_CHAN_ID + 1, + SOC_TOUCH_MIN_CHAN_ID + 2, + SOC_TOUCH_MIN_CHAN_ID + 3, +}; + +int main (void) +{ + esp_err_t err = ESP_OK; + uint32_t benchmark = 0; + uint32_t smooth = 0; + uint32_t touch_value = 0; + + for (int i = 0; i < EXAMPLE_TOUCH_CHANNEL_NUM; i++) { + /* Read the benchmark and the filtered (smooth) touch data */ + if (lp_core_touch_pad_read_benchmark(s_channel_id[i], &benchmark) != ESP_OK) { + /* Skip channel this time */ + continue; + } + if (lp_core_touch_pad_filter_read_smooth(s_channel_id[i], &smooth) != ESP_OK) { + /* Skip channel this time */ + continue; + } + + /* The difference between the benchmark and the smooth touch data + * is used to determine an active touch pad. + * Check if the touch value breaches the wakeup threshold. + */ + if (smooth > benchmark) { + if ((smooth - benchmark) > TOUCH_PAD_THRESHOLD) { + touch_value |= (1 << s_channel_id[i]); + } + } + } + + /* Wake up the main CPU by LP core if a touch pad is active */ + if (touch_value) { + touch_data = touch_value; + ulp_lp_core_wakeup_main_processor(); + touch_value = 0; + } + + /* lp_core_halt() is called automatically when main exits */ + return 0; +} diff --git a/examples/system/ulp/lp_core/lp_touch/main/lp_touch_main.c b/examples/system/ulp/lp_core/lp_touch/main/lp_touch_main.c new file mode 100644 index 0000000000..4c23cf67b7 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_touch/main/lp_touch_main.c @@ -0,0 +1,189 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/* ULP RISC-V touch sensor example */ + +#include +#include "esp_sleep.h" +#include "ulp_lp_core.h" +#include "lp_core_main.h" +#include "driver/touch_sens.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "touch_sens_example_config.h" +#include "esp_sleep.h" + +extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_main_bin_start"); +extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_main_bin_end"); + + +// Touch version 3 supports multiple sample configurations (i.e. supports frequency hopping), +// others only have one set of sample configurations. +#define EXAMPLE_TOUCH_SAMPLE_CFG_NUM TOUCH_SAMPLE_CFG_NUM // Up to 'TOUCH_SAMPLE_CFG_NUM' +#define EXAMPLE_TOUCH_CHANNEL_NUM 4 +#define EXAMPLE_TOUCH_CHAN_INIT_SCAN_TIMES 3 + +ESP_STATIC_ASSERT(EXAMPLE_TOUCH_SAMPLE_CFG_NUM <= TOUCH_SAMPLE_CFG_NUM, "sample configuration number exceed the supported number"); +ESP_STATIC_ASSERT(EXAMPLE_TOUCH_CHANNEL_NUM <= (TOUCH_MAX_CHAN_ID - TOUCH_MIN_CHAN_ID + 1), "touch channel number exceed the max supported number "); + +// 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.015f, // 1.5% +}; +// The touch channel IDs that used in this example +// For the corresponding GPIOs of these channel, please refer to 'touch_sensor_channel.h' +static int s_channel_id[EXAMPLE_TOUCH_CHANNEL_NUM] = { + TOUCH_MIN_CHAN_ID, + TOUCH_MIN_CHAN_ID + 1, + TOUCH_MIN_CHAN_ID + 2, + TOUCH_MIN_CHAN_ID + 3, +}; + +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("Touch [CH %d]", s_channel_id[i]); + /* Generate the default channel configuration and then update the active threshold based on the real benchmark */ + touch_channel_config_t chan_cfg = EXAMPLE_TOUCH_CHAN_CFG_DEFAULT(); + for (int j = 0; j < EXAMPLE_TOUCH_SAMPLE_CFG_NUM; j++) { + chan_cfg.active_thresh[j] = (uint32_t)(benchmark[j] * s_thresh2bm_ratio[i]); + 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)); + } +} + +static void init_touch_pad(void) +{ + touch_sensor_handle_t sens_handle = NULL; + touch_channel_handle_t chan_handle[EXAMPLE_TOUCH_CHANNEL_NUM]; + + /* Step 1: Create a new touch sensor controller handle with default sample configuration */ + touch_sensor_sample_config_t sample_cfg[TOUCH_SAMPLE_CFG_NUM] = { + TOUCH_SENSOR_V3_DEFAULT_SAMPLE_CONFIG2(3, 29, 8, 3), + TOUCH_SENSOR_V3_DEFAULT_SAMPLE_CONFIG2(2, 88, 31, 7), + TOUCH_SENSOR_V3_DEFAULT_SAMPLE_CONFIG2(3, 10, 31, 7) + }; + 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)); + /* Step 2: Create and enable the new touch channel handles with default configurations */ + touch_channel_config_t chan_cfg = EXAMPLE_TOUCH_CHAN_CFG_DEFAULT(); + /* Allocate new touch channel on the touch controller */ + for (int i = 0; i < EXAMPLE_TOUCH_CHANNEL_NUM; i++) { + ESP_ERROR_CHECK(touch_sensor_new_channel(sens_handle, s_channel_id[i], &chan_cfg, &chan_handle[i])); + /* Display the touch channel corresponding GPIO number, you can also know from `touch_sensor_channel.h` */ + touch_chan_info_t chan_info = {}; + ESP_ERROR_CHECK(touch_sensor_get_channel_info(chan_handle[i], &chan_info)); + printf("Touch [CH %d] enabled on GPIO%d\n", s_channel_id[i], chan_info.chan_gpio); + } + printf("=================================\n"); + /* Step 3: Confiture the default filter for the touch sensor (Note: Touch V1 uses software filter) */ + touch_sensor_filter_config_t filter_cfg = TOUCH_SENSOR_DEFAULT_FILTER_CONFIG(); + ESP_ERROR_CHECK(touch_sensor_config_filter(sens_handle, &filter_cfg)); + + /* Step 4: Keep the RTC_PERIPH domain power on to ensure the touch sensor works during the deep sleep */ + /* Note: Here we don't need to enable the Touch Sensor wake-up by calling `touch_sensor_config_sleep_wakeup` + * because we want to decide whether to wake up in the LP program, instead of by the touch sensor hardware directly */ + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + /* Step 5: 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); + + /* Step 6: Enable the touch sensor */ + ESP_ERROR_CHECK(touch_sensor_enable(sens_handle)); + + /* Step 7: Start continuous scanning, you can also trigger oneshot scanning manually */ + ESP_ERROR_CHECK(touch_sensor_start_continuous_scanning(sens_handle)); +} + +static void lp_core_init(void) +{ + esp_err_t ret = ESP_OK; + + /* Set the time to wake up the LP core periodically */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER, + .lp_timer_sleep_duration_us = 300*1000, // 300 ms + }; + + ret = ulp_lp_core_load_binary(lp_core_main_bin_start, (lp_core_main_bin_end - lp_core_main_bin_start)); + if (ret != ESP_OK) { + printf("LP Core load failed\n"); + abort(); + } + + ret = ulp_lp_core_run(&cfg); + if (ret != ESP_OK) { + printf("LP Core run failed\n"); + abort(); + } + printf("LP core loaded with firmware successfully\n"); +} + +void app_main(void) +{ + /* If user is using USB-serial-jtag then idf monitor needs some time to + * re-connect to the USB port. We wait 1 sec here to allow for it to make the reconnection + * before we print anything. Otherwise the chip will go back to sleep again before the user + * has time to monitor any output. + */ + vTaskDelay(pdMS_TO_TICKS(1000)); + + esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); + /* Woke up by LP core, i.e., wake up the main CPU in the LP program */ + if (cause == ESP_SLEEP_WAKEUP_ULP) { + printf("LP core woke up the main CPU\n"); + // We can access the LP core global variable by adding `ulp_` prefix + uint32_t touch_data = ulp_touch_data; + for (int i = 0; i < EXAMPLE_TOUCH_CHANNEL_NUM; i++) { + if ((touch_data >> s_channel_id[i]) & 0x1) { + printf("T%d touched\n", s_channel_id[i]); + } + } + printf("\n"); + } + /* Woke up by other source, like power_on */ + else { + printf("Not an LP core wakeup. Cause = %d\n", cause); + printf("Initializing...\n"); + + /* Initialize Touch sensor from the main processor */ + init_touch_pad(); + /* Load LP Core binary and start the coprocessor */ + lp_core_init(); + } + + /* Go to sleep, only the ULP RISC-V will run */ + printf("Entering in deep sleep\n\n"); + + /* Small delay to ensure the messages are printed */ + vTaskDelay(100); + + ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup()); + esp_deep_sleep_start(); +} diff --git a/examples/system/ulp/lp_core/lp_touch/sdkconfig.defaults b/examples/system/ulp/lp_core/lp_touch/sdkconfig.defaults new file mode 100644 index 0000000000..d41662df2a --- /dev/null +++ b/examples/system/ulp/lp_core/lp_touch/sdkconfig.defaults @@ -0,0 +1,10 @@ +# Enable LP Core +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_TYPE_LP_CORE=y +CONFIG_ULP_COPROC_RESERVE_MEM=4096 + +# Set log level to Warning to produce clean output +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_BOOTLOADER_LOG_LEVEL=2 +CONFIG_LOG_DEFAULT_LEVEL_WARN=y +CONFIG_LOG_DEFAULT_LEVEL=2