news 2026/9/13 18:52:47

Arduino ESP32 电容触摸传感器 API 完全指南:touchRead、触摸中断与深度睡眠唤醒

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Arduino ESP32 电容触摸传感器 API 完全指南:touchRead、触摸中断与深度睡眠唤醒

Arduino ESP32 电容触摸传感器 API 完全指南:touchRead、触摸中断与深度睡眠唤醒

【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32

本指南以 Arduino-ESP32 核心的 TOUCH 官方文档(docs/en/api/touch.rst)为骨架,系统讲解 ESP32 系列 SoC 电容触摸传感器的工作原理、touchRead()touchSetCycles()、触摸中断(touchAttachInterrupt系列)与深度睡眠触摸唤醒等全部 API,并结合 esp32-hal-touch.c 源码与 Touch 示例 给出可直接复制的实战代码。阅读完本文,你将掌握如何用触摸引脚替代机械按键、如何校准阈值、如何在触摸时触发中断或唤醒芯片,并理解 ESP32(TOUCH_V1)与 ESP32-S2/S3(TOUCH_V2)两代触摸外设在 API 行为上的差异。

触摸传感器的工作原理

触摸传感器是一种外设(peripheral),它内置振荡器电路,在固定时间窗口内对指定 GPIO 引脚上的充放电循环次数进行测量。正因如此,这类传感器也被称为电容式传感器(capacitive sensors)。

其物理机理是:当手指触碰这些引脚时,手指的电荷会改变与触摸传感器相连的 RC 电路的等效电容,从而使单位时间内的充放电循环次数发生变化。touchRead()返回的就是某个测量周期(meas)内的循环次数。这个计数值的变化量被用来判断"是否发生了触摸"。基于此,这些引脚可以很方便地接入电容触摸焊盘(capacitive pads),用来替代机械按键。

注意:触摸外设并非存在于每一款 SoC 中。例如 ESP32-P4(TOUCH_V3)等芯片具有不同的触摸实现,具体请参考各芯片的数据手册(datasheet)。本仓库的触摸 HAL 还根据 IDF 版本做了新旧两套 API 的区分,详见下文"源码级实现"一节。

通用 TOUCH API

以下 API 在所有支持触摸外设的芯片上(ESP32 / ESP32-S2 / ESP32-S3)均可用,声明位于 esp32-hal-touch.h,实现在 esp32-hal-touch.c。

touchRead():读取触摸传感器数据

每个触摸传感器都有一个计数器,用来统计充放电循环次数。当焊盘被"触摸"时,由于等效电容变大,计数器的值会随之改变,据此即可判断焊盘是否被触摸。

touch_value_t touchRead(uint8_t pin);
  • pin:要读取触摸值的 GPIO 引脚。

函数返回值类型因芯片而异:在 ESP32 上返回uint16_t,在 ESP32-S2/S3 上返回uint32_t。这一点在源码中由SOC_TOUCH_SENSOR_VERSION宏决定:

#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 typedef uint16_t touch_value_t; #elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2 ESP32S3 typedef uint32_t touch_value_t; #endif

读数方向性:ESP32 上触摸时读数接近 0(值下降),而 ESP32-S2/S3 上触摸时读数升高。这一点决定了中断阈值的判断方向,也是 V1/V2 两代 API 行为差异的根源。

以经典 ESP32 DevKit 为例,触摸引脚与 GPIO 的对应关系定义在 variants/esp32/pins_arduino.h:

static const uint8_t T0 = 4; static const uint8_t T1 = 0; static const uint8_t T2 = 2; static const uint8_t T3 = 15; static const uint8_t T4 = 13; static const uint8_t T5 = 12; static const uint8_t T6 = 14; static const uint8_t T7 = 27; static const uint8_t T8 = 33; static const uint8_t T9 = 32;

即 ESP32 的 10 个触摸通道(TOUCH0~TOUCH9)依次映射到 GPIO 4、0、2、15、13、12、14、27、33、32。不同开发板的映射可能不同,请以对应 variant 目录下的pins_arduino.h为准。

touchSetCycles():设置测量与休眠周期

该函数用于设置一次测量操作所花费的周期数。touchRead的结果、阈值判定以及检测精度都依赖于这两个值。默认配置下touchRead大约耗时 0.5 ms。

void touchSetCycles(uint16_t measure, uint16_t sleep);
  • measure:设置测量触摸传感器数值所花费的时间。
  • sleep:设置下一次测量周期开始前的等待时间。

