第十八章 电容式触摸传感器实验

本章将学习ESP32P4搭载的电容 式触摸传感器。利用该传感器,实现电容触摸按键功能。在本章节,通过使用电容触摸传感器通道0(IO2)来做一个简单的电容触摸按键,通过该按键控制LED0的亮灭。
本章分为如下几个小节:
18.1 电容式触摸传感器介绍
18.2 硬件设计
18.3 程序设计
18.4 下载验证

18.1 电容式触摸传感器介绍

18.1.1 触摸按键介绍

在前面按键实验中,我们已经学习过机械按键,这章节我们将学习另外一种人机交互设备:电容触摸按键。
电容式触摸感应技术已经广泛应用在家用电器、消费电子等领域,以此发展的触摸按键产品与传统按键相比下,其主要优势有:
● 无机械装置,不宜磨损老化,使用寿命长;
● 表面无缝隙,无水分、杂质渗透
● 减少元件使用,BOM成本降低
● 面板不用开孔,工业设计成本降低
● 简洁美观,设计灵活
电容触摸按键也可称为电容触摸传感器,主要由保护覆盖层、触摸电极、绝缘基板和走线组成,保护覆盖层位于最上层,绝缘基板上设有电极及走线,如下图所示。

图18.1.1.1 典型触摸传感器系统组成示意图
保护覆盖层:指的是触摸面板。触摸面板必须是绝缘材质,作用是隔离触摸电极与外部环境,起保护作用。但保护覆盖层会降低触摸的灵敏度,需要根据应用场景选择合适厚度、材质。
触摸电极:指的是触摸传感器的重要组成。手指触摸时与触摸电极形成平行板电容器,改变触摸通道的电容量。触摸电极必须是导电材质。样式多变,如PCB板上的铜箔、金属板等。
绝缘基板:对触摸电极起支撑作用,非导电材质。
走线:连接触摸电极与芯片,包括PCB走线和连接器。
触摸传感器电路可以测量触摸通道上的总电容量。电容触摸检测原理主要就是通过测量面板(传感器)和其环境之间的电容变化来检测触摸界面是否有触摸事件发生。当手指触摸传感器表面时,人体的导电性质和大质量构成接地的导电层(平行于触摸电极),该操作构成一个平行板电容器,导致电容量发生变化。当电容量发生改变且变化量超过阈值,会判定为“手指触摸动作”。手指触摸按键场景图如下所示。

图18.1.1.2 手指触摸按键图

18.1.2 ESP32电容式触摸传感器

ESP32-P4内置电容式触摸传感器,通过触摸管脚外接触摸板,构成触摸传感器系统。触摸传感器系统主要应用于人机交互场景中,检测手指触碰或接近。
ESP32-P4提供了多达14个电容式传感器GPIO,如下图所示,能够探测由手指或其他物品直接接触或接近而产生的电容差异。ESP32-P4的触摸传感器同时还支持防水和数字滤波等功能来进一步提高传感器的性能。

图18.1.2.1 触摸通道
接下来,介绍一下触摸传感器的工作原理以及工作信号。首先,来看一下触摸传感器工作原理图,如下图所示。

图18.1.2.2 触摸传感器工作原理
手指触碰或靠近触摸管脚时,管脚电容增大。触摸传感器可检测到触摸管脚上的电容变化。触摸传感器使用固定电源对触摸管脚进行充放电,电压范围为参考高值DREFH~参考低值DREFL。触摸管脚被触摸时,其电容增大,因此充放电时间延长。通过测量触摸管脚在固定周期内充放电所需时间,可判断管脚是否被触碰。
每次电压变化,触摸传感器生成一个脉冲。一个OUT信号包含若干个脉冲。一次测量操作包括:对触摸管脚进行N次(固定次数,可配置)的充放电,然后测量OUT信号生成N个脉冲所需时间。
接下来,通过查看触摸传感器的内容结构,更好了解一下其原理,如下图所示。

