【正点原子STM32】电容触摸按键实验(电容触摸按键原理、电容值C跟时间值t成正比关系、利用定时器输入捕获功能计算充电时间、检测电容触摸按键过程、tpad关键函数、tpad_scan函数 点按和连按)

一、电容触摸按键原理介绍
二、检测电容触摸按键过程
三、编程实战
四、总结

一、电容触摸按键原理介绍

在这里插入图片描述
电容触摸按键是一种常见的电子开关,它通过检测人体的电容变化来实现按键操作。其原理基于电容的变化,具体介绍如下:

  1. 电容感应原理:电容触摸按键利用人体和地面之间的电容来检测触摸。当人体靠近电容触摸区域时,人体与地面之间的电容会发生变化,因为人体是导电的,会对电场产生影响。这种电容变化可以被电路感知到。

  2. 电容传感器:电容触摸按键通常使用的是电容传感器来检测电容的变化。传感器通常由一对电极构成,一个是发送电极,负责发送电场,另一个是接收电极,负责接收电场。当有人触摸时,人体作为第三电极会改变电场,从而改变接收电极的电容值。

  3. 检测电路:检测电路通常由电容传感器、信号处理电路和控制器组成。电容传感器用来检测电容变化,信号处理电路用来放大和处理传感器输出的信号,控制器用来解读处理后的信号并执行相应的操作,比如开关灯、调节音量等。

  4. 工作原理:当有人触摸电容触摸按键时,电容传感器感知到电容变化,并将信号传送给信号处理电路。信号处理电路处理后的信号会传送给控制器,控制器根据接收到的信号执行相应的操作。例如,如果是控制灯光的开关,触摸按键后控制器会接收到信号并执行打开或关闭灯光的指令。

  5. 优点:电容触摸按键相比传统的机械按键具有一些优点,例如无接触、易清洁、寿命长、外观美观等,因此在许多应用中得到了广泛应用。

总的来说,电容触摸按键利用人体电容变化来实现按键操作,通过电容传感器和相应的电路进行信号检测和处理,从而实现各种应用场景下的控制操作。
在这里插入图片描述
两种情况下的充电过程:

  1. 无手指触摸情况

    • 当没有手指触摸时,只有电容Cs参与充电。
    • 上电时,电容Cs开始充电,受到电阻的作用,直到充满电容为止。
    • 这个充电时间称为Tcs。
  2. 有手指触摸情况

    • 当有手指触摸时,电容Cs和Cx两个电容都参与充电。
    • 上电时,电容Cs和Cx同时开始充电,受到电阻的作用,直到两者都充满为止。
    • 由于Cx的存在,使得充电时间相比于无手指触摸情况会变长。
    • 这个充电时间称为Tcx。

在有手指触摸的情况下,因为额外的电容Cx也在充电,所以相比无手指触摸的情况,充电时间会相对延长,即Tcx > Tcs。这是由于手指的导电性使得整个电容系统的等效电容增大,需要更长的时间来充满。
在这里插入图片描述
在这里插入图片描述
RC电路充放电的情况,其中包含了两种情况的表达方式,具体如下:

  1. 一般情况下的公式
    V t = V 0 + ( V 1 − V 0 ) × ( e − t R C ) V_t = V_0 + (V_1 - V_0) \times \left( e^{-\frac{t}{RC}} \right) Vt=V0+(V1V0)×(eRCt)
    这个公式描述了电容从初始电压 V 0 V_0 V0 到最终电压 V 1 V_1 V1 的充电或放电过程中,在任意时间 t t t 时刻的电压 V t V_t Vt。其中 R C RC RC 是电路的时间常数。

  2. 简化情况下的公式
    当初始电压 V 0 V_0 V0 为0时,即从0V开始充电,可以简化为:
    V t = V 1 × ( e − t R C ) V_t = V_1 \times \left( e^{-\frac{t}{RC}} \right) Vt=V1×(eRCt)
    这个公式描述了电容从0V开始充电到最终电压 V 1 V_1 V1 的过程中,在任意时间 t t t 时刻的电压 V t V_t Vt

在这里插入图片描述

