触摸传感器
文章目录
- 触摸传感器
- 1. 触摸传感器简介
- 1.1 触摸传感器结构
- 1.2 触摸传感器原理
- 1.3 触摸传感器的灵敏度优化
- 1.4 esp32检测过程
- 2. 触摸传感器框图剖析
- 2.1 触发源
- 2.2 触摸传感器控制器
- 2.3 触摸传感器执行器
- 2.4 处理器
- 3. 触摸传感器配置流程
- 3.1 触摸传感器初始化
- 3.2 配置触摸传感器参数
- 3.2.1 配置触发模式
- 3.2.2 设置充放电参数
- 3.2.4 设置IIR滤波器
- 3.2.5 载入配置
- 3.3 配置相关的gpio
- 3.4 配置触发阈值
- 3.5 配置相关中断
- 4. 触摸传感器实验
- 4.1 脉冲读取实验
- 4.1.1 实验目的
- 4.1.2 软件设计
- 4.2 基于轮询的触摸按键检测实验
- 4.2.1 实验目的
- 4.2.2 硬件设计
- 4.2.3 软件设计
- 4.2.3.1 定义相关的宏
- 4.2.3.2 定义相关的数据结构体
- 4.2.3.4 触摸传感器的使能函数
- 4.2.3.3 触摸传感器的配置
- 4.2.3.4 按键状态监测
- 4.2.3.5 主函数
- 4.2.4 完整代码
- 4.2.4.1 bsp_touch.h
- 4.2.4.2 bsp_touch.cpp
- 4.2.4.4 main
- 5.参考资料
1. 触摸传感器简介
关于触摸传感器原理非常详细的介绍可以看官方的文章,“ESP32 触摸传感器应用方案简介”。这里做简要讲解。
1.1 触摸传感器结构
我们先来看触摸传感器的结构[3]。
典型的触摸传感器结构包括: 保护层、触摸电极、绝缘基板、走线
- 保护层:保护层位置处在触摸电极的上层,必须是绝缘的,起到保护电极的作用。但是保护层也会使得触摸灵敏度下降
- 触摸电极:触摸电极与gnd形成电容,这个电容量用于指示电极有没有被触摸。
- 绝缘基板:绝缘基板主要对触摸电极起支撑作用
- 走线:走线会引入干扰和寄生电容,在pcb设计的时候,走线一定要尽可能的短。
1.2 触摸传感器原理
我们知道一个显而易见的道理,就是在充电电流一定的情况下,给电容充电到相同电压,电容容量越大,充电时间越长。其实触摸传感器就是利用这个原理设计的。
我们需要了解的是,构成触摸传感器电容量的要素有那些[3]:
电容组成 | 说明 |
Cground | 触摸电路参考地和大地之间的电容 |
Ccomponet | ESP32 内部寄生电容 |
Ctrace | 走线与电路参考地之间的寄生电容 |
Celectrode | 触摸电极与电路参考地之间的寄生电容 |
Ctouch | 手指与触摸电极所形成的相对于大地的触摸电容 |
这其中起到最主要作用的就是触摸电极和参考地之间的电容。当手指触摸到触摸电极的时候,手指与触摸电极之间也会形成电容,增大了触摸电极处的整体电容量。这样的话,对触摸电极进行充电的时候,充电时间就会变长。
图A是无手指触摸的充电特性曲线,图B是有手指触摸的充电特性曲线。利用这个充电时间的长短,我们就可以判断触摸电极有没有被触摸
1.3 触摸传感器的灵敏度优化
同时,我们也注意到,影响触摸传感器灵敏度的决定性因素,其实就是触摸电容和寄生电容之间的相对大小。如果触摸电容相对较大,手指触摸以后,整体电容就有明显变化,充电时间也就有明显差异。
因此,如果要提高触摸按键的灵敏度,我们应该减少寄生电容,并且尽量增加触摸电容。
电容类型 | 组成 | 电容优化方向 | 优化方法 |
寄生电容 Cp | Ctrace + Celectrode + Ccomponet | 减少 | 减少走线长度,优化 PCB 布局 |
触摸电容 | Ctouch | 增加 | 覆盖层与电极紧密贴合,选用介电常数大的覆盖层,减少覆盖层厚度,增大触摸电极的面积 |
固定电容 | Cground | - | - |
1.4 esp32检测过程
esp32的触摸传感器检测原理上,大致也是采取的上述原理。
当检测开始的时候,触摸传感器的引脚会产生一段时间的三角波,对触摸电极进行不断的充放电,然后esp32会把这段三角波每一个变化周期记录为一个脉冲,最后统计这段时间内产生的脉冲数量。如果手触摸的电极,充电时间变长,脉冲数会减少。因此,设定一个阈值,如果测定的脉冲数量小于阈值,就可认为电极被触摸了。
2. 触摸传感器框图剖析
触摸传感器可以分为四个功能模块
- (1):触发源
- (2):触摸传感器控制器
- (3):触摸传感器执行器
- (4):处理器
2.1 触发源
触摸传感器如果想要发出检测三角波,需要有一个触发源,告诉触摸传感器什么时候发出这个检测电波。
触发源包括硬件触发和软件触发。可以通过touch_pad_set_fsm_mode() 进行设置
硬件触发是指,启动一个定时器,每隔一段时间就自动发送一段检测电波。
软件触发是指,默认情况下不发送检测电波,只有运行touch_pad_read_raw_data() 等需要获取脉冲数的相关函数的时候,才会发送检测电波进行触摸检测。
2.2 触摸传感器控制器
触摸传感器控制器的主要功能是使能相应的触摸传感器,并且对产生的三角波特性进行配置。包括:
- 使能相应的控制器及引脚
- 设定每次发送检测电波的持续时间
- 设定触摸传感器的触发阈值
- 设定输出检测电波的电压范围
- 与下游执行器发送指令与交互数据
2.3 触摸传感器执行器
执行器主要完成的任务是,当接收到控制器传输来的检测电波发送命令的时候,按照控制器的设定,进行输出。同时,对输出的电平进行测量,记录产生的脉冲数量,然后把数据返回给控制器
2.4 处理器
处理器主要是处理控制器产生的请求,包括
- 脉冲计数达到阈值的时候产生的中断请求
- 触摸传感器产生的cpu唤醒请求。
3. 触摸传感器配置流程
3.1 触摸传感器初始化
//初始化传感器驱动程序
touch_pad_init();
3.2 配置触摸传感器参数
3.2.1 配置触发模式
//设置硬件触发
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
3.2.2 设置充放电参数
//设置充电门限,必须设置
touch_pad_set_voltage();
//设置充电速率,非必须
touch_pad_set_cnt_mode();
//设置测量时间,非必须
touch_pad_set_meas_time();
- 充电门限
用户可以设置触摸传感器内部电路充放电的电压门限、高电压衰减值(HATTEN)。门限的范围越小,脉冲计数值越大。但门限过小会导致读数的稳定性变差。选择合适的门限电压可以提高读数的稳定性。
下图是示波器抓取某一触摸管脚上的充放电电压波形,不同的电压参数的对比。
电压参数为:refh = 2.4V, refl = 0.8V, atten = 0V。
电压参数为:refh = 2.4V, refl = 0.8V, atten = 0.5V。
可以看到,高电压衰减使得能够达到的最大值减小了。
不同电压值对稳定性和灵敏度有一定的影响,门限电压范围越大,系统抗干扰能力越强。电压值建议选择 (refh = 2.7V, refl = 0.5V, atten = 0V) 或者 (refh = 2.4V, refl = 0.8V, atten = 0V) 组合。
如果高电压门限在供电电源允许范围内,衰减值应设置为 0V。
- 测量时间
- 充电速率
充电速率是通过调节充电电流大小进行调整的。高的充放电电流会增加抗干扰能力,建议选择 TOUCH_PAD_SLOPE_7。
3.2.4 设置IIR滤波器
touch_pad_filter_start(10);
ESP32 触摸传感器驱动包含无限脉冲响应滤波器(IIR)功能,用户可以使用此接口读取滤波之后的脉冲计数值。IIR 产生与 RC 滤波器相类似的阶跃响应。 IIR 滤波器能够衰减高频噪声成分,并忽略低频信号。下图是手指触摸响应的波形。
用户可以通过 API 设置 IIR 滤波器的采样周期。周期越大读数稳定性越高,相应的也会产生延迟。建议滤波周期的范围 10 ms~25 ms。下图是采样周期设置成 10 ms 与 20 ms 的滤波效果和延迟的对比图。黄色是滤波之后的读数,红色是没有经过滤波的读数。
- IIR 滤波周期:10 ms
- IIR 滤波周期:20 ms
下面两个图分别是 10 ms 和 20 ms 滤波周期滤波效果的对比图,黄色是滤波之后的读数。
- IIR 滤波周期:10 ms
- IIR 滤波周期:20 ms
值得说明的是,如果开启了滤波,数据的读取函数是
touch_pad_read_filtered(); //读取滤波后的数据
touch_pad_read_raw_data(); //读取原始的数据
没开启滤波使用
touch_pad_read();
3.2.5 载入配置
touch_pad_config(channel, 0);
如果这里第二个数字写的不是0,就是配置了触发阈值,如果不想在这里配置,可以写0,然后后面再进行配置
3.3 配置相关的gpio
把gpio配置为输出模式
gpio_pad_select_gpio(gpio);
gpio_set_direction(gpio, GPIO_MODE_OUTPUT);
需要注意的是,触摸传感器的通道与gpio是绑定的,必须对应使用。
触摸传感器通道 | 管脚 |
T0 | GPIO4 |
T1 | GPIO0 |
T2 | GPIO2 |
T3 | MTDO |
T4 | MTCK |
T5 | MTDI |
T6 | MTMS |
T7 | GPIO27 |
T8 | 32K_XN |
T9 | 32K_XP |
3.4 配置触发阈值
触发阈值在后面设定的原因是,有时候,不同的产品设计出来以后,触发阈值可能与较大的差异,可以采用自适应的方法设定阈值。就是在启动后,读取没有触摸的脉冲数量,然后再取其2/3作为触发阈值
//读取初始化没有触摸时候的脉冲数
uint16_t value;
touch_pad_read_filtered(Touch_Channel, &value);
//这个脉冲数的2/3作为阈值
Threshold = value * 2 / 3;
如果要使用中断的话,通过
touch_pad_set_thresh() 配置触发阈值
3.5 配置相关中断
中断的配置方法就是
- 配置中断阈值 touch_pad_config()或touch_pad_set_thresh()
- 配置中断模式touch_pad_set_trigger_mode()
- 写中断服务函数
- 载入中断服务函数touch_pad_isr_register()
- 使能中断 touch_pad_intr_enable()
不过,触摸传感器的中断使用下来感觉不怎么好用。主要原因是,只要你的手在传感器上,就会不断的触发中断,不太好控制。就比如如果要用这个控制led的话,没有办法很好的控制led亮灭。
4. 触摸传感器实验
4.1 脉冲读取实验
4.1.1 实验目的
读取触摸传感器的脉冲数。
4.1.2 软件设计
这个实验中,不使用中断,因此也不需要配置触发阈值
#include "driver/gpio.h"
#include "driver/touch_pad.h"
uint16_t value;
void setup() {
touch_pad_init(); //初始化传感器驱动程序
touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_0V);
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);//设置为硬件触发
touch_pad_config(TOUCH_PAD_NUM0, 0); //不使用中断,因此也不需要设置阈值
touch_pad_filter_start(10); //设置IIR滤波器
uint16_t value;
while (1)
{
touch_pad_read_raw_data(TOUCH_PAD_NUM0, &value); //读取原始数据
printf("raw data = %d\n", value);
touch_pad_read_filtered(TOUCH_PAD_NUM0, &value); //读取滤波后的数据
printf("IIR data = %d\n", value);
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void loop() {
}
4.2 基于轮询的触摸按键检测实验
4.2.1 实验目的
因为感觉触摸传感器的中断不是非常好用,所以这里实现了一个基于轮询的led开关控制的实验。
4.2.2 硬件设计
使用esp32开发板上gpio2带的led进行实验。采用gpio4(touch0)进行触摸检测,并且把gpio4用杜邦线引出。
主要现象就是,手触碰gpio4的时候,led亮灭状态会进行翻转。
4.2.3 软件设计
4.2.3.1 定义相关的宏
//定义触摸按键序号
#define Touch0 0
#define Touch1 1
#define Touch2 2
#define Touch3 3
#define Touch4 4
#define Touch5 5
#define Touch6 6
#define Touch7 7
#define Touch8 8
#define Touch9 9
//定义触摸按键的GPIO
#define T0 GPIO_NUM_4
#define T1 GPIO_NUM_1
#define T2 GPIO_NUM_2
#define T3 GPIO_NUM_15
#define T4 GPIO_NUM_13
#define T5 GPIO_NUM_12
#define T6 GPIO_NUM_14
#define T7 GPIO_NUM_27
#define T8 GPIO_NUM_33
#define T9 GPIO_NUM_32
//定义触摸按键的通道
#define Touch_Channel0 TOUCH_PAD_NUM0
#define Touch_Channel1 TOUCH_PAD_NUM1
#define Touch_Channel2 TOUCH_PAD_NUM2
#define Touch_Channel3 TOUCH_PAD_NUM3
#define Touch_Channel4 TOUCH_PAD_NUM4
#define Touch_Channel5 TOUCH_PAD_NUM5
#define Touch_Channel6 TOUCH_PAD_NUM6
#define Touch_Channel7 TOUCH_PAD_NUM7
#define Touch_Channel8 TOUCH_PAD_NUM8
#define Touch_Channel9 TOUCH_PAD_NUM9
4.2.3.2 定义相关的数据结构体
//定义相关的存储数据结构
gpio_num_t Touch_Gpio[10] ={T0,T1,T2,T3,T4,T5,T6,T7,T8,T9}; //引脚
touch_pad_t Touch_Channel[10] ={Touch_Channel0,Touch_Channel1,Touch_Channel2,Touch_Channel3,Touch_Channel4,Touch_Channel5,Touch_Channel6,Touch_Channel7,Touch_Channel8,Touch_Channel9}; //通道
uint16_t Threshold[10] = { 0,0,0,0,0,0,0,0,0,0 }; //用来存储阈值
4.2.3.4 触摸传感器的使能函数
/**
* @brief 开启触摸按键功能
**/
void touch_init()
{
//初始化传感器驱动程序
touch_pad_init();
}
4.2.3.3 触摸传感器的配置
/**
* @brief 配置触摸按键通道,一共T0-T9 10个通道,这里只定义了T0和T7
* @param[in] Touchx: Touch[0..9] 使用第几个触摸按键
**/
void touch_config(uint8_t Touchx)
{
touch_mode_config(Touchx); //初始化触摸按键
touch_gpio_config(Touchx); //初始化相关的gpio
touch_thresh_config(Touchx); //初始化阈值
}
相关函数
//设置触摸按键模式
static void touch_mode_config(uint8_t Touchx)
{
//01 设置检测电压范围0-2.7V
touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_0V);
//02 设置触发模式是硬件定时器触发
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
//03 载入配置,设置阈值0就是不设置中断
touch_pad_config(Touch_Channel[Touchx], 0);
//04 开启滤波器,如果不开滤波器,读取脉冲数方法不一样
touch_pad_filter_start(10);
}
//设置触摸按键相关的gpio
static void touch_gpio_config(uint8_t Touchx)
{
gpio_pad_select_gpio(Touch_Gpio[Touchx]);
gpio_set_direction(Touch_Gpio[Touchx], GPIO_MODE_OUTPUT);
}
//设置触摸按键的检测阈值
//手摸按键周期会变长,脉冲数会减少
static void touch_thresh_config(uint8_t Touchx)
{
//读取初始化没有触摸时候的脉冲数
uint16_t value;
touch_pad_read_filtered(Touch_Channel[Touchx], &value);
//这个脉冲数的2/3作为阈值
Threshold[Touchx] = value * 2 / 3;
}
4.2.3.4 按键状态监测
/**
* @brief 检测触摸按键是否被按键
* @param[in] Touchx: Touch[0..9] 使用第几个触摸按键
* @retval true:按下 false:没有按下
**/
bool touch_state(uint8_t Touchx)
{
//用轮询的方法进行检测
uint16_t value;
touch_pad_read_filtered(Touch_Channel[Touchx], &value);
if (value < Threshold[Touchx])
{
vTaskDelay(2/ portTICK_PERIOD_MS);
touch_pad_read_filtered(Touch_Channel[Touchx], &value);
if (value < Threshold[Touchx])
{
while (value < Threshold[Touchx])
{
touch_pad_read_filtered(Touch_Channel[Touchx], &value);
}
return true;
}
}
return false;
}
4.2.3.5 主函数
void setup() {
//01 初始化led
led_init();
//02 初始化触摸按键
touch_init();
touch_config(Touch0);
while (1)
{
if (touch_state(Touch0) == true)
{
led_toggle();
}
}
}
4.2.4 完整代码
4.2.4.1 bsp_touch.h
#ifndef _BSP_TOUCH_H_
#define _BSP_TOUCH_H_
#include <stdio.h>
#include "driver/gpio.h"
#include "driver/touch_pad.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
//定义触摸按键序号
#define Touch0 0
#define Touch1 1
#define Touch2 2
#define Touch3 3
#define Touch4 4
#define Touch5 5
#define Touch6 6
#define Touch7 7
#define Touch8 8
#define Touch9 9
//定义触摸按键的GPIO
#define T0 GPIO_NUM_4
#define T1 GPIO_NUM_1
#define T2 GPIO_NUM_2
#define T3 GPIO_NUM_15
#define T4 GPIO_NUM_13
#define T5 GPIO_NUM_12
#define T6 GPIO_NUM_14
#define T7 GPIO_NUM_27
#define T8 GPIO_NUM_33
#define T9 GPIO_NUM_32
//定义触摸按键的通道
#define Touch_Channel0 TOUCH_PAD_NUM0
#define Touch_Channel1 TOUCH_PAD_NUM1
#define Touch_Channel2 TOUCH_PAD_NUM2
#define Touch_Channel3 TOUCH_PAD_NUM3
#define Touch_Channel4 TOUCH_PAD_NUM4
#define Touch_Channel5 TOUCH_PAD_NUM5
#define Touch_Channel6 TOUCH_PAD_NUM6
#define Touch_Channel7 TOUCH_PAD_NUM7
#define Touch_Channel8 TOUCH_PAD_NUM8
#define Touch_Channel9 TOUCH_PAD_NUM9
//函数声明
void touch_init(); //初始化触摸按键
void touch_config(uint8_t Touchx); //配置触摸按键
bool touch_state(uint8_t Touchx); //使用轮询的方法检测按键是否被按键
#endif
4.2.4.2 bsp_touch.cpp
#include "bsp_touch.h"
//定义相关的存储数据结构
gpio_num_t Touch_Gpio[10] ={T0,T1,T2,T3,T4,T5,T6,T7,T8,T9}; //引脚
touch_pad_t Touch_Channel[10] ={Touch_Channel0,Touch_Channel1,Touch_Channel2,Touch_Channel3,Touch_Channel4,Touch_Channel5,Touch_Channel6,Touch_Channel7,Touch_Channel8,Touch_Channel9}; //通道
uint16_t Threshold[10] = { 0,0,0,0,0,0,0,0,0,0 }; //用来存储阈值
//设置触摸按键模式
static void touch_mode_config(uint8_t Touchx)
{
//01 设置检测电压范围0-2.7V
touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_0V);
//02 设置触发模式是硬件定时器触发
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
//03 载入配置,设置阈值0就是不设置中断
touch_pad_config(Touch_Channel[Touchx], 0);
//04 开启滤波器,如果不开滤波器,读取脉冲数方法不一样
touch_pad_filter_start(10);
}
//设置触摸按键相关的gpio
static void touch_gpio_config(uint8_t Touchx)
{
gpio_pad_select_gpio(Touch_Gpio[Touchx]);
gpio_set_direction(Touch_Gpio[Touchx], GPIO_MODE_OUTPUT);
}
//设置触摸按键的检测阈值
//手摸按键周期会变长,脉冲数会减少
static void touch_thresh_config(uint8_t Touchx)
{
//读取初始化没有触摸时候的脉冲数
uint16_t value;
touch_pad_read_filtered(Touch_Channel[Touchx], &value);
//这个脉冲数的2/3作为阈值
Threshold[Touchx] = value * 2 / 3;
}
/**
* @brief 开启触摸按键功能
**/
void touch_init()
{
//初始化传感器驱动程序
touch_pad_init();
}
/**
* @brief 配置触摸按键通道,一共T0-T9 10个通道,这里只定义了T0和T7
* @param[in] Touchx: Touch[0..9] 使用第几个触摸按键
**/
void touch_config(uint8_t Touchx)
{
touch_mode_config(Touchx); //初始化触摸按键
touch_gpio_config(Touchx); //初始化相关的gpio
touch_thresh_config(Touchx); //初始化阈值
}
/**
* @brief 检测触摸按键是否被按键
* @param[in] Touchx: Touch[0..9] 使用第几个触摸按键
* @retval true:按下 false:没有按下
**/
bool touch_state(uint8_t Touchx)
{
//用轮询的方法进行检测
uint16_t value;
touch_pad_read_filtered(Touch_Channel[Touchx], &value);
if (value < Threshold[Touchx])
{
vTaskDelay(2/ portTICK_PERIOD_MS);
touch_pad_read_filtered(Touch_Channel[Touchx], &value);
if (value < Threshold[Touchx])
{
while (value < Threshold[Touchx])
{
touch_pad_read_filtered(Touch_Channel[Touchx], &value);
}
return true;
}
}
return false;
}
4.2.4.4 main
#include "bsp_led.h"
#include "bsp_touch.h"
void setup() {
//01 初始化led
led_init();
//02 初始化触摸按键
touch_init();
touch_config(Touch0);
while (1)
{
if (touch_state(Touch0) == true)
{
led_toggle();
}
}
}
5.参考资料
- [1] esp32 技术参考指南:29.2 电容式触摸传感器
- [2] 触摸传感器
- [3] ESP32 触摸传感器应用方案简介