当前位置: 首页 > article >正文

正点原子STM32F103战舰版电容触摸键学习

一、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

二、tpad.c代码

 1.全局变量和初始化

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


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

2.tpad_init函数

/**
 * @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;
    
    tpad_timx_cap_init(TPAD_ARR_MAX_VAL, psc - 1);  /* 以72 / (psc - 1) Mhz的频率计数 */

    for (i = 0; i < 10; i++)        /* 连续读取10次 */
    {
        buf[i] = tpad_get_val();
        delay_ms(10);
    }

    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;
            }
        }
    }

    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);

    if (g_tpad_default_val > TPAD_ARR_MAX_VAL / 2)
    {
        return 1;                   /* 初始化遇到超过TPAD_ARR_MAX_VAL/2的数值,不正常! */
    }

    return 0;
}
  • 功能:初始化触摸按键。首先配置定时器,然后读取触摸传感器的值多次(10次),并计算中间6个值的平均值,作为基线值。
  • 排序:对读取的值进行排序,以去除异常值(最小和最大值)。
  • 返回值:返回 0 表示初始化成功,返回 1 表示初始化失败(基线值异常)。

 3.tpad_reset函数

/**
 * @brief       复位TPAD
 *   @note      我们将TPAD按键看做是一个电容, 当手指按下/不按下时容值有变化
 *              该函数将GPIO设置成推挽输出, 然后输出0, 进行放电, 然后再设置
 *              GPIO为浮空输入, 等待外部大电阻慢慢充电
 * @param       无
 * @retval      无
 */
static void tpad_reset(void)
{
    GPIO_InitTypeDef gpio_init_struct;

    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);   /* TPAD引脚推挽输出 */

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

    delay_ms(5);

    g_timx_cap_chy_handle.Instance->SR = 0;             /* 清除标记 */
    g_timx_cap_chy_handle.Instance->CNT = 0;            /* 归零 */

    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引脚浮空输入 */
}
  • 功能:重置触摸传感器。将 GPIO 设置为推挽输出并输出低电平以放电,然后将 GPIO 设置为浮空输入以等待外部电阻充电。
  • 延迟:通过延迟确保放电过程完成后再进行输入捕获。 

放电原理po一张 战舰开发指南里面的内容:

 4.tpad_get_val函数

/**
 * @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();
    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;  /* 返回捕获/比较值 */
}
  • 流程
    • 首先调用 tpad_reset函数重置触摸传感器的状态。
    • 通过检查定时器标志位来等待捕获上升沿。如果在设定的超时时间内没有捕获到上升沿,则返回当前计数器的值。
    • 如果成功捕获到上升沿,则返回捕获寄存器的值。

5.tpad_get_maxval函数

/**
 * @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;
}
  • 流程
    • 通过循环调用 tpad_get_val() 函数 n 次,每次获取一个值,并与当前最大值进行比较。
    • 返回所有读取值中的最大值。

 6.tpad_scan函数

/**
 * @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;
}
  • 流程
    • 根据模式设置采样次数(支持连续触发时为6次)。
    • 调用 tpad_get_maxval() 获取最大值。
    • 如果最大值大于基线值加上阈值(TPAD_GATE_VAL),则判断为按键有效。
    • 使用 keyen 变量来控制按键的有效性,防止重复触发。

 

 7.tpad_timx_cap_init函数

/**
 * @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);                             /* 使能输入捕获和定时器 */
}
  • 流程
    • 启用 GPIO 和定时器的时钟。
    • 初始化 GPIO 为下拉输入模式。(确保该引脚在无触摸状态下稳定地处于逻辑低电平(0),从而提高触摸传感器的可靠性和准确性
    • 配置定时器的分频、计数模式和自动重装载值。
    • 配置输入捕获的通道,设置为上升沿捕获。
    • 启动定时器的输入捕获功能。

 原理理解

 

解释 Tcs 和 Tcx 的功能

  1. Tcs (充电时间常数)

    • Tcs 表示在触摸没有发生时,电容 CsCs 的充电过程中的时间常数。它与触摸传感器的静态基线值相对应,类似于 g_tpad_default_val
    • 当没有触摸时,传感器的电容会在一定时间内充电到一个稳定状态, Tcs 就是这个稳定状态下的电容值。
  2. Tcx (触摸响应时间常数)

    • Tcx 代表在触摸发生时,电容 CsCs 充电所需的时间,也可视为系统对触摸响应的阈值。它类似于 TPAD_GATE_VAL
    • 当手指接触传感器时,电容 $ Cs $ 的电压变化会改变,传感器会快速充电或放电至新电平,Tcx就是监测这种变化的关键阈值。

http://www.kler.cn/a/502286.html

相关文章:

  • Blazor中Syncfusion Word组件使用方法
  • Gitlab-Runner配置
  • 数据结构与算法之二叉树: LeetCode 543. 二叉树的直径 (Ts版)
  • 在 Flownex 网络中创建传热元件
  • 小目标检测难点分析和解决策略
  • _STM32关于CPU超频的参考_HAL
  • LabVIEW光流算法的应用
  • HTML 闪烁动画(Blink Animation)
  • 前端练习题
  • MACPA:fMRI连接性分析的新工具
  • 第 5 场 算法季度赛
  • 51c大模型~合集104
  • 计算机网络之---网络层的基本功能
  • 使用python调用JIRA6 REST API及遇到的问题
  • 自然语言处理之jieba分词和TF-IDF分析
  • Linux 高级路由 —— 筑梦之路
  • 链条缺陷检测数据集VOC+YOLO格式1422张7类别
  • [Android]service命令的使用
  • 【微服务】面试 3、 服务监控 SkyWalking
  • 景联文科技提供高质量多模态数据处理服务,驱动AI新时代
  • 经典软件测试面试题大全(含答案+文档)
  • Redis 的大 Key 对持久化有什么影响
  • AI学习路线图-提示词工程-吴恩达
  • 爬虫程序如何存储数据到数据库?
  • 【Vim Masterclass 笔记11】S06L24 + L25:Vim 文本的插入、变更、替换与连接操作同步练习(含点评课)
  • 计算机网络—地址与子网(IPv4)相关知识总结