电容值C跟时间值t成正比关系,电容越大,充电到达某个临界值时间越长

在这种情况下,假设电容值 C C C 与时间 t t t 成正比关系,即电容 C C C 越大,充电到达某个临界值的时间 t t t 越长。那么在给定的条件下,可以利用充放电公式来描述电容的充电过程。

考虑到您提到了两种电容 C s Cs Cs C x Cx Cx,以及外接的充放电电阻 R R R,我们可以采用以下步骤:

  1. 计算等效电容值:将 C s Cs Cs C x Cx Cx 并联得到一个等效电容 C e q C_{eq} Ceq,即 C e q = C s + C x C_{eq} = Cs + Cx Ceq=Cs+Cx

  2. 确定临界电压值 V 1 V_1 V1:在STM32的输入端口为高电平时,电容充电到的临界电压值应该大于或等于STM32认为为高电平的最低电压值 V t h V_{th} Vth

  3. 确定时间常数 R C RC RC:根据给定的外接电容充放电电阻 R R R 和等效电容 C e q C_{eq} Ceq,计算得到时间常数 R C RC RC

  4. 使用充放电公式计算充电时间 t t t:根据简化情况下的公式,即
    V t = V 1 × ( e − t R C ) V_t = V_1 \times \left( e^{-\frac{t}{RC}} \right) Vt=V1×(eRCt)
    根据已知的 V 1 V_1 V1,计算在给定时间 t t t 下电容的电压 V t V_t Vt,直到 V t V_t Vt 达到或超过 V t h V_{th} Vth 时停止充电,此时的时间 t t t 即为充电到达临界值的时间。

  5. 控制电容充电开关:利用STM32的IO口控制电容的充放电开关,当充电时间达到临界值时关闭充电开关,停止充电。

综上所述,根据您提供的条件,可以利用电容充放电公式和已知参数来计算电容充电到达临界值所需的时间,并通过STM32的IO口控制充电开关,实现对电容的精确充电。

可以利用定时器输入捕获功能计算充电时间

可以利用定时器的输入捕获功能来实现充电时间的测量,进而检测是否有手指触摸。以下是大致的实现步骤:

  1. 初始化:首先,您需要初始化定时器和IO口。定时器配置为输入捕获模式,用于测量充电时间;IO口连接到电容的充电开关,用于控制电容的充放电。

  2. 放电:在开始之前,确保电容处于放电状态,即将充电开关关闭,电容开始放电,保持初始状态为0。

  3. 测量基准时间 T c s T_{cs} Tcs:启动定时器,开始测量充电时间 T c s T_{cs} Tcs。当电容充满并达到阈值 V t h V_{th} Vth 时,停止定时器并记录时间 T c s T_{cs} Tcs,作为无触摸时的基准时间。

  4. 循环测量充电时间 T T T:在一个定时循环中,启动定时器,并等待电容开始充电。当电容充满并达到阈值 V t h V_{th} Vth 时,停止定时器并记录时间 T T T

  5. 比较 T T T T c s T_{cs} Tcs:将测量得到的充电时间 T T T 与基准时间 T c s T_{cs} Tcs 进行比较。如果 T T T 超过一定阈值(例如 T c s T_{cs} Tcs 的某个倍数),则判断为有手指触摸。

  6. 反复执行:将步骤 4 和 5 放入一个循环中,以持续监测电容的充电时间,并根据需要进行触摸检测。

  7. 实时响应:根据触摸检测结果,可以实时进行相应的操作,比如触发事件、改变系统状态等。

通过这种方法,您可以利用定时器输入捕获功能来实现对电容充电时间的测量,并据此检测是否有手指触摸。

二、检测电容触摸按键过程

