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

news/2025/1/14 22:25:16/

一、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.ppmy.cn/news/1563144.html

相关文章

20250110_ PyTorch中的张量操作

文章目录 前言1、torch.cat 函数2、索引、维度扩展和张量的广播3、切片操作3.1、 encoded_first_node3.2、probs 4、长难代码分析4.1、selected4.1.1、multinomial(1)工作原理&#xff1a; 总结 前言 1、torch.cat 函数 torch.cat 函数将两个张量拼接起来&#xff0c;具体地是…

第27章 汇编语言--- 设备驱动开发基础

汇编语言是低级编程语言的一种&#xff0c;它与特定的计算机架构紧密相关。在设备驱动开发中&#xff0c;汇编语言有时用于编写性能关键的部分或直接操作硬件&#xff0c;因为它是接近机器语言的代码&#xff0c;可以提供对硬件寄存器和指令集的直接访问。 要展开源代码详细叙…

Python单例模式的代码实现和原理

Python单例设计模式的代码实现 import threading import timeclass Singleton(object): def __new__(cls, *args, **kw):if not hasattr(cls, _instance):orig super(Singleton, cls)cls._instance orig.__new__(cls, *args, **kw)return cls._instanceclass Bus(Singleton…

数据链路层-STP

生成树协议STP&#xff08;Spanning Tree Protocol&#xff09; 它的实现目标是&#xff1a;在包含有物理环路的网络中&#xff0c;构建出一个能够连通全网各节点的树型无环逻辑拓扑。 选举根交换机&#xff1a; 选举根端口&#xff1a; 选举指定端口&#xff1a; 端口名字&…

2025软件测试面试题大全(含答案)备战“金三银四”

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 001、软件的生命周期(prdctrm) 计划阶段(planning)-〉需求分析(requirement)-〉设计阶段(design)-〉编码(coding)->测试(testing)->运行与维护(running m…

Django后端相应类设计

通用的ApiResponse类&#xff1a;用于生成统一的 API 响应格式。每个响应都包含以下字段&#xff08;每个接口最终的返回数据格式&#xff09;&#xff1a; status_code&#xff1a;HTTP 状态码&#xff08;如 200、400、500 等&#xff09;message&#xff1a;响应的描述信息…

LeetCode:39. 组合总和

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;39. 组合总和 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 cand…

EdgeOne安全专项实践:上传文件漏洞攻击详解与防范措施

靶场搭建 当我们考虑到攻击他人服务器属于违法行为时&#xff0c;我们需要思考如何更好地保护我们自己的服务器。为了测试和学习&#xff0c;我们可以搭建一个专门的靶场来模拟文件上传漏洞攻击。以下是我搭建靶场的环境和一些参考资料&#xff0c;供大家学习和参考&#xff0…