源码中 ESP32 的默认值为0x1000(measure)与0x1000(sleep),ESP32-S2/S3 则使用 IDF 的TOUCH_PAD_MEASURE_CYCLE_DEFAULTTOUCH_PAD_SLEEP_CYCLE_DEFAULT

#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 static uint16_t __touchSleepCycles = 0x1000; static uint16_t __touchMeasureCycles = 0x1000; #elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3 static uint16_t __touchSleepCycles = TOUCH_PAD_SLEEP_CYCLE_DEFAULT; static uint16_t __touchMeasureCycles = TOUCH_PAD_MEASURE_CYCLE_DEFAULT; #endif

底层实现分别调用 IDF 的touch_pad_set_measurement_clock_cycles()(ESP32,设置测量时钟周期)或touch_pad_set_charge_discharge_times()(S2/S3,设置充放电次数),再统一调用touch_pad_set_measurement_interval()设置测量间隔(见 esp32-hal-touch.c)。

触摸中断:touchAttachInterrupt / touchAttachInterruptArg

这两个函数用于为触摸焊盘挂载中断回调。回调函数会在以下条件下被调用:ESP32 上触摸值低于给定阈值时,或ESP32-S2/S3 上触摸值高于给定阈值时。要确定"触摸"与"未触摸"状态之间的合适阈值,请先使用touchRead()实测读数。

void touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), touch_value_t threshold); void touchAttachInterruptArg(uint8_t pin, void (*userFunc)(void*), void *arg, touch_value_t threshold);

参数说明:

  • pin:GPIO 触摸焊盘引脚。
  • userFunc:中断触发时要调用的函数。touchAttachInterruptArg版本中,ISR 回调内可以访问传入的参数。
  • arg(仅touchAttachInterruptArg):传递给中断回调的参数。
  • threshold:触发中断的阈值。

在源码实现中,两个函数最终都汇聚到__touchConfigInterrupt():若userFuncNULL则视为解绑(将阈值设为TOUCH_PAD_THRESHOLD_MAX以停用 ISR),否则先完成触摸模块与通道初始化,再记录回调指针、callWithArgs标志和参数,最后调用touch_pad_set_thresh()写入阈值(见 esp32-hal-touch.c)。内部 ISR__touchISR在触发后会逐位检查触摸通道状态并分发到对应的用户回调(见 esp32-hal-touch.c)。

touchDetachInterrupt():解绑触摸中断

用于将触摸中断从触摸焊盘上解绑:

void touchDetachInterrupt(uint8_t pin);
  • pin:GPIO 触摸焊盘引脚。

实现上等价于调用touchAttachInterrupt(pin, NULL, 0),通过把回调置空、阈值置为TOUCH_PAD_THRESHOLD_MAX来停用该通道的 ISR(见 esp32-hal-touch.c)。

touchSleepWakeUpEnable():深度睡眠触摸唤醒

该函数用于将触摸焊盘配置为深度睡眠唤醒源

void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold);
  • pin:GPIO 触摸焊盘引脚。
  • threshold:触发唤醒的阈值。

注意:ESP32-S2 与 ESP32-S3只支持一个用于睡眠唤醒的触摸焊盘。

源码中,该函数会通过 periman 完成引脚总线注册与触摸模块初始化,然后按芯片分支处理(见 esp32-hal-touch.c):

  • ESP32:调用touch_pad_set_thresh()设置阈值;
  • ESP32-S2/S3:调用touch_pad_sleep_channel_enable(pad, true)使能睡眠通道,再调用touch_pad_sleep_set_threshold()设置唤醒阈值;

最后统一调用esp_sleep_enable_touchpad_wakeup()使能触摸唤醒。在新版 API(IDF ≥ 5.5)中,该函数同时支持深度睡眠与浅睡眠(light sleep),且浅睡眠下所有已使用的触摸焊盘都能唤醒芯片。

芯片专属 API

ESP32 专属(TOUCH_V1):touchInterruptSetThresholdDirection()

该函数用于告诉驱动:当传感器值低于还是高于阈值时激活中断。默认是"低于"(lower)。

void touchInterruptSetThresholdDirection(bool mustbeLower);
  • mustbeLower:为true时在传感器值低于阈值时触发,为false时在高于阈值时触发。

底层分别映射到 IDF 的touch_pad_set_trigger_mode(TOUCH_TRIGGER_BELOW)TOUCH_TRIGGER_ABOVE(见 esp32-hal-touch.c)。由于 ESP32 触摸读数在触摸时下降,默认的"低于阈值触发"正好匹配触摸事件。

ESP32-S2 / ESP32-S3 专属(TOUCH_V2):touchInterruptGetLastStatus()

该函数用于获取触摸焊盘最新的 ISR 状态

bool touchInterruptGetLastStatus(uint8_t pin);

如果触摸焊盘已被按下且持续处于按下状态则返回true,否则返回false

该函数可以配合 ISR 用户回调使用,以便在触摸焊盘被按下和释放的瞬间分别采取动作——例如把触摸模拟成"按下 / 松开"的按钮。源码在 V2 的 ISR 中根据TOUCH_PAD_INTR_MASK_ACTIVE(按下)与TOUCH_PAD_INTR_MASK_INACTIVE(释放)两种中断掩码实时维护lastStatusIsPressed标志(见 esp32-hal-touch.c),touchInterruptGetLastStatus()只是读取该标志并返回。

阈值校准与基准值:touchSetDefaultThreshold()

在示例中可以看到另一个与阈值相关的 API——touchSetDefaultThreshold(float percentage)。当向touchAttachInterrupt传入的threshold0时,驱动会使用基准值(benchmark)的百分比作为默认阈值,默认百分比为 1.5%,可通过该函数修改:

// 将默认阈值设为基准值的 5%;仅在 threshold = 0 时生效 touchSetDefaultThreshold(5);

其头文件声明位于 esp32-hal-touch-ng.h(新版 API 中),适合在不知道绝对读数、希望以相对变化量判断触摸时使用。

源码级实现细节

理解 HAL 层实现有助于在真实项目中规避坑点:

  • periman 引脚管理:每次touchRead()、中断挂载或唤醒配置时,HAL 都会先通过perimanGetPinBus()检查引脚是否已被占用;若未注册,则调用perimanSetPinBus()ESP32_BUS_TYPE_TOUCH类型登记该引脚,并在全部触摸通道释放后自动touch_pad_deinit()(见 esp32-hal-touch.c)。这意味着触摸引脚与 GPIO 其他功能(如 ADC、PWM)的互斥是自动管理的。
  • 模块初始化__touchInit()完成touch_pad_init()、电压设置(ESP32 为TOUCH_HVOLT_2V7 / TOUCH_LVOLT_0V5 / TOUCH_HVOLT_ATTEN_0V,S2/S3 使用TOUCH_PAD_HIGH_VOLTAGE_THRESHOLD等宏)、FSM 定时模式启动,以及 ESP32 上的touch_pad_filter_start(10)滤波启动(见 esp32-hal-touch.c)。
  • 弱符号别名:公开 API 通过__attribute__((weak, alias(...)))导出,允许用户代码覆盖默认实现(见 esp32-hal-touch.c)。
  • 新老两套 API:当 IDF 版本 ≥ 5.5 或触摸外设版本为 3(如 ESP32-P4)时,编译走 esp32-hal-touch-ng.c 与 esp32-hal-touch-ng.h 这条路径(touchSetCycles变为touchSetTiming(float measure, uint32_t sleep),并新增touchSetConfig()调节充放电电压/次数),否则使用本指南所述的经典 API。

完整示例应用

仓库 libraries/ESP32/examples/Touch 下提供了三个可直接编译运行的示例,下面完整列出。

示例一:TouchRead —— 读取触摸值

最简单的触摸测试程序,每秒打印一次 T2(GPIO 2)的读数。触摸该引脚时观察数值变化,即可判断触摸行为:

// ESP32 Touch Test // Just test touch pin - Touch2 is T2 which is on GPIO 2. #include <Arduino.h> void setup() { Serial.begin(115200); delay(1000); // give me time to bring up serial monitor Serial.println("ESP32 Touch Test"); } void loop() { Serial.println(touchRead(T2)); // get value using T2 delay(1000); }

运行要点:先在不触摸的状态下记录读数,再用手触摸 T2 引脚对比读数变化;在 ESP32 上触摸时读数会明显下降(接近 0),据此可为中断示例挑选合适的阈值。

示例二:TouchInterrupt —— 触摸中断

演示如何为两个触摸焊盘(T2、T3)挂载中断回调,并利用touchSetDefaultThreshold(5)把默认阈值设为基准值的 5%:

/* This is an example how to use Touch Intrrerupts The bigger the threshold, the more sensible is the touch */ #include <Arduino.h> int threshold = 0; // if 0 is used, benchmark value is used. Its by default 1,5% change, can be changed by touchSetDefaultThreshold(float percentage) bool touch1detected = false; bool touch2detected = false; void gotTouch1() { touch1detected = true; } void gotTouch2() { touch2detected = true; } void setup() { Serial.begin(115200); delay(1000); // give me time to bring up serial monitor //Optional: Set the threshold to 5% of the benchmark value. Only effective if threshold = 0. touchSetDefaultThreshold(5); Serial.println("ESP32 Touch Interrupt Test"); touchAttachInterrupt(T2, gotTouch1, threshold); touchAttachInterrupt(T3, gotTouch2, threshold); } void loop() { if (touch1detected) { touch1detected = false; Serial.println("Touch 1 detected"); } if (touch2detected) { touch2detected = false; Serial.println("Touch 2 detected"); } }

注意示例头部注释的要点:阈值越大,触摸越灵敏(指 ESP32-S2/S3 方向;ESP32 上读数方向相反)。ISR 回调中只置标志位,实际处理放在loop()中,避免在中断上下文做耗时操作。该示例的 CI 配置见 TouchInterrupt/ci.yml。

示例三:TouchButton —— 模拟按键按下/释放

该示例基于 V2 专属 APItouchInterruptGetLastStatus(),把触摸行为模拟成"按下 / 松开"的按键,仅在 ESP32-S2 / ESP32-S3 上适用

/* This is an example how to use Touch Intrrerupts The sketch will tell when it is touched and then released as like a push-button This method based on touchInterruptGetLastStatus() */ #include "Arduino.h" int threshold = 0; // if 0 is used, benchmark value is used. Its by default 1,5% change, can be changed by touchSetDefaultThreshold(float percentage) bool touch1detected = false; bool touch2detected = false; void gotTouch1() { touch1detected = true; } void gotTouch2() { touch2detected = true; } void setup() { Serial.begin(115200); delay(1000); // give me time to bring up serial monitor //Optional: Set the threshold to 5% of the benchmark value. Only effective if threshold = 0. touchSetDefaultThreshold(5); //Set the touch pads Serial.println("\n ESP32 Touch Interrupt Test\n"); touchAttachInterrupt(T1, gotTouch1, threshold); touchAttachInterrupt(T2, gotTouch2, threshold); } void loop() { if (touch1detected) { touch1detected = false; if (touchInterruptGetLastStatus(T1)) { Serial.println(" --- T1 Touched"); } else { Serial.println(" --- T1 Released"); } } if (touch2detected) { touch2detected = false; if (touchInterruptGetLastStatus(T2)) { Serial.println(" --- T2 Touched"); } else { Serial.println(" --- T2 Released"); } } delay(80); }

使用注意事项与最佳实践

  1. 先校准再设阈值touchRead()是挑选阈值的第一手依据。分别记录"未触摸"与"触摸"状态下的读数,取中间值作为touchAttachInterruptthreshold;或直接传入0使用touchSetDefaultThreshold()的基准值百分比方案。
  2. 注意读数的方向性:ESP32 触摸读数下降、ESP32-S2/S3 读数上升,判断"阈值是否触发"时方向相反;跨芯片移植代码时务必重新校准。
  3. 睡眠唤醒限制:ESP32-S2/S3 仅支持一个触摸唤醒焊盘;新版 API 中浅睡眠(light sleep)下所有已启用焊盘均可唤醒。
  4. 引脚资源互斥:触摸引脚由 periman 统一管理,与 ADC、GPIO 等总线互斥;若引脚已被其他外设占用,触摸初始化会失败或返回 0,可通过log_e输出排查。
  5. ISR 保持轻量:中断回调内只置标志位或做极简操作,复杂逻辑放到loop();需要向回调传参时使用touchAttachInterruptArg
  6. 版本差异:IDF ≥ 5.5 或 TOUCH_V3(如 ESP32-P4)芯片走新的 esp32-hal-touch-ng API,touchSetCycles已更名为touchSetTiming,请以实际编译使用的核心版本为准。

【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 18:51:07

CW32L012串口上位机攻略:免拆板更新外部SPI Flash

先说这个上位机是干什么的。CW32L012这颗芯片主打超低功耗&#xff0c;但内部Flash容量摆在那里&#xff0c;产品里要放字库、提示音、配置文件这类大数据时根本不够用&#xff0c;所以很多方案都会外挂一颗SPI接口的串行Flash&#xff08;比如W25Q系列&#xff09;。以前调试这…

作者头像 李华
网站建设 2026/9/13 18:49:55

gcmfaces工具箱:Matlab/Octave处理立方球网格海洋模式数据指南

简介&#xff1a;gcmfaces 是一款面向 Matlab 与 Octave 的开源工具箱&#xff0c;专为全球气候模型&#xff08;GCM&#xff09;海洋环流数据处理而设计。它帮助科研人员高效读取、管理、可视化和计算大规模分块网格数据&#xff0c;支持物理量诊断与并行加速&#xff0c;尤其…

作者头像 李华