在这里插入图片描述
常见的电容触摸按键检测流程。以下是对每个步骤的进一步解释:

  1. 电容放电:TPAD引脚被设置为推挽输出,并输出低电平,这会导致电容开始放电到地。这样做是为了确保电容处于初始放电状态,以便开始新的充电周期。

  2. 电容充电:TPAD引脚被设置为浮空输入,使得电容开始充电。此时电容会通过外部电阻开始充电,充电电流会导致电压在电容上逐渐增加。

  3. 开启输入捕获功能:在电容开始充电的同时,开启TPAD引脚的输入捕获功能。这样,当电容充电到一定电压(达到阈值 V t h V_{th} Vth )时,可以检测到TPAD引脚上升沿的触发。

  4. 等待触发上升沿:充电过程中,持续监测TPAD引脚的状态,直到检测到上升沿触发。这表示电容充电已经达到了预设的阈值 V t h V_{th} Vth ,即触摸按键被触发。

  5. 计算充电时间:通过定时器捕获或比较寄存器获取充电时间,即从TPAD引脚设置为浮空输入开始到检测到上升沿触发的时间间隔。这个时间间隔 T T T 就是电容充电所需的时间。

  6. 判断触摸按键状态:通过比较充电时间 T T T 与预设的基准时间 T 1 T1 T1 ,如果 T − T 1 T - T1 TT1 大于某个阈值,就可以判断为触摸按键被按下。因为电容的大小会影响充电时间,所以当电容增大时,充电时间 T T T 会相应增加。

这种方法通过测量电容充电时间来判断触摸按键是否被按下,利用了电容大小对充电时间的影响,实现了简单而有效的触摸检测。
在这里插入图片描述

三、编程实战

在这里插入图片描述
这些关键函数的功能描述:

  1. tpad_reset函数

    • 功能:复位TPAD。
    • 实现:将TPAD引脚设置为推挽输出,输出低电平,实现电容放电到地,并清空定时器计数器CNT值,为下一次充电计时做准备。
  2. tpad_get_val函数

    • 功能:获取一次捕获值。
    • 实现:复位TPAD,等待捕获上升沿得到充电时间,即从TPAD引脚设置为浮空输入开始到检测到上升沿触发的时间间隔。
  3. tpad_get_maxval函数

    • 功能:多次调用tpad_get_val函数获取充电时间,并获取最大的值。
    • 实现:通过多次调用tpad_get_val函数获取多个充电时间值,然后从中选取最大的一个作为最终的充电时间值,以减少可能存在的测量误差。
  4. tpad_init函数

    • 功能:初始化TPAD。
    • 实现:调用tpad_get_val函数获取电容触摸按键没有按下的默认充电时间值,用作后续判断的基准时间。
  5. tpad_scan函数

    • 功能:扫描TPAD。
    • 实现:调用tpad_get_maxval函数获取多次充电中最大充电时间,然后与基准时间进行比较,以判断是否有手指触摸按下。
  6. tpad_timx_cap_init函数

    • 功能:输入捕获通道初始化。
    • 实现:对定时器的输入捕获通道进行初始化,使其能够捕获TPAD引脚上的上升沿触发事件,从而获取充电时间。

这些函数共同实现了对电容触摸按键的扫描和检测功能,通过测量电容充电时间来判断是否有手指触摸按下。
在这里插入图片描述
以下是对这些函数的功能进一步解释:

  1. tpad_init函数

    • 功能:初始化TPAD。
    • 实现:首先调用tpad_timx_cap_init函数初始化输入捕获功能。然后进行10次调用tpad_get_val(),获取电容触摸按键没有按下的默认充电时间值。在这10次调用中,会重复执行tpad_get_val()函数,其中tpad_get_val()函数内部会调用tpad_reset函数来获取电容充电时间,并从中选取中间6次的平均值,复制给tpad_default_val变量,作为基准时间。
  2. tpad_get_val函数

    • 功能:获取一次充电时间。
    • 实现:调用tpad_reset函数进行电容的放电和初始化操作,然后等待捕获上升沿事件(通过轮询或中断方式)。当捕获到上升沿时,返回捕获寄存器的值,即电容充电所需的时间。
  3. tpad_reset函数

    • 功能:复位TPAD。
    • 实现:初始化PA1为推挽输出,输出低电平,实现电容放电到地;然后计数器的值初始化为0;接着将PA1初始化为浮空输入,等待电容充电。在等待捕获上升沿的过程中,当捕获到上升沿时,返回捕获寄存器的值,即电容充电所需的时间。在10次调用tpad_get_val()中,选取中间6次的充电时间值进行平均,并赋值给tpad_default_val变量作为基准时间。
  4. tpad_scan函数

    • 功能:扫描TPAD。
    • 实现:调用tpad_get_maxval函数获取多次充电的平均值,然后将这个平均值与tpad_default_val + TPAD_GATE_VAL进行比较。如果大于这个值,则说明有手指按下触摸按键,支持连按和不连按。