图18.1.2.3 触摸传感器内部结构
对触摸传感器的使用,主要就是对FSM进行配置。
FSM设备会释放标准的三角波给引脚电容,相当于给电容进行周期性充放电。FSM控制开始充放电,同时FSM内部的脉冲计数器和时间计数器启动,测量完成N次充放电的任务需要多少us时间。充放电要让电压升到指定阈值才算测量成功,而人手触摸引脚会导致电容增大,充电时间边长。一次测量任务持续时间应小于1ms。
读数的返回结果就是时间值,即耗时多长完成N次充放电。
因此,读出来的raw value越大反映了充电时间越长,人手接触面积越大,读数值越大。官方的其他API给我们配置的就是各种能影响采样时长的参数,比如电压上下限(电容充到多高的电压(DREFH)算是完成充电,放到多低的电压(DREFL)算是完成放电)、斜率控制(充电电流的大小(slope)也能影响采样时间)、采样次数(charge_discharge_times)。

18.2 硬件设计

18.2.1 例程功能

利用ESP32-P4开发板底板的电容触摸按键(右下角白色LOGO,即TPAD),通过电容式触摸传感器对电容触摸按键进行检测,实现对LED0的控制。按下TPAD按键,控制LED0的状态的翻转。

18.2.2 硬件资源

1)LED灯
LED 0 - IO51
2)SENSOR
TOUCH_CH0 - IO2

18.2.3 原理图

本章用到电容式触摸传感器的通道0(IO2)采集TPAD信号,其连接原理图如下图所示。

图18.2.3.1 TPAD连接原理图

18.3 程序设计

18.3.1 TOUCH的IDF驱动

TOUCH外设驱动位于ESP-IDF下的components/esp_driver_touch_sens目录下。使用TOUCH功能,必须先导入以下头文件:

#include "driver/touch_sens.h"

接下来,作者将介绍一些常用的函数,这些函数的描述及其作用如下:
1,初始化触摸传感器函数touch_sensor_new_controller
该函数用于初始化触摸传感器并得到控制器句柄,其函数原型如下:

esp_err_t touch_sensor_new_controller(const touch_sensor_config_t *sens_cfg, touch_sensor_handle_t *ret_sens_handle);

函数形参:


表18.3.1.1 touch_sensor_new_controller函数形参描述
函数返回值:
ESP_OK表示触摸传感器初始化成功。
ESP_ERR_NO_MEM表示内存不足。
ESP_ERR_INVALID_ARG表示错误参数。
ESP_ERR_INVALID_STATE表示触摸传感器已在使用 。
sens_cfg为指向触摸传感器控制器配置结构体指针。接下来,介绍touch_sensor_config_t结构体中各个成员,如下代码所示:

typedef struct {
    uint32_t                        power_on_wait_us;   /* 开机等待时间 */
    float                           meas_interval_us;   /* 每个通道的测量间隔 */
    uint32_t                        max_meas_time_us;   /* 测量一个通道的最大时间 */
    touch_out_mode_t                output_mode;        /* 计数模式的二值化输出 */
    uint32_t                        sample_cfg_num;     /* 采样的样本数 */
    touch_sensor_sample_config_t    *sample_cfg;        /* 数组配置 */
} touch_sensor_config_t;

touch_sensor_config_t结构体用于配置触摸传感器的参数,以下对各个成员做的简单介绍。
1)power_on_wait_us:
通道上电之间的等待时间能够测量,保证数据的稳定性。
2)meas_interval_us:
各通道测量间隔。
3)max_meas_time_us:
测量一个通道的最大时间,如果超过此事件,将触发超时中断。
4)output_mode:
触摸通道计数模式的二值化触摸输出。
5)sample_cfg_num:
用于采样的样例配置号。
6)sample_cfg:样例配置的数组配置。其类型是touch_sensor_sample_config_t,如下所示。

typedef struct {
    uint32_t                        div_num;                /* 触摸输出脉冲分配 */
    uint32_t                        charge_times;           /* 充电盒放电次数 */
    uint8_t                         rc_filter_res;          /* RC滤波器的电阻 */
    uint8_t                         rc_filter_cap;          /* RC滤波器的电容 */
    uint8_t                         low_drv;                /* 低速触摸驱动 */
    uint8_t                         high_drv;               /* 高速触摸驱动 */
    uint8_t                         bias_volt;              /* 内部LDO电压 */
    bool                            bypass_shield_output;    /* 是否旁路屏蔽输出 */
} touch_sensor_sample_config_t;

在文件中有提供一个默认配置宏TOUCH_SENSOR_V3_DEFAULT_SAMPLE_CONFIG进行配置。在文件中有提供一个默认配置宏TOUCH_SENSOR_DEFAULT_BASIC_CONFIG进行配置。
ret_sens_handle是指向触摸传感器控制器句柄结构体的指针,由于参数非常多,且在此也不需要了解他的成员,所以不作展开,想要了解可自行搜索查看。

