本文介绍了 ESP-IDF 框架下的 ESP32 触摸传感器相关功能,章节目录如下:
文章目录
1、前提
1.1、基础知识
1.1.1、触摸通道
仅部分型号芯片支持触摸传感器功能,比如:ESP32、ESP32-S2、ESP32-S3 和 ESP32-P4 等,不同型号芯片支持的触摸传感器版本也不同,具体如 电容式触摸传感器版本概览 表所示,以下仅以 ESP32 系列型号芯片对触摸传感器介绍。
ESP32 有 10 个触摸通道,从 TOUCH_PAD_NUM0
到 TOUCH_PAD_NUM9
,10 个触摸通道的具体对应引脚表如下所示:
触摸传感器通道 | No. | 管脚名称 |
---|---|---|
TOUCH0 | 24 | GPIO4 |
TOUCH1 | 23 | GPIO0 |
TOUCH2 | 22 | GPIO2 |
TOUCH3 | 21 | MTDO |
TOUCH4 | 20 | MTCLK |
TOUCH5 | 18 | MTDI |
TOUCH6 | 17 | MTMS |
TOUCH7 | 16 | GPIO27 |
TOUCH8 | 13 | 32K_XN |
TOUCH9 | 12 | 32K_XP |
1.1.2、触摸原理
关于触摸传感器的具体应用说明请阅读 触摸传感器应用说明 ,简单总结来说:
ESP32 触摸传感器可以测量触摸通道上的总电容量,而人体接近或触摸电极时,电容会增大。当电容量发生改变且变化量超过阈值,会判定为 “手指触摸动作”。反映到程序上,当人体接近或触摸电极时,通过 touch_pad_read()
返回的值通常会减少。
为什么会减少呢?
ESP32 测量电容的方式是在单位时间内给触摸引脚充放电并记录其充放电次数,然后作为函数返回值返回。当人体接近或触摸电极时,电容增大,当触摸引脚电容较大时,单位时间内的充放电次数减小,通过 touch_pad_read()
返回的值通常会减少。
下图是触摸传感器系统的电容分布情况:
1.1.3、中断
触摸传感器中断启动流程:
touch_pad_config(TOUCH_PAD_CHANNEL, Non-zero)
touch_pad_isr_register()
touch_pad_set_trigger_mode()
touch_pad_intr_enable()
touch_pad_config
需要设置一个非零数作为中断触发的阈值(触摸通道计数值不可能小于零),如果阈值设置为 0 ,可以通过后续 touch_pad_set_thresh
手动修改阈值
ESP32 触摸传感器中断模式:
typedef enum {
TOUCH_TRIGGER_BELOW = 0, /* 计数值小于阈值 */
TOUCH_TRIGGER_ABOVE = 1, /* 计数值大于阈值 */
TOUCH_TRIGGER_MAX,
} touch_trigger_mode_t;
一般常使用 TOUCH_TRIGGER_BELOW
模式,因为当人体接近或触摸电极时,读取触摸通道返回的值将变小(原因见 1.1.1 节内容)。
1.2、数据结构
ESP-IDF 驱动头文件
#include "driver/touch_pad.h"
1.3、硬件原理图
触摸传感器为 ESP32 芯片内部硬件外设,无硬件原理图,笔者使用的开发板为淘宝上购买的 ESP32 WROOM 开发板,读者可自行购买。
2、示例程序
2.1、轮询手动触发
功能:
- 触摸
TOUCH_PAD_CHANNEL
对应的引脚输出触摸提示
程序流程:
- 调用
touch_pad_init()
初始化触摸传感器驱动程序 - 【可选】调用
touch_pad_filter_start()
- 调用
touch_pad_config(TOUCH_PAD_CHANNEL, 0)
使能某一 GPIO 的触感功能 - 调用
touch_pad_read() / touch_pad_read_filtered() / touch_pad_read_raw_data()
从触摸通道读取数据 - 自行判断读取到的值是否小于阈值,小于则表示触摸
示例说明:
- 静态触发阈值为
baseline * TOUCH_PAD_THRESHOLD_PERCENTAGE
- 基线
baseline
为连续 100 次采集的平均值 - 宏
TOUCH_PAD_THRESHOLD_PERCENTAGE
为阈值百分比(0-1),越大触摸越灵敏
示例程序:
#include "freertos/FreeRTOS.h"
#include "driver/touch_pad.h"
#include "esp_log.h"
#define TOUCH_PAD_CHANNEL TOUCH_PAD_NUM3
#define TOUCH_PAD_THRESHOLD_PERCENTAGE 0.5
/* The following does not need to be modified */
#define TOUCH_THRESH_NO_USE 0
static const char *TAG = "Touch_Pad_Example";
static uint16_t static_threshold = 0;
uint16_t calibrate_threshold(touch_pad_t pad) {
const int sample_count = 100;
uint32_t sum = 0;
uint16_t raw;
for (int i = 0; i < sample_count; i++) {
touch_pad_read(pad, &raw);
sum += raw;
vTaskDelay(pdMS_TO_TICKS(10));
}
uint16_t baseline = sum / sample_count;
return baseline * TOUCH_PAD_THRESHOLD_PERCENTAGE;
}
static void tp_init(void)
{
esp_err_t ret;
// 初始化 Touch_Pad
ret = touch_pad_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "touch_pad_init failed, ret = %d", ret);
return;
}
// 设置触摸传感器去噪参数(可选)
touch_pad_filter_start(10);
// 初始化指定通道的触摸传感器,并设置它的初始灵敏度阈值
ret = touch_pad_config(TOUCH_PAD_CHANNEL, TOUCH_THRESH_NO_USE);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "touch_pad_config failed, ret = %d", ret);
return;
}
// 获得静态触发阈值
static_threshold = calibrate_threshold(TOUCH_PAD_CHANNEL);
ESP_LOGI(TAG, "Calibrated threshold: %d", static_threshold);
}
void app_main(void)
{
uint16_t touch_value = 0;
esp_err_t ret;
tp_init();
while (1) {
ret = touch_pad_read(TOUCH_PAD_CHANNEL, &touch_value);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "Touch Value: %d", touch_value);
// 低于设定阈值则认为是触摸
if (touch_value < static_threshold) {
ESP_LOGI(TAG, "Touch Detected!");
}
} else {
ESP_LOGE(TAG, "touch_pad_read error, ret = %d", ret);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
// Will not execute
touch_pad_deinit();
}
2.2、中断回调自动触发
功能:
- 触摸
TOUCH_PAD_CHANNEL
对应的引脚触发中断翻转 LED 灯状态
程序流程:
- 调用
touch_pad_init()
初始化触摸传感器驱动程序 - 【可选】调用
touch_pad_filter_start()
对触摸传感器采样进行滤波 - 调用
touch_pad_config()
使能某一 GPIO 的触感功能 - 调用
touch_pad_set_thresh()
设置触摸传感器触发阈值 - 调用
touch_pad_isr_register()
注册触摸传感器中断服务函数 - 调用
touch_pad_set_trigger_mode()
设置触摸传感器中断模式 - 调用
touch_pad_intr_enable()
启用中断 - 在中断服务函数中调用
touch_pad_get_status()
获取触摸传感器通道活动状态掩码 - 调用
touch_pad_clear_status()
清除中断状态 - 如果触发的通道是你确定的触摸通道,则翻转 LED 灯状态
示例程序:
#include "freertos/FreeRTOS.h"
#include "driver/touch_pad.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include <rom/ets_sys.h>
#define TOUCH_PAD_CHANNEL TOUCH_PAD_NUM3
#define TOUCH_PAD_THRESHOLD_PERCENTAGE 0.5
#define LED_GPIO_PIN GPIO_NUM_13
/* The following does not need to be modified */
#define TOUCH_THRESH_NO_USE 0
static const char *TAG = "Touch_Pad_ISR_Example";
static uint16_t static_threshold = 0;
static volatile bool led_state = false;
void led_init(void) {
gpio_config_t io_cfg = {
.pin_bit_mask = 1 << LED_GPIO_PIN,
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = 0,
.pull_down_en = 0,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config( & io_cfg);
}
uint16_t calibrate_threshold(touch_pad_t pad) {
const int sample_count = 100;
uint32_t sum = 0;
uint16_t raw;
for (int i = 0; i < sample_count; i++) {
touch_pad_read(pad, &raw);
sum += raw;
vTaskDelay(pdMS_TO_TICKS(10));
}
uint16_t baseline = sum / sample_count;
return baseline * TOUCH_PAD_THRESHOLD_PERCENTAGE;
}
static void IRAM_ATTR touch_isr_handler(void *arg)
{
// 获取中断状态
uint32_t pad_intr = touch_pad_get_status();
// 清除中断状态
touch_pad_clear_status();
// 如果是你使用的 Touch_Pad 通道
if (pad_intr & (1 << TOUCH_PAD_CHANNEL)) {
ets_printf("Touch Detected!\r\n");
led_state = !led_state;
gpio_set_level(LED_GPIO_PIN, led_state);
}
}
static void tp_init(void)
{
esp_err_t ret;
// 初始化 Touch_Pad
ret = touch_pad_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "touch_pad_init failed, ret = %d", ret);
return;
}
// 设置触摸传感器去噪参数(可选)
touch_pad_filter_start(100);
// 初始化指定通道的触摸传感器,并设置它的初始灵敏度阈值
ret = touch_pad_config(TOUCH_PAD_CHANNEL, TOUCH_THRESH_NO_USE);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "touch_pad_config failed, ret = %d", ret);
return;
}
// 获得静态触发阈值并设置
static_threshold = calibrate_threshold(TOUCH_PAD_CHANNEL);
ESP_LOGI(TAG, "Calibrated threshold: %d", static_threshold);
touch_pad_set_thresh(TOUCH_PAD_CHANNEL, static_threshold);
// 注册触摸传感器中断处理函数
ret = touch_pad_isr_register(touch_isr_handler, NULL);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "touch_pad_isr_register failed, ret = %d", ret);
}
// 启用触摸传感器中断
touch_pad_set_trigger_mode(TOUCH_TRIGGER_BELOW);
touch_pad_intr_enable();
}
void app_main(void)
{
led_init();
tp_init();
}
3、常用函数
// 触摸传感器初始化/反初始化
esp_err_t touch_pad_init(void);
esp_err_t touch_pad_deinit(void);
// 初始化指定通道的触摸传感器,并设置它的初始灵敏度阈值
esp_err_t touch_pad_config(touch_pad_t touch_num, uint16_t threshold);
// 设置/获取灵敏度阈值
esp_err_t touch_pad_set_thresh(touch_pad_t touch_num, uint16_t threshold);
esp_err_t touch_pad_get_thresh(touch_pad_t touch_num, uint16_t *threshold);
// 读取滤波后/原始数据
esp_err_t touch_pad_read(touch_pad_t touch_num, uint16_t *touch_value);
esp_err_t touch_pad_read_filtered(touch_pad_t touch_num, uint16_t *touch_value);
esp_err_t touch_pad_read_raw_data(touch_pad_t touch_num, uint16_t *touch_value);
// 设置触摸传感器去噪参数
esp_err_t touch_pad_filter_start(uint32_t filter_period_ms);
// 停止去噪
esp_err_t touch_pad_filter_stop(void);
// 注册触摸传感器中断处理函数
esp_err_t touch_pad_isr_register(intr_handler_t fn, void *arg);
// 触摸传感器中断触发模式设置
esp_err_t touch_pad_set_trigger_mode(touch_trigger_mode_t mode);
// 中断使能/失能
esp_err_t touch_pad_intr_enable(void);
esp_err_t touch_pad_intr_disable(void);
4、烧录验证
“轮询手动触发” 实验效果如下:
根据上图可知,当用手触摸 TOUCH_PAD_CHANNEL
对应的引脚时,串口将输出 ”Touch Detected!“。
“中断回调自动触发” 实验效果如下:
根据上图可知,当用手触摸 TOUCH_PAD_CHANNEL
对应的引脚时,串口将输出 “Touch Detected!”,并且 LED 引脚状态出现翻转情况。