这些函数共同实现了电容触摸按键的初始化、扫描和检测功能,通过测量电容充电时间来判断是否有手指触摸按下,并支持连按和不连按的检测。

tpad.c

#include "./BSP/TPAD/tpad.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"

/******************************************************************************************/
/* 空载的时候(没有手按下),计数器需要的时间
 * 这个值应该在每次开机的时候被初始化一次
 */
volatile uint16_t g_tpad_default_val = 0;       /* 空载的时候(没有手按下),计数器需要的时间 */


/* 定时器输入边沿捕获 */
static TIM_HandleTypeDef g_timx_cap_chy_handle; /* 定时器x句柄 */


/**
 * @brief       初始化触摸按键
 * @param       psc     : 分频系数(值越小, 越灵敏, 最小值为: 1)
 * @retval      0, 初始化成功; 1, 初始化失败;
 */
uint8_t tpad_init(uint16_t psc)
{
    uint16_t buf[10];   // 用于存储多次读取的值
    uint16_t temp;      // 临时变量
    uint8_t j, i;       // 循环变量
    
    /* 1、初始化定时器输入捕获 */
    tpad_timx_cap_init(TPAD_ARR_MAX_VAL, psc - 1);  /* 以72 / (psc - 1) Mhz的频率计数 */
    
    /* 2、得到没有电容触摸按键按下时默认的充电时间g_tpad_default_val */
    for (i = 0; i < 10; i++)        /* 连续读取10次 */
    {
        buf[i] = tpad_get_val();    // 获取一次充电时间
        delay_ms(10);               // 延时10毫秒,等待下一次读取
    }

//    /* 方法1 */
//    for (i = 0; i < 9; i++)
//    {
//        for (j = i + 1; j < 10; j++)
//        {   
//            if (buf[i] > buf[j])    /* 升序排列 */
//            {
//                temp = buf[i];
//                buf[i] = buf[j];
//                buf[j] = temp;
//            }
//        }
//    }

    /* 对读取的值进行排序,选取中间6次的值进行平均,作为基准时间 */
    /* 方法2(经典冒泡算法) */
    for (i = 0; i < 9; i++)             
    {
        for (j = 0; j < 9 - i; j++)
        {   
            if (buf[j] > buf[j + 1])
            {
                temp = buf[j];
                buf[j] = buf[j + 1];
                buf[j + 1] = temp;
            }
        }
    }
    
    temp = 0;
    for (i = 2; i < 8; i++)             // 取中间的6个数据进行平均
    {
        temp += buf[i];
    }
    
    g_tpad_default_val = temp / 6;      // 计算平均值并赋值给基准时间
    printf("g_tpad_default_val:%d\r\n", g_tpad_default_val);            /* 输出基准时间 */
    printf("charging time:%d\r\n", g_tpad_default_val / (psc - 1));     /* 输出充电时间 */

    if (g_tpad_default_val > TPAD_ARR_MAX_VAL / 2)    // 如果基准时间大于计数器最大值的一半,返回错误
    {
        return 1;   /* 初始化遇到超过TPAD_ARR_MAX_VAL/2的数值,不正常! */
    }

    return 0;
}

/**
 * @brief       复位TPAD
 *   @note      我们将TPAD按键看做是一个电容, 当手指按下/不按下时容值有变化
 *              该函数将GPIO设置成推挽输出, 然后输出0, 进行放电, 然后再设置
 *              GPIO为浮空输入, 等待外部大电阻慢慢充电
 * @param       无
 * @retval      无
 */