2,触摸传感器通道配置函数touch_sensor_new_channel
该函数用于配置触摸传感器通道,其函数原型如下:

esp_err_t touch_sensor_new_channel(touch_sensor_handle_t sens_handle, 
int chan_id,
                                   const touch_channel_config_t *chan_cfg,
                                   touch_channel_handle_t *ret_chan_handle);

函数形参:


表18.3.1.2 touch_sensor_new_channel函数形参描述
函数返回值:
ESP_OK表示配置触摸传感器通道成功。
ESP_ERR_NO_MEM表示内存不足。
ESP_ERR_INVALID_ARG表示错误参数。
ESP_ERR_INVALID_STATE表示触摸传感器控制器未使能或已经分配了该通道。
sens_handle为触摸传感器控制器句柄结构体,前面已经有说明了。
chan_id为触摸传感器通道号,可选0~13。
chan_cfg为指向触摸传感器通道配置结构体指针,touch_channel_config_t结构体其定义如下:

typedef struct {
    uint32_t                        active_thresh[TOUCH_SAMPLE_CFG_NUM];  
} touch_channel_config_t;

每个样例配置的激活阈值,当触摸通道平滑值减去基准值超过该阈值时,视为激活。
ret_chan_handle为触摸传感器通道句柄,由于参数非常多,且在此也不需要了解他的成员,所以不作展开,想要了解可自行搜索查看。
3,过滤器配置函数touch_sensor_config_filter
该函数用于配置过滤器,其函数原型如下:

esp_err_t touch_sensor_config_filter(touch_sensor_handle_t sens_handle, const touch_sensor_filter_config_t *filter_cfg);

函数形参:


表18.3.1.3 touch_sensor_config_filter函数形参描述
函数返回值:
ESP_OK表示配置过滤器成功。
ESP_ERR_INVALID_ARG表示触摸传感器控制器句柄为空。
sens_handle为触摸传感器控制器句柄结构体,前面已经有说明了。
filter_cfg为指向触摸传感器过滤器配置结构体指针,touch_sensor_filter_config_t结构体其定义如下:

typedef struct {
    struct {
        touch_benchmark_filter_mode_t   filter_mode;     /* 基准筛选模式 */
        uint32_t                        jitter_step;      /* 抖动滤波步长 */
        int                             denoise_lvl;      /* 噪声水平 */
    } benchmark;                                         /* 基准配置 */
    struct {
        touch_smooth_filter_mode_t      smooth_filter;      /* 平滑数据过滤模式 */
        uint32_t                        active_hysteresis;  /* 迟滞阈值激活 */
        uint32_t                        debounce_cnt;       /* 脱扣计数 */
    } data;                                                 /* 数据配置 */
} touch_sensor_filter_config_t;

4,注册事件回调函数touch_sensor_register_callbacks
该函数用于注册事件回调函数,其函数原型如下:

esp_err_t touch_sensor_register_callbacks(touch_sensor_handle_t sens_handle, 
const touch_event_callbacks_t *callbacks, 
void *user_ctx);

函数形参:


表18.3.1.4 touch_sensor_register函数形参描述
函数返回值:
ESP_OK表示成功。
ESP_ERR_INVALID_ARG表示参数有误。
ESP_ERR_INVALID_STATE表示触摸传感器控制器未使能。
5,使能触摸传感器控制器函数touch_sensor_enable
该函数用于使能触摸传感器控制器,其函数原型如下:

esp_err_t touch_sensor_enable(touch_sensor_handle_t sens_handle);

函数形参:


表18.3.1.5 touch_sensor_enable函数形参描述
函数返回值:
ESP_OK表示成功。
ESP_ERR_INVALID_ARG表示参数有误。
ESP_ERR_INVALID_STATE表示触摸传感器控制器已在使用。
6,启动触摸通道连续扫描函数touch_sensor_start_continuous_scanning
该函数用于对所有已注册的触摸通道进行连续扫描,其函数原型如下:

esp_err_t touch_sensor_start_continuous_scanning(
touch_sensor_handle_t sens_handle);

函数形参:


表18.3.1.6 touch_sensor_start_continuous_scanning函数形参描述
函数返回值:
ESP_OK表示成功。
ESP_ERR_INVALID_ARG表示参数有误。
ESP_ERR_INVALID_STATE表示触摸传感器控制器未使能或已在扫描。
7,触摸通道读数函数touch_channel_read_data
该函数用于读取每个通道不同种类的数据,其函数原型如下:

