【ESP32-IDF笔记】06-触摸传感IO配置
配置环境
Visual Studio Code :版本1.98.2
ESP32:ESP32-S3
ESP-IDF:V5.4
概述
触摸传感器系统由保护覆盖层、触摸电极、绝缘基板和走线组成,保护覆盖层位于最上层,绝缘基板上设有电极及走线。触摸覆盖层将引起电容变化,根据电容变化,可以判断此次触摸是否为有效触摸行为。
触摸传感器可以以矩阵或滑条等方式组合使用,从而覆盖更大触感区域及更多触感点。触摸传感由软件或专用硬件计时器发起,由有限状态机 (FSM) 硬件控制。
ESP32-S3 提供了多达 14 个电容式传感 GPIO,能够探测由手指或其他物品直接接触或接近而产生的电容差异。
这种设计具有低噪声和高灵敏度的特点,可以用于支持使用相对较小的触摸板。设计中也可以使用触摸板阵列以探测更大区域或更多点。ESP32-S3 的触摸传感器同时还支持防水和数字滤波等功能来进一步提高传感器的性能。
功能介绍
下面将 API 分解成几个函数组进行介绍:
- 初始化触摸传感器驱动程序
- 配置触摸传感器 GPIO 管脚
- 触摸状态测量
- 调整测量参数(优化测量)
- 滤波采样
- 触摸监测方式
- 设置中断信号监测触碰动作
- 中断触发,唤醒睡眠模式
请前往 API 参考 章节,查看某一函数的具体描述。应用示例 章节则介绍了此 API 的具体实现。
初始化触摸传感器驱动程序
使用触摸传感器之前,需要先调用 touch_pad_init()
函数初始化触摸传感器驱动程序。此函数设置了 API 参考 项下的 Macros 中列出的几项 .._DEFAULT
驱动程序参数,同时删除之前设置过的触摸传感器信息(如有),并禁用中断。
如果不再需要该驱动程序,可以调用 touch_pad_deinit()
释放已初始化的驱动程序。
配置触摸传感器 GPIO 管脚
可调用 touch_pad_config()
使能某一 GPIO 的触感功能。ESP32 最多可支持 10 个电容式触摸传感器通道。
触摸传感器通道 | GPIO 管脚 |
---|---|
T0 | GPIO4 |
T1 | GPIO0 |
T2 | GPIO2 |
T3 | MTDO |
T4 | MTCK |
T5 | MTDI |
T6 | MTMS |
T7 | GPIO27 |
T8 | 32K_XN |
T9 | 32K_XP |
使用 touch_pad_set_fsm_mode()
选择触摸传感器测量(由 FSM 操作)是由硬件定时器自动启动,还是由软件自动启动。如果选择软件模式,请使用 touch_pad_sw_start()
启动 FSM。
触摸状态测量
借助以下两个函数从传感器读取原始数据和滤波后的数据:
touch_pad_read_raw_data()
touch_pad_read_filtered()
这两个函数也可以用于检查触碰和释放触摸传感器时传感器读数变化范围,然后根据这些信息设定触摸传感器的触摸阈值。
备注
使用
touch_pad_read_filtered()
之前,需要先调用 滤波采样 中特定的滤波器函数来初始化并配置该滤波器。
测量方式
请参考应用示例 peripherals/touch_sensor/touch_sensor_v1/touch_pad_read,查看如何使用读取触摸传感器数据。
测量方式
触摸传感器会统计固定时间内的充放电次数,其计数结果即为原始数据,可由 touch_pad_read_raw_data()
读出。上述固定时间可通过 touch_pad_set_measurement_clock_cycles()
设置。完成一次测量后,触摸传感器会在下次测量开始前保持睡眠状态。两次测量之前的间隔时间可由 touch_pad_set_measurement_interval()
进行设置。
备注
若设置的计数时间太短(即测量持续的时钟周期数太小),则可能导致结果不准确,但是过大的计数时间也会造成功耗上升。另外,若睡眠时间加测量时间的总时间过长,则会造成触摸传感器响应变慢。
优化测量
触摸传感器设有数个可配置参数,以适应触摸传感器设计特点。例如,如果需要感知较细微的电容变化,则可以缩小触摸传感器充放电的参考电压范围。使用 touch_pad_set_voltage()
函数,可以设置电压参考低值和参考高值。
优化测量除了可以识别细微的电容变化之外,还可以降低应用程序功耗,但可能会增加测量噪声干扰。如果得到的动态读数范围结果比较理想,则可以调用 touch_pad_set_measurement_clock_cycles()
函数来减少测量时间,从而进一步降低功耗。
可用的测量参数及相应的 ‘set’ 函数总结如下:
-
触摸传感器充放电参数:
- 电压门限:
touch_pad_set_voltage()
- 速率(斜率):
touch_pad_set_cnt_mode()
- 电压门限:
-
单次测量所用的时钟周期:
touch_pad_set_measurement_clock_cycles()
电压门限(参考低值/参考高值)、速率(斜率)与测量时间的关系如下图所示:
上图中的 Output 代表触摸传感器读值,即一个测量周期内测得的脉冲计数值。
所有函数均成对出现,用于设定某一特定参数,并获取当前参数值。例如:touch_pad_set_voltage()
和 touch_pad_get_voltage()
。
滤波采样
如果测量中存在噪声,可以使用提供的 API 函数对采样进行滤波。使用滤波器之前,请先调用 touch_pad_filter_start()
启动该滤波器。
滤波器类型为 IIR(无限脉冲响应滤波器),可以调用 touch_pad_set_filter_period()
配置此类滤波器的采样周期。
如需停止滤波器,请调用 touch_pad_filter_stop()
函数。如果不再使用该滤波器,请调用 touch_pad_filter_delete()
删除此滤波器。
触摸监测
触摸监测基于配置的阈值和 FSM 执行的原始测量,并由 ESP32 硬件实现。可以调用 touch_pad_get_status()
查看被触碰的触摸传感器,或调用 touch_pad_clear_status()
清除触摸状态信息。
也可以将硬件触摸监测连接至中断,详细介绍见下一章节。
如果测量中存在噪声,且电容变化幅度较小,硬件触摸监测结果可能就不太理想。如需解决这一问题,不建议使用硬件监测或中断信号,建议在自己的应用程序中进行采样滤波,并执行触摸监测。请参考 peripherals/touch_sensor/touch_sensor_v1/touch_pad_interrupt,查看以上两种触摸监测的实现方式。
中断触发
在对触摸监测启用中断之前,请先设置一个触摸监测阈值。然后使用 触摸状态测量 中所述的函数读取并显示触摸和释放触摸传感器时测得的结果。如果测量中存在噪声且相对电容变化较小,请使用滤波器。也可以根据应用程序和环境条件,测试温度和电源电压变化对测量值的影响。
确定监测阈值后就可以在初始化时调用 touch_pad_config()
设置此阈值,或在运行时调用 touch_pad_set_thresh()
设置此阈值。
下一步就是设置如何触发中断。可以设置在阈值以下或以上触发中断,具体触发模式由函数 touch_pad_set_trigger_mode()
设置。
最后可以使用以下函数配置和管理中断调用:
touch_pad_isr_register()
/touch_pad_isr_deregister()
touch_pad_intr_enable()
/touch_pad_intr_disable()
中断配置完成后,可以调用 touch_pad_get_status()
查看中断信号来自哪个触摸传感器,也可以调用 touch_pad_clear_status()
清除触摸传感器状态信息。
备注
触摸监测中的中断信号基于原始/未经滤波的采样(对比设置的阈值),并在硬件中实现。启用软件滤波 API(请参考 滤波采样)并不会影响这一过程。
从睡眠模式唤醒
如果使用触摸传感器中断将芯片从睡眠模式唤醒,可以选择配置一些触摸传感器,例如 SET1 或 SET1 和 SET2,触摸这些触摸传感器将触发中断并唤醒芯片。请调用 touch_pad_set_trigger_source()
实现上述操作。
可以使用以下函数管理 ‘SET’ 中触摸传感器所需的位模式配置:
touch_pad_set_group_mask()
/touch_pad_get_group_mask()
touch_pad_clear_group_mask()
API 参考
头文件
- components/driver/touch_sensor/esp32/include/driver/touch_sensor.h
- components/driver/touch_sensor/include/driver/touch_sensor_common.h
#include "driver/touch_sensor.h"
#include "driver/touch_sensor_common.h"
CMakeLists.txt添加
REQUIRES driver
函数
初始化触控模块
esp_err_t touch_pad_init(void)
返回:* ESP_OK成功
- ESP_ERR_NO_MEM 触摸板初始化错误
- ESP_ERR_NOT_SUPPORTED触摸板正在为外部 XTAL 提供电流
注意
如果 default 参数与使用场景不匹配,可以在执行此函数后进行更改。
卸载触摸板驱动程序
esp_err_t touch_pad_deinit(void)
返回:
-
ESP_OK成功
-
ESP_FAIL 触摸板驱动程序未初始化
注意
调用该函数后,禁止调用其他触控函数。
初始化触摸板 GPIO
esp_err_t touch_pad_io_init(touch_pad_t touch_num)
参数:
- touch_num – 触摸板索引
返回:
-
成功ESP_OK
-
ESP_ERR_INVALID_ARG 如果参数错误
设置触摸传感器的高电压阈值
esp_err_t touch_pad_set_voltage(touch_high_volt_t refh, touch_low_volt_t refl, touch_volt_atten_t atten)
触摸传感器通过对通道进行充电和放电来测量通道电容值。所以高阈值应小于电源电压。
参数:
-
refh – DREFH 的值
-
refl – DREFL 的值
-
atten – DREFH 上的衰减
返回:
-
成功ESP_OK
-
ESP_ERR_INVALID_ARG 如果参数错误
获取触摸传感器参考电压
esp_err_t touch_pad_get_voltage(touch_high_volt_t *refh, touch_low_volt_t *refl, touch_volt_atten_t *atten)
参数:
-
refh – 接受 DREFH 值的指针
-
refl – 接受 DREFL 值的指针
-
atten – 接受 DREFH 衰减的指针
返回:
- 成功ESP_OK
设置每个焊盘的触摸传感器充放电速度
esp_err_t touch_pad_set_cnt_mode(touch_pad_t touch_num, touch_cnt_slope_t slope, touch_tie_opt_t opt)
如果斜率为 0,则计数器将始终为零。如果斜率为 1,则相应地充电和放电会很慢。如果斜率设置为 7(最大值),则充电和放电将很快。
参数:
-
touch_num – 触摸板索引
-
slope – 触摸板充放电速度
-
opt – 初始电压
返回:
-
成功ESP_OK
-
ESP_ERR_INVALID_ARG 如果参数错误
注意:
充放电电流越高,触摸通道的抗扰度就越大,但会增加系统功耗。
获取每个焊盘的触摸传感器充电/放电速度
esp_err_t touch_pad_get_cnt_mode(touch_pad_t touch_num、 touch_cnt_slope_t *slope、 touch_tie_opt_t *opt)
参数:
-
touch_num – 触摸板索引
-
slope – 接受触摸板充电/放电斜率的指针
-
opt – 接受初始电压的指针
返回:
-
成功ESP_OK
-
ESP_ERR_INVALID_ARG 如果参数错误
取消注册
esp_err_t touch_pad_isr_deregister(void (fn)(void)), void *arg)
取消注册以前使用 touch_pad_isr_handler_register 注册的处理程序。
参数:
-
fn – 要调用的处理程序函数(传递给 touch_pad_isr_handler_register)
-
arg – 处理程序的参数(传递给 touch_pad_isr_handler_register)
.h文件
touch.h
#ifndef __TOUCH_H
#define __TOUCH_H#include "esp_err.h"void touch_init(void); //初始化
void touch_read_task(void ); //测试案例
#endif
.c文件
touch.c
#include "touch.h"#include <stdio.h>#include "freertos/FreeRTOS.h"#include "freertos/task.h"#include "freertos/queue.h"#include "esp_log.h"#include "driver/touch_pad.h"//参考案例 peripherals/touch_sensor/touch_sensor_v2/touch_pad_interruptstatic const char *TAG = "touch->";// 自定义添加需要配置的触摸IO#define TOUCH_BUTTON_NUM 2static const touch_pad_t button[TOUCH_BUTTON_NUM] = {TOUCH_PAD_NUM1, // TOUCH_PAD_NUM2, // // If this pad be touched, other pads no response.};/** 触摸阈值。该阈值决定了触摸的灵敏度。* 此阈值是通过测试不同触摸通道读数的变化得出的。* 如果 (原始数据 - 基准值) > 基准值 * 阈值,则触摸板被激活。* 如果 (原始数据 - 基准值) < 基准值 * 阈值,则触摸板未被激活。*/static const float button_threshold[TOUCH_BUTTON_NUM] = {0.2, // 20%.0.2, // 20%.};static QueueHandle_t que_touch = NULL;typedef struct touch_msg {touch_pad_intr_mask_t intr_mask;uint32_t pad_num;uint32_t pad_status;uint32_t pad_val;} touch_event_t;#define TOUCH_BUTTON_WATERPROOF_ENABLE 0 //防水功能#define TOUCH_BUTTON_DENOISE_ENABLE 1#define TOUCH_CHANGE_CONFIG 0 //是否更改默认配置/*处理触摸板被触摸时触发的中断。识别被触摸的是哪个触摸板,并将其记录在表格中。
*/static void touchsensor_interrupt_cb(void *arg){int task_awoken = pdFALSE;touch_event_t evt;evt.intr_mask = touch_pad_read_intr_status_mask();evt.pad_status = touch_pad_get_status();evt.pad_num = touch_pad_get_current_meas_channel();//ESP_LOGW(TAG, "interrupt cb");xQueueSendFromISR(que_touch, &evt, &task_awoken);if (task_awoken == pdTRUE) {portYIELD_FROM_ISR();}}/// @brief 设置中断阈值/// @param static void touch_set_thresholds(void){uint32_t touch_value;for (int i = 0; i < TOUCH_BUTTON_NUM; i++) {//读取基准值touch_pad_read_benchmark(button[i], &touch_value);//设置中断阈值touch_pad_set_thresh(button[i], touch_value * button_threshold[i]);ESP_LOGI(TAG, "touch pad [%d] base %"PRIu32", thresh %"PRIu32, \button[i], touch_value, (uint32_t)(touch_value * button_threshold[i]));}}static void touch_set_filter_set(touch_filter_mode_t mode){/* 滤波功能 */touch_filter_config_t filter_info = {.mode = mode, // Test jitter and filter 1/4..debounce_cnt = 1, // 1 time count..noise_thr = 0, // 50%.jitter_step = 4, // use for jitter mode..smh_lvl = TOUCH_PAD_SMOOTH_IIR_2,};touch_pad_filter_set_config(&filter_info);touch_pad_filter_enable();ESP_LOGI(TAG, "touch pad filter init");}void touch_init(void){if (que_touch == NULL) {que_touch = xQueueCreate(TOUCH_BUTTON_NUM, sizeof(touch_event_t));}// 初始化触模块外设,它将启动一个定时器来运行滤波器。ESP_LOGI(TAG, "Initializing touch pad");/* 初始化触模块 */touch_pad_init();for (int i = 0; i < TOUCH_BUTTON_NUM; i++) {touch_pad_config(button[i]); //为每个触摸通道配置参数。}/* 触摸模块0的去噪设置。. */touch_pad_denoise_t denoise = {/* 要消除的位是根据噪声水平来确定的。 */.grade = TOUCH_PAD_DENOISE_BIT4,/* 通过调整参数,T0的读数应近似于被测通道的读数。 */.cap_level = TOUCH_PAD_DENOISE_CAP_L4,};touch_pad_denoise_set_config(&denoise);touch_pad_denoise_enable();ESP_LOGI(TAG, "Denoise function init");/* 滤波模设置 */touch_set_filter_set(TOUCH_PAD_FILTER_IIR_16); //滤波模式为一阶无限冲激响应(IIR)滤波器。系数为16(典型值) 。 touch_pad_timeout_set(true, TOUCH_PAD_THRESHOLD_MAX); //启用超时检查,并为所有触摸传感器通道测量设置超时阈值。/* 注册触摸中断中断服务程序(ISR),启用中断类型 . */touch_pad_isr_register(touchsensor_interrupt_cb, NULL, TOUCH_PAD_INTR_MASK_ALL);/* 如果你有其他触摸算法,你可以在生成 `TOUCH_PAD_INTR_MASK_SCAN_DONE` 中断后获取测量值。. */touch_pad_intr_enable(TOUCH_PAD_INTR_MASK_ACTIVE | TOUCH_PAD_INTR_MASK_INACTIVE | TOUCH_PAD_INTR_MASK_TIMEOUT);/* 启用触摸传感器时钟。工作模式为“定时器触发”。 */touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);touch_pad_fsm_start(); //启动触摸传感器有限状态机/* 等待触摸传感器初始化完成 */vTaskDelay(50 / portTICK_PERIOD_MS);touch_set_thresholds();}//测试案例void touch_read_task(void ){touch_event_t evt = {0};int ret = xQueueReceive(que_touch, &evt, (TickType_t)0); //if (ret != pdTRUE) {return;}if (evt.intr_mask & TOUCH_PAD_INTR_MASK_ACTIVE) { //对其中一个已启用的通道生效。ESP_LOGI(TAG, "TouchSensor [%"PRIu32"] be activated, status mask 0x%"PRIu32"", evt.pad_num, evt.pad_status);}if (evt.intr_mask & TOUCH_PAD_INTR_MASK_INACTIVE) { //对其中一个已启用的通道无效。ESP_LOGI(TAG, "TouchSensor [%"PRIu32"] be inactivated, status mask 0x%"PRIu32, evt.pad_num, evt.pad_status);}if (evt.intr_mask & TOUCH_PAD_INTR_MASK_SCAN_DONE) { //对所有已启用的通道进行测量。ESP_LOGI(TAG, "The touch sensor group measurement is done [%"PRIu32"].", evt.pad_num);}if (evt.intr_mask & TOUCH_PAD_INTR_MASK_TIMEOUT) { //其中一个已启用通道超时。/* 在这里添加你的异常处理。*/ESP_LOGI(TAG, "Touch sensor channel %"PRIu32" measure timeout. Skip this exception channel!!", evt.pad_num);touch_pad_timeout_resume(); // 指向下一个要测量的通道。}}
使用
- 在.c文件中添加需要使用的触摸IO
// 添加需要配置的触摸IO#define TOUCH_BUTTON_NUM 2 //个数static const touch_pad_t button[TOUCH_BUTTON_NUM] = {TOUCH_PAD_NUM1, // 对应ESP32S3 -> GPIO1TOUCH_PAD_NUM2, // 对应ESP32S3 -> GPIO2};
- 在.c文件中设置触摸阈值
static const float button_threshold[TOUCH_BUTTON_NUM] = {0.2, // 20%.0.2, // 20%.};
- 在app_main中进行初始化和调用
//....
#include "touch.h"
//....
void app_main(void)
{//....touch_init();while (1){touch_read_task(); vTaskDelay(50/portTICK_PERIOD_MS);}
}
运行结果
用手触摸GPIO1然后离开时
用手触摸GPIO2 然后离开时
GPIO2短接3.3V时或者短接GND时