static void tpad_reset(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    /* 设置GPIO为推挽输出,输出低电平,实现电容放电到地 */
    gpio_init_struct.Pin = TPAD_GPIO_PIN;                               /* 输入捕获的GPIO口 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;                        /* 复用推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                                /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;                    /* 中速 */
    HAL_GPIO_Init(TPAD_GPIO_PORT, &gpio_init_struct);

    HAL_GPIO_WritePin(TPAD_GPIO_PORT, TPAD_GPIO_PIN, GPIO_PIN_RESET);   /* TPAD引脚输出0, 放电 */

    delay_ms(5);    // 延时5毫秒

    /* 清空定时器计数器CNT值 */
    g_timx_cap_chy_handle.Instance->SR = 0;                             /* 清除标记 */
    g_timx_cap_chy_handle.Instance->CNT = 0;                            /* 归零 */

    /* 设置GPIO为浮空输入,等待电容充电 */
    gpio_init_struct.Pin = TPAD_GPIO_PIN;                               /* 输入捕获的GPIO口 */
    gpio_init_struct.Mode = GPIO_MODE_INPUT;                            /* 复用推挽输出 */
    gpio_init_struct.Pull = GPIO_NOPULL;                                /* 浮空 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;                    /* 中速 */
    HAL_GPIO_Init(TPAD_GPIO_PORT, &gpio_init_struct);                   /* TPAD引脚浮空输入 */
}

/**
 * @brief       得到定时器捕获值
 *   @note      如果超时, 则直接返回定时器的计数值
 *              我们定义超时时间为: TPAD_ARR_MAX_VAL - 500
 * @param       无
 * @retval      捕获值/计数值(超时的情况下返回)
 */
static uint16_t tpad_get_val(void)
{
    uint32_t flag = (TPAD_TIMX_CAP_CHY == TIM_CHANNEL_1) ? TIM_FLAG_CC1 :\
                    (TPAD_TIMX_CAP_CHY == TIM_CHANNEL_2) ? TIM_FLAG_CC2 :\
                    (TPAD_TIMX_CAP_CHY == TIM_CHANNEL_3) ? TIM_FLAG_CC3 : TIM_FLAG_CC4;
    
    tpad_reset();   /* 复位TPAD */
    
    while (__HAL_TIM_GET_FLAG(&g_timx_cap_chy_handle, flag) == RESET)   /* 等待通道CHY捕获上升沿 */
    {
        if (g_timx_cap_chy_handle.Instance->CNT > TPAD_ARR_MAX_VAL - 500)
        {
            return g_timx_cap_chy_handle.Instance->CNT;                 /* 超时了,直接返回CNT的值 */
        }
    }

    return TPAD_TIMX_CAP_CHY_CCRX;      /* 返回捕获/比较值 */
}

/**
 * @brief       读取n次, 取最大值
 * @param       n       :连续获取的次数
 * @retval      n次读数里面读到的最大读数值
 */
static uint16_t tpad_get_maxval(uint8_t n)
{
    uint16_t temp = 0;
    uint16_t maxval = 0;

    while (n--)
    {
        temp = tpad_get_val();  /* 得到一次值 */

        if (temp > maxval)
        {
            maxval = temp;
        }
    }

    return maxval;
}

/**
 * @brief       扫描触摸按键
 * @param       mode    :扫描模式
 *   @arg       0, 不支持连续触发(按下一次必须松开才能按下一次);
 *   @arg       1, 支持连续触发(可以一直按下)
 * @retval      0, 没有按下; 1, 有按下;
 */
uint8_t tpad_scan(uint8_t mode)
{
    static uint8_t keyen = 0;   /* 0, 可以开始检测;  >0, 还不能开始检测; */
    uint8_t res = 0;
    uint8_t sample = 3;         /* 默认采样次数为3次 */
    uint16_t rval;

    if (mode)
    {
        sample = 6;     /* 支持连按的时候,设置采样次数为6次 */
        keyen = 0;      /* 支持连按, 每次调用该函数都可以检测 */
    }

    rval = tpad_get_maxval(sample);
    
    if (rval > (g_tpad_default_val + TPAD_GATE_VAL))    /* 大于tpad_default_val+TPAD_GATE_VAL,有效 */
    {
        if (keyen == 0)
        {
            res = 1;    /* keyen==0, 有效 */
        }

        //printf("r:%d\r\n", rval);   /* 输出计数值, 调试的时候才用到 */
        keyen = 3;  /* 至少要再过3次之后才能按键有效 */
    }

    if (keyen) 
    {
        keyen--;
    }
    
    return res;
}

/**
 * @brief       触摸按键输入捕获设置
 * @param       arr     :自动重装值
 * @param       psc     :时钟预分频数
 * @retval      无
 */
static void tpad_timx_cap_init(uint16_t arr, uint16_t psc)
{
    GPIO_InitTypeDef gpio_init_struct;
    TIM_IC_InitTypeDef timx_ic_cap_chy;
    
    TPAD_GPIO_CLK_ENABLE();                                             /* TPAD引脚 时钟使能 */
    TPAD_TIMX_CAP_CHY_CLK_ENABLE();                                     /* 定时器 时钟使能 */

    gpio_init_struct.Pin = TPAD_GPIO_PIN;                               /* 输入捕获的GPIO口 */
    gpio_init_struct.Mode = GPIO_MODE_INPUT;                            /* 输入 */
    gpio_init_struct.Pull = GPIO_PULLDOWN;                              /* 下拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;                    /* 中速 */
    HAL_GPIO_Init(TPAD_GPIO_PORT, &gpio_init_struct);                   /* TPAD引脚浮空输入 */

    g_timx_cap_chy_handle.Instance = TPAD_TIMX_CAP;                     /* 定时器5 */
    g_timx_cap_chy_handle.Init.Prescaler = psc;                         /* 定时器分频 */
    g_timx_cap_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;        /* 向上计数模式 */
    g_timx_cap_chy_handle.Init.Period = arr;                            /* 自动重装载值 */
    g_timx_cap_chy_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;  /* 时钟分频因子 */
    HAL_TIM_IC_Init(&g_timx_cap_chy_handle);

    timx_ic_cap_chy.ICPolarity = TIM_ICPOLARITY_RISING;                                     /* 上升沿捕获 */
    timx_ic_cap_chy.ICSelection = TIM_ICSELECTION_DIRECTTI;                                 /* 映射到TI1上 */
    timx_ic_cap_chy.ICPrescaler = TIM_ICPSC_DIV1;                                           /* 配置输入分频,不分频 */
    timx_ic_cap_chy.ICFilter = 0;                                                           /* 配置输入滤波器,不滤波 */
    HAL_TIM_IC_ConfigChannel(&g_timx_cap_chy_handle, &timx_ic_cap_chy, TPAD_TIMX_CAP_CHY);  /* 配置TIM5通道2 */
    
    HAL_TIM_IC_Start(&g_timx_cap_chy_handle, TPAD_TIMX_CAP_CHY);                            /* 使能输入捕获和定时器 */
}

tpad.h

#ifndef __TPAD_H
#define __TPAD_H

#include "./SYSTEM/sys/sys.h"


/******************************************************************************************/
/* TPAD 引脚 及 定时器 定义 */

/* 我们使用定时器的输入捕获功能, 对TPAD进行检测
 * 这里的输入捕获使用定时器TIM5_CH2, 捕获TPAD按键的输入
 * 因为我们的TPAD是连接在PA1引脚上的, PA1只能是TIM2_CH2 / TIM5_CH2
 * 所以定时器也只能在这两个里面选, 如果你自己设计的板卡, 则根据原理
 * 图进行相应的修改即可, 包括GPIO 及 对应的定时器和通道
 */
#define TPAD_GPIO_PORT                          GPIOA
#define TPAD_GPIO_PIN                           GPIO_PIN_1
#define TPAD_GPIO_CLK_ENABLE()                  do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* PA口时钟使能 */

#define TPAD_TIMX_CAP                           TIM5
#define TPAD_TIMX_CAP_CHY                       TIM_CHANNEL_2                                /* 通道Y,  1<= Y <=4 */
#define TPAD_TIMX_CAP_CHY_CCRX                  TIM5->CCR2                                   /* 通道Y的捕获/比较寄存器 */
#define TPAD_TIMX_CAP_CHY_CLK_ENABLE()          do{ __HAL_RCC_TIM5_CLK_ENABLE(); }while(0)   /* TIM5 时钟使能 */

/******************************************************************************************/

/* 触摸的门限值, 也就是必须大于 g_tpad_default_val + TPAD_GATE_VAL
 * 才认为是有效触摸, 改大 TPAD_GATE_VAL, 可以降低灵敏度, 反之, 则可以提高灵敏度
 * 根据实际需求, 选择合适的 TPAD_GATE_VAL 即可
 */
#define TPAD_GATE_VAL       100             /* 触摸的门限值, 也就是必须大于 g_tpad_default_val + TPAD_GATE_VAL, 才认为是有效触摸 */
#define TPAD_ARR_MAX_VAL    0XFFFF          /* 最大的ARR值, 一般设置为定时器的ARR最大值 */


extern volatile uint16_t g_tpad_default_val;/* 空载的时候(没有手按下),计数器需要的时间 */

/* 静态函数, 仅限 tapd.c调用 */
static void tpad_reset(void);               /* 复位 */
static uint16_t tpad_get_val(void);         /* 得到定时器捕获值 */
static uint16_t tpad_get_maxval(uint8_t n); /* 读取n次, 获取最大值 */
static void tpad_timx_cap_init(uint16_t arr, uint16_t psc); /* 定时器输入捕获初始化 */


/* 接口函数, 可以在其他.c调用 */
uint8_t tpad_init(uint16_t psc);    /* TPAD 初始化 函数 */
uint8_t tpad_scan(uint8_t mode);    /* TPAD 扫描 函数 */

#endif

main.h

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/TPAD/tpad.h"


int main(void)
{
    uint8_t t = 0;

    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    led_init();                         /* 初始化LED */
    tpad_init(6);                       /* 初始化触摸按键 */

    while (1)
    {
        if (tpad_scan(1))  /* 成功捕获到了一次上升沿(此函数执行时间至少15ms) */
        {
            LED1_TOGGLE(); /* LED1翻转 */
        }

        t++;
        if (t == 15)
        {
            t = 0;
            LED0_TOGGLE(); /* LED0翻转 */
        }

        delay_ms(10);
    }
}

uint8_t tpad_scan(uint8_t mode)函数 点按和连按

在这里插入图片描述
tpad_scan函数的工作原理以及根据不同的mode参数如何影响连续按下的行为:

函数说明:

  • tpad_scan函数用于扫描电容触摸按键是否被按下。
  • 函数的参数mode决定了是否支持连续按下的检测。
  • 返回值为1表示按键被按下,返回值为0表示未检测到按键按下。

工作原理:

  1. 如果mode0,即不支持连续按下:

    • 函数在每次调用时,会进行3次采样。
    • 如果检测到按键按下(即采样值超过了默认值加上阈值),且上一次按键检测后至少经过了3次调用(即keyen不为0),则返回按键被按下的状态,并将keyen设为3,表示需要再经过3次调用才能再次检测到按键按下。
    • 如果keyen不为0,则将其递减。
  2. 如果mode1,即支持连续按下:

    • 函数在每次调用时,会进行6次采样。
    • 其余逻辑与不支持连续按下的情况相似,但是不会有对keyen的限制,即每次调用都可以检测到按键的状态。

按键状态示例:

  • 不支持连续按下情况:

    • 手指按下:②->③->A->B->④
    • 手指未移走:②->③->B->④
    • 手指移走:②->④(两次)->②
  • 支持连续按下情况:

    • 手指按下:①->②->③->A->B->④
    • 手指未移走:①->②->③-> A->B->④
    • 手指移走:①->②

实验现象

MODE为0 点按现象

在这里插入图片描述
在这里插入图片描述
按一下就翻转一次
在这里插入图片描述

MODE为1 连按现象

在这里插入图片描述
连按LED会一直翻转
在这里插入图片描述

四、总结

在这里插入图片描述
在这里插入图片描述

  • 34
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖喱年糕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值