esp_err_t touch_channel_read_data( touch_channel_handle_t chan_handle, 
touch_chan_data_type_t type, uint32_t *data);

函数形参:


表18.3.1.7 touch_channel_read_data函数形参描述
函数返回值:
ESP_OK表示成功。
ESP_ERR_INVALID_ARG表示参数有误。
channel_handle为触摸传感器通道句柄结构体,前面已经有说明了。
type为读取的数据类型,其数据类型为touch_chan_data_type_t,它是枚举类型,有三个选项:TOUCH_CHAN_DATA_TYPE_SMOOTH、TOUCH_CHAN_DATA_TYPE_BENCHMARK和TOUCH_CHAN_DATA_TYPE_PROXIMITY。

8,启动一次触摸通道扫描函数touch_sensor_trigger_oneshot_scanning
该函数用于启用一次对所有已注册的触摸通道进行扫描,其函数原型如下:

esp_err_t touch_sensor_trigger_oneshot_scanning(touch_sensor_handle_t sens_handle, int timeout_ms);

函数形参:


表18.3.1.8 touch_channel_trigger_onshot_scanning函数形参描述
函数返回值:
ESP_OK表示成功。
ESP_ERR_TIMEOUT表示超时完成一次扫描。
ESP_ERR_INVALID_ARG表示参数有误。
ESP_ERR_INVALID_STATE表示触摸传感器控制器未使能或已在扫描。

18.3.2 程序流程图


图18.3.2.1 电容触摸按键实验程序流程图

18.3.3 程序解析

在08_cap_touchkey例程中,在08_cap_touchkey\components\BSP路径下新建TOUCH_SENS文件夹,并且需要更改CMakeLists.txt内容,以便在其他文件上调用。
1.TOUCH_SENS驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。TOUCH_SENS驱动源码包括两个文件:touch_sens.c和touch_sens.h。
下面先解析touch_sens.h的程序。对触摸传感器通道、扫描次数以及活动阈值做了相关定义。

#define TOUCH_CHANNEL                   0       /* TOUCH_CHANNEL0 */
#define TOUCH_CHAN_INIT_SCAN_TIMES      3       /* 扫描次数 */
#define TOUCH_ACTIVE_THRESH             1000    /* 活动阈值 */

我们选择使用触摸传感器通道0,扫描次数为3,而活动阈值为1000。
下面我们再解析touch_sens.c的程序,看一下初始化函数touch_sens_init,代码如下:


/**
 * @brief        触摸传感器初始化
 * @param         无
 * @retval         ESP_OK:表示触摸初始化成功
 */
esp_err_t touch_sens_init(void)
{
    /* 采样配置 */
touch_sensor_sample_config_t sample_cfg = 
TOUCH_SENSOR_V3_DEFAULT_SAMPLE_CONFIG(1, 1, 1);   

    /* 控制器配置 */
touch_sensor_config_t sens_cfg = 
TOUCH_SENSOR_DEFAULT_BASIC_CONFIG(1, &sample_cfg); 
    ESP_ERROR_CHECK(touch_sensor_new_controller(&sens_cfg, &s_sens_handle)); 

    /* 滤波器配置 */
touch_sensor_filter_config_t filter_cfg = /* 使用默认过滤器配置提高数据稳定性 */
TOUCH_SENSOR_DEFAULT_FILTER_CONFIG();     
    /* 配置基线值和读数值的滤波策略和更新方式 */
    ESP_ERROR_CHECK(touch_sensor_config_filter(s_sens_handle, &filter_cfg)); 

    /* 触摸传感器通道配置 */
    touch_channel_config_t chan_cfg = {
        .active_thresh = {
            TOUCH_ACTIVE_THRESH,     /* 触摸数值 - 基准值 > 1000,则表示已触摸 */
        },
    };

    ESP_ERROR_CHECK(touch_sensor_new_channel(s_sens_handle, TOUCH_CHANNEL, &chan_cfg, &s_chan_handle));         /* 新建触摸通道 */

    /* 触摸校准 */
    touch_sens_calibration();

    /* 注册触摸传感器回调,这里只取' active '和' deactivate '事件为例 */
    touch_event_callbacks_t callbacks = {
        .on_active              = touch_on_active_callback,     /* 触摸事件回调 */
        .on_inactive            = touch_on_inactive_callback,    /* 释放事件回调 */
        .on_measure_done        = NULL,       /* 测量完成事件回调 */
        .on_scan_done           = NULL,       /* 扫描完成事件回调 */
        .on_timeout             = NULL,       /* 超时事件回调 */
        .on_proximity_meas_done = NULL,       /* 触摸传感器对接近感测完成事件回调 */
    };
ESP_ERROR_CHECK(touch_sensor_register_callbacks(s_sens_handle, &callbacks, 
NULL));      /* 注册事件回调 */

    /* 使能触摸通道 */
    ESP_ERROR_CHECK(touch_sensor_enable(s_sens_handle));

    /* 启动连续扫描 */
    ESP_ERROR_CHECK(touch_sensor_start_continuous_scanning(s_sens_handle));

    return ESP_OK;
}

