test(touch): add touch sensor light sleep wake-up test

This commit is contained in:
Hu Rui
2026-09-01 20:08:37 +08:00
parent 207a508b9b
commit 06a099b6fb
13 changed files with 275 additions and 23 deletions

View File

@@ -5,4 +5,5 @@ components/esp_driver_touch_sens/test_apps/touch_sens:
- esp_driver_touch_sens
- esp_hal_touch_sens
- esp_hw_support
- ulp
- soc

View File

@@ -3,4 +3,20 @@ set(srcs "test_app_main.c" "test_touch_sens_common.cpp")
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "."
PRIV_REQUIRES unity esp_driver_touch_sens esp_psram
REQUIRES ulp
WHOLE_ARCHIVE)
if(CONFIG_IDF_TARGET_ESP32)
set(ulp_sources "ulp_fsm/main.S")
elseif(CONFIG_IDF_TARGET_ESP32S2 OR CONFIG_IDF_TARGET_ESP32S3)
set(ulp_sources "ulp_rv/main.c")
elseif(CONFIG_IDF_TARGET_ESP32P4 OR CONFIG_IDF_TARGET_ESP32S31)
set(ulp_sources "ulp_lpc/main.c")
endif()
if(ulp_sources)
set(ulp_app_name ulp_${COMPONENT_NAME})
set(ulp_exp_dep_srcs "test_touch_sens_common.cpp")
ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}")
endif()

View File

@@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#define TEST_TOUCH_WAKEUP_CHANNEL 3
#define TEST_TOUCH_SIMULATE_HOLD_TIME_US 10000
#ifndef __ASSEMBLER__
#include <stdint.h>
#include <stdbool.h>
#include "soc/soc_caps.h"
#include "hal/touch_sensor_ll.h"
static inline void test_touch_simulate_touch(int chan_id, bool active)
{
#if SOC_IS(ESP32S31)
/* ESP32-S31 has no internal capacitor, emulate touch by changing charging cycles. */
for (int i = 0; i < TOUCH_SAMPLE_CFG_NUM; i++) {
uint16_t ct;
touch_ll_get_charge_times(i, &ct);
/**
* assume original ct = 8q + r, 0 <= r < 8
* activated ct = 8q + r + q = 9q + r
* deactivated ct = (9q + r) - q = 8q + r = original ct
*/
ct = active ? ct + (ct >> 3) : ct - (ct / 9);
touch_ll_set_charge_times(i, ct);
}
#elif SOC_TOUCH_SENSOR_VERSION <= 2
touch_ll_set_charge_speed(chan_id, active ? TOUCH_CHARGE_SPEED_4 : TOUCH_CHARGE_SPEED_7);
#elif SOC_TOUCH_SENSOR_VERSION == 3
touch_ll_set_internal_capacitor(active ? 0x7f : 0);
#endif
}
#endif // __ASSEMBLER__

View File

