多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

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

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 示例 给出可直接复制的实战代码。阅读完本文你将掌握如何用触摸引脚替代机械按键、如何校准阈值、如何在触摸时触发中断或唤醒芯片并理解 ESP32TOUCH_V1与 ESP32-S2/S3TOUCH_V2两代触摸外设在 API 行为上的差异。触摸传感器的工作原理触摸传感器是一种外设peripheral它内置振荡器电路在固定时间窗口内对指定 GPIO 引脚上的充放电循环次数进行测量。正因如此这类传感器也被称为电容式传感器capacitive sensors。其物理机理是当手指触碰这些引脚时手指的电荷会改变与触摸传感器相连的 RC 电路的等效电容从而使单位时间内的充放电循环次数发生变化。touchRead()返回的就是某个测量周期meas内的循环次数。这个计数值的变化量被用来判断是否发生了触摸。基于此这些引脚可以很方便地接入电容触摸焊盘capacitive pads用来替代机械按键。注意触摸外设并非存在于每一款 SoC 中。例如 ESP32-P4TOUCH_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.hstatic 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 的默认值为0x1000measure与0x1000sleepESP32-S2/S3 则使用 IDF 的TOUCH_PAD_MEASURE_CYCLE_DEFAULT与TOUCH_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);参数说明pinGPIO 触摸焊盘引脚。userFunc中断触发时要调用的函数。touchAttachInterruptArg版本中ISR 回调内可以访问传入的参数。arg仅touchAttachInterruptArg传递给中断回调的参数。threshold触发中断的阈值。在源码实现中两个函数最终都汇聚到__touchConfigInterrupt()若userFunc为NULL则视为解绑将阈值设为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);pinGPIO 触摸焊盘引脚。实现上等价于调用touchAttachInterrupt(pin, NULL, 0)通过把回调置空、阈值置为TOUCH_PAD_THRESHOLD_MAX来停用该通道的 ISR见 esp32-hal-touch.c。touchSleepWakeUpEnable()深度睡眠触摸唤醒该函数用于将触摸焊盘配置为深度睡眠唤醒源void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold);pinGPIO 触摸焊盘引脚。threshold触发唤醒的阈值。注意ESP32-S2 与 ESP32-S3只支持一个用于睡眠唤醒的触摸焊盘。源码中该函数会通过 periman 完成引脚总线注册与触摸模块初始化然后按芯片分支处理见 esp32-hal-touch.cESP32调用touch_pad_set_thresh()设置阈值ESP32-S2/S3调用touch_pad_sleep_channel_enable(pad, true)使能睡眠通道再调用touch_pad_sleep_set_threshold()设置唤醒阈值最后统一调用esp_sleep_enable_touchpad_wakeup()使能触摸唤醒。在新版 APIIDF ≥ 5.5中该函数同时支持深度睡眠与浅睡眠light sleep且浅睡眠下所有已使用的触摸焊盘都能唤醒芯片。芯片专属 APIESP32 专属TOUCH_V1touchInterruptSetThresholdDirection()该函数用于告诉驱动当传感器值低于还是高于阈值时激活中断。默认是低于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_V2touchInterruptGetLastStatus()该函数用于获取触摸焊盘最新的 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.ctouchInterruptGetLastStatus()只是读取该标志并返回。阈值校准与基准值touchSetDefaultThreshold()在示例中可以看到另一个与阈值相关的 API——touchSetDefaultThreshold(float percentage)。当向touchAttachInterrupt传入的threshold为0时驱动会使用基准值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_0VS2/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 —— 读取触摸值最简单的触摸测试程序每秒打印一次 T2GPIO 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); }使用注意事项与最佳实践先校准再设阈值touchRead()是挑选阈值的第一手依据。分别记录未触摸与触摸状态下的读数取中间值作为touchAttachInterrupt的threshold或直接传入0使用touchSetDefaultThreshold()的基准值百分比方案。注意读数的方向性ESP32 触摸读数下降、ESP32-S2/S3 读数上升判断阈值是否触发时方向相反跨芯片移植代码时务必重新校准。睡眠唤醒限制ESP32-S2/S3 仅支持一个触摸唤醒焊盘新版 API 中浅睡眠light sleep下所有已启用焊盘均可唤醒。引脚资源互斥触摸引脚由 periman 统一管理与 ADC、GPIO 等总线互斥若引脚已被其他外设占用触摸初始化会失败或返回 0可通过log_e输出排查。ISR 保持轻量中断回调内只置标志位或做极简操作复杂逻辑放到loop()需要向回调传参时使用touchAttachInterruptArg。版本差异IDF ≥ 5.5 或 TOUCH_V3如 ESP32-P4芯片走新的 esp32-hal-touch-ng APItouchSetCycles已更名为touchSetTiming请以实际编译使用的核心版本为准。【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表