在触摸传感器初始化函数中,首先通过touch_sensor_new_controller函数使用默认配置创建触摸传感器控制器句柄,然后通过touch_sensor_config_filter函数使用默认配置对过滤器进行设置,后面再通过调用touch_sensor_new_channel函数对触摸通道进行配置。接下来就是触摸校准,注册触摸传感器回调函数以及使能触摸通道。
接下来,介绍一下触摸扫描函数touch_sens_scan,代码如下:

/**
 * @brief          触摸扫描
 * @param        无
 * @retval      true:表示按下;false:表示未按下或已释放
 */
bool touch_sens_scan(void)
{
    bool touch_sens_state = false;  /* 按键状态:未按下 */
    static uint8_t key_up = 1;      /* 不支持连续按 */
    
    /* 读取基准数值 */
ESP_ERROR_CHECK(touch_channel_read_data(s_chan_handle,
                TOUCH_CHAN_DATA_TYPE_BENCHMARK, &benchmark));
    /* 读取触摸数值 */
ESP_ERROR_CHECK(touch_channel_read_data(s_chan_handle, 
TOUCH_CHAN_DATA_TYPE_SMOOTH, &chan_data));

    /* 判断触摸数值 - 基准数值 > 激活阈值 */
if (((chan_data - benchmark) > TOUCH_ACTIVE_THRESH) && 
(touch_cb_state == 0xAA) && key_up)
    {
        touch_sens_state = true;        /* 触摸按键被按下 */
        key_up = 0;
    }
    else if (touch_cb_state == 0x00)    /* 触摸按键已经释放 */
    {
        key_up = 1;
        touch_sens_state = false;
        touch_cb_state = 0xFF;
    }

    return touch_sens_state;
}

在触摸扫描函数中,通过调用touch_channel_read_data函数读取基准数值和触摸数值,后面就判断是否被按下。touch_cb_state变量的值是在回调函数中改变的,0xAA表示在触摸,而0x00表示释放触摸。当按下触摸按键时,该函数返回的是true;当未按下或已经松手触摸按键时,该函数返回的是false。

文件中的其他函数请大家自行查看源码,都有详细的注释。

2.CMakeLists.txt文件
本例程功能实现主要依靠TOUCH_SENS驱动。要在main函数中,成功调用TOUCH_SENS文件中的内容,就得需要修改BSP文件夹下的CMakeLists.txt文件,修改如下:

set(src_dirs
               LED
               TOUCH_SENS)

set(include_dirs
              LED
TOUCH_SENS)

set(requires
               driver
esp_driver_touch_sens)

idf_component_register(    SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})

component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)

3.main.c驱动代码
在main.c里面编写如下代码。

void app_main(void)
{
    esp_err_t ret;
    bool capkey = false;
    
    ret = nvs_flash_init();     /* 初始化NVS */
    if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ESP_ERROR_CHECK(nvs_flash_init());
    }

    led_init();                 /* LED初始化 */
    touch_sens_init();          /* TOUCH_SENS初始化 */

    while (1)
    {
        capkey = touch_sens_scan();
        
        if (capkey)
        {
            LED0_TOGGLE();
        }

        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

在app_main函数中,调用touch_sens_init函数对触摸传感器初始化后,便在循环中,每隔10毫秒就调用touch_sens_scan函数扫描触摸按键状态,如果被按下,翻转LED0的状态。

18.4 下载验证

程序下载完成后,按下电容触摸按键控制LED0灯的状态。

标签: none

添加新评论