@@ -13,6 +13,17 @@
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_heap_caps.h"
#include "test_helper.h"
#include "esp_sleep.h"
#if SOC_LP_CORE_SUPPORTED
#include "ulp_lp_core.h"
#elif SOC_RISCV_COPROC_SUPPORTED
#include "ulp_riscv.h"
#elif SOC_ULP_FSM_SUPPORTED
#include "ulp.h"
#include "ulp_main.h"
#endif
/**************************** Configurations ****************************/
@@ -98,26 +109,6 @@ static bool TEST_TCH_IRAM_ATTR s_test_touch_on_inactive_callback(touch_sensor_ha
/**************************** Helper Functions ****************************/
static void s_test_touch_simulate_touch(touch_channel_handle_t touch_chan, bool active)
{
#if CONFIG_IDF_TARGET_ESP32S31
/* ESP32-S31 has no internal capacitor, emulate touch by changing charging cycles. */
for (int i = 0; i < TOUCH_SAMPLE_CFG_NUM; i++) {
uint32_t charge_times = s_sample_cfg[i].charge_times;
if (active) {
charge_times += charge_times >> 1; // 1.5x
}
touch_ll_set_charge_times(i, charge_times);
}
#elif SOC_TOUCH_SENSOR_VERSION <= 2
touch_chan_info_t chan_info = {};
touch_sensor_get_channel_info(touch_chan, &chan_info);
touch_ll_set_charge_speed(chan_info.chan_id, active ? TOUCH_CHARGE_SPEED_4 : TOUCH_CHARGE_SPEED_7);
#elif SOC_TOUCH_SENSOR_VERSION == 3
touch_ll_set_internal_capacitor(active ? 0x7f : 0);
#endif
}
static void s_test_touch_do_initial_scanning(touch_sensor_handle_t touch, int scan_times)
{
/* Enable the touch sensor to do the initial scanning, so that to initialize the channel data */
@@ -261,13 +252,13 @@ TEST_CASE("touch_sens_active_inactive_test", "[touch]")
// Read data before touched
s_test_touch_log_data(touch_chan, "Data Before");
// Simulate touch
s_test_touch_simulate_touch(touch_chan, true);
test_touch_simulate_touch(chan_ids[0], true);
vTaskDelay(pdMS_TO_TICKS(100));
// Read data after touched
s_test_touch_log_data(touch_chan, "Data After ");
// Simulate release
s_test_touch_simulate_touch(touch_chan, false);
test_touch_simulate_touch(chan_ids[0], false);
vTaskDelay(pdMS_TO_TICKS(100));
}
printf("\n");
@@ -322,3 +313,104 @@ TEST_CASE("touch_sens_current_meas_channel_test", "[touch]")
TEST_ESP_OK(touch_sensor_del_controller(touch));
}
#endif // SOC_TOUCH_SENSOR_VERSION > 1
/**
* @note ESP32-P4 + sleep + PSRAM + O0 optimization can cause SPM overflow.
* Therefore, disable the sleep test when iram_safe is enabled.
*/
#if SOC_ULP_SUPPORTED && !(CONFIG_TOUCH_ISR_IRAM_SAFE && SOC_IS(ESP32P4))
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end");
#define TEST_ULP_WAKEUP_PERIOD_US (1 * 1000 * 1000)
/**
* @note The ULP start APIs (ulp_xxx_run) start a timer to trigger ULP execution periodically,
* but do not guarantee whether the ULP will run once immediately. Therefore, each ULP
* program includes logic to skip the first run, ensuring that the touch event is simulated
* only after entering sleep.
*/
static void s_test_touch_start_ulp(void)
{
#if SOC_LP_CORE_SUPPORTED
TEST_ESP_OK(ulp_lp_core_load_binary(ulp_main_bin_start, ulp_main_bin_end - ulp_main_bin_start));
ulp_lp_core_cfg_t cfg = {};
cfg.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER;
cfg.lp_timer_sleep_duration_us = TEST_ULP_WAKEUP_PERIOD_US;
TEST_ESP_OK(ulp_lp_core_run(&cfg));
#elif SOC_RISCV_COPROC_SUPPORTED
TEST_ESP_OK(ulp_riscv_load_binary(ulp_main_bin_start, ulp_main_bin_end - ulp_main_bin_start));
TEST_ESP_OK(ulp_set_wakeup_period(0, TEST_ULP_WAKEUP_PERIOD_US));
TEST_ESP_OK(ulp_riscv_run());
#elif SOC_ULP_FSM_SUPPORTED
TEST_ESP_OK(ulp_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)));
TEST_ESP_OK(ulp_set_wakeup_period(0, TEST_ULP_WAKEUP_PERIOD_US));
TEST_ESP_OK(ulp_run(&ulp_entry - RTC_SLOW_MEM));
#else
#error "No supported ULP"
#endif
}
TEST_CASE("touch_sens_light_sleep_wakeup_test", "[touch]")
{
test_touch_fixture_t *fixture = (test_touch_fixture_t *)alloca(
sizeof(test_touch_fixture_t) + sizeof(touch_channel_handle_t) * 2);
const uint32_t chan_ids[] = {TEST_TOUCH_WAKEUP_CHANNEL, TEST_TOUCH_WAKEUP_CHANNEL + 1};
test_touch_cb_data_t cb_data = {};
s_test_touch_fixture_init(fixture, 2, chan_ids, &cb_data);
touch_sensor_handle_t touch = fixture->sensor;
#if SOC_TOUCH_SENSOR_VERSION == 3
/**
* @note For TOUCH_SENSOR_VERSION == 3, since we cannot simulate an isolated trigger on a single channel,
* the thresholds for all other channels are raised to the maximum to prevent them from being triggered.
*/
touch_channel_config_t inactive_cfg;
for (int i = 0; i < TOUCH_SAMPLE_CFG_NUM; i++) {
inactive_cfg.active_thresh[i] = TOUCH_LL_ACTIVE_THRESH_MAX;
}
TEST_ESP_OK(touch_sensor_reconfig_channel(fixture->channels[1], &inactive_cfg));
#endif
touch_sleep_config_t sleep_cfg = {};
sleep_cfg.slp_wakeup_lvl = TOUCH_LIGHT_SLEEP_WAKEUP;
TEST_ESP_OK(touch_sensor_config_sleep_wakeup(touch, &sleep_cfg));
TEST_ESP_OK(touch_sensor_enable(touch));
TEST_ESP_OK(touch_sensor_start_continuous_scanning(touch));
/* Set up the ULP to simulate touch wake-up */
s_test_touch_start_ulp();
/* Enter light sleep */
TEST_ESP_OK(esp_light_sleep_start());
/* Wakeup from sleep */
uint32_t wakeup_causes = esp_sleep_get_wakeup_causes();
if (wakeup_causes & BIT(ESP_SLEEP_WAKEUP_TOUCHPAD)) {
printf("wakeup by touchpad\n");
} else {
printf("wakeup by other causes: %" PRIu32 "\n", wakeup_causes);
TEST_FAIL();
}
#if SOC_TOUCH_SENSOR_VERSION == 1
printf("hardware active interrupt count: %d\n", cb_data.hw_active_count);
TEST_ASSERT_GREATER_OR_EQUAL(1, cb_data.hw_active_count);
#else
TEST_ASSERT_EQUAL(1, cb_data.active_count);
#endif
for (int cnt = 0; cb_data.inactive_count != cb_data.active_count; cnt++) {
if (cnt > 100) {
TEST_FAIL_MESSAGE("timeout waiting for inactive callback");
}
vTaskDelay(pdMS_TO_TICKS(10));
}
TEST_ESP_OK(touch_sensor_stop_continuous_scanning(touch));
TEST_ESP_OK(touch_sensor_disable(touch));
TEST_ESP_OK(touch_sensor_config_sleep_wakeup(touch, NULL));
TEST_ESP_OK(touch_sensor_del_channel(fixture->channels[0]));
TEST_ESP_OK(touch_sensor_del_channel(fixture->channels[1]));
TEST_ESP_OK(touch_sensor_del_controller(touch));
}
#endif // SOC_ULP_SUPPORTED && !(CONFIG_TOUCH_ISR_IRAM_SAFE && SOC_IS(ESP32P4))

View File

@@ -0,0 +1,50 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/rtc_io_reg.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/soc_ulp.h"
#include "test_helper.h"
#define WRITE_TOUCH_PAD_FIELD_2(chan, value) WRITE_RTC_FIELD(RTC_IO_TOUCH_PAD##chan##_REG, RTC_IO_TOUCH_PAD##chan##_DAC, value)
#define WRITE_TOUCH_PAD_FIELD(chan, value) WRITE_TOUCH_PAD_FIELD_2(chan, value)
#define ULP_FSM_CLK_CYCLES_PER_US 8
#define TOUCH_SIMULATE_HOLD_CYCLES (TEST_TOUCH_SIMULATE_HOLD_TIME_US * ULP_FSM_CLK_CYCLES_PER_US)
.bss
run_count:
.long 0
.text
.global entry
entry:
move r1, run_count
ld r0, r1, 0
add r0, r0, 0
jump first_run, eq
/* Simulate touch event */
WRITE_TOUCH_PAD_FIELD(TEST_TOUCH_WAKEUP_CHANNEL, 4)
.rept TOUCH_SIMULATE_HOLD_CYCLES / 0xffff
wait 0xffff
.endr
.if TOUCH_SIMULATE_HOLD_CYCLES % 0xffff
wait TOUCH_SIMULATE_HOLD_CYCLES % 0xffff
.endif
WRITE_TOUCH_PAD_FIELD(TEST_TOUCH_WAKEUP_CHANNEL, 7)
/* Disable the ULP timer */
WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)
halt
first_run:
add r0, r0, 1
st r0, r1, 0
halt

View File

@@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ulp_lp_core_utils.h"
#include "ulp_lp_core_lp_timer_shared.h"
#include "test_helper.h"
int main(void)
{
static uint8_t run_count = 0;
if (run_count == 0) {
run_count++;
} else {
test_touch_simulate_touch(TEST_TOUCH_WAKEUP_CHANNEL, true);
ulp_lp_core_delay_us(TEST_TOUCH_SIMULATE_HOLD_TIME_US);
test_touch_simulate_touch(TEST_TOUCH_WAKEUP_CHANNEL, false);
ulp_lp_core_lp_timer_disable();
}
return 0;
}

View File

@@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ulp_riscv_utils.h"
#include "test_helper.h"
int main(void)
{
static uint8_t run_count = 0;
if (run_count == 0) {
run_count++;
} else {
test_touch_simulate_touch(TEST_TOUCH_WAKEUP_CHANNEL, true);
ulp_riscv_delay_us(TEST_TOUCH_SIMULATE_HOLD_TIME_US);
test_touch_simulate_touch(TEST_TOUCH_WAKEUP_CHANNEL, false);
ulp_riscv_timer_stop();
}
return 0;
}

View File

@@ -17,4 +17,4 @@ from pytest_embedded_idf.utils import soc_filtered_targets
)
@idf_parametrize('target', soc_filtered_targets('SOC_TOUCH_SENSOR_SUPPORTED == 1'), indirect=['target'])
def test_touch_sens(dut: Dut) -> None:
dut.run_all_single_board_cases()
dut.run_all_single_board_cases(timeout=20)

View File

@@ -0,0 +1,2 @@
CONFIG_ULP_COPROC_ENABLED=y
CONFIG_ULP_COPROC_TYPE_FSM=y

View File

@@ -1,3 +1,5 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_HEX=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0
CONFIG_ULP_COPROC_ENABLED=y
CONFIG_ULP_COPROC_TYPE_LP_CORE=y

View File

@@ -0,0 +1,2 @@
CONFIG_ULP_COPROC_ENABLED=y
CONFIG_ULP_COPROC_TYPE_RISCV=y

View File

@@ -1,2 +1,4 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0
CONFIG_ULP_COPROC_ENABLED=y
CONFIG_ULP_COPROC_TYPE_RISCV=y

View File

@@ -0,0 +1,2 @@
CONFIG_ULP_COPROC_ENABLED=y
CONFIG_ULP_COPROC_TYPE_LP_CORE=y