电容按键 其实只需要理解,手指按上去后充电时间变长,我们可以利用定时器输入捕获功能计算充电时间,超过无触摸时的充电时间一定的阈值就认为是有手指触摸。
基本原理就是这样,我们开始写代码:
其实,看过了上一章内容,就知道,我们只需要把TIM环境配置好,就相当于把HAL库搬了过来,直接使用HAL库的例程就可以了。
这是一个放电的过程,我们需要它一开始时是没有电的
static void TPAD_Reset(void)
{GPIO_InitTypeDef GPIO_InitStructure;//配置引脚为普通推挽输出GPIO_InitStructure.Pin = TPAD_TIM_CH_PIN;GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;GPIO_InitStructure.Pull = GPIO_PULLDOWN;HAL_GPIO_Init(TPAD_TIM_CH_PORT, &GPIO_InitStructure);//输出低电平,放电HAL_GPIO_WritePin ( TPAD_TIM_CH_PORT, TPAD_TIM_CH_PIN ,GPIO_PIN_RESET);//保持一小段时间低电平,保证放电完全HAL_Delay(5);//清除更新标志__HAL_TIM_CLEAR_FLAG(&TIM_Handle,TIM_FLAG_CC1);__HAL_TIM_CLEAR_FLAG(&TIM_Handle,TIM_FLAG_UPDATE);//计数器归0__HAL_TIM_SET_COUNTER(&TIM_Handle,0);//引脚配置为复用功能,不上、下拉GPIO_InitStructure.Pin = TPAD_TIM_CH_PIN;GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;GPIO_InitStructure.Alternate = TPAD_TIM_AF;GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;GPIO_InitStructure.Pull = GPIO_NOPULL;HAL_GPIO_Init(TPAD_TIM_CH_PORT,&GPIO_InitStructure);
}
然后是tpad.c的完整代码:
/** Copyright (c) 2006-2021, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2025-06-14 c the first version*/#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include <tpad.h>#define TPAD_ARR_MAX_VAL 0XFFFF//保存没按下时定时器计数值
__IO uint16_t tpad_default_val=0;/************************************* 定时器输入捕获配置************************************/TIM_HandleTypeDef TIM_Handle;
static void TIMx_CHx_Cap_Init(uint32_t arr,uint16_t psc)
{GPIO_InitTypeDef GPIO_InitStructure;TIM_IC_InitTypeDef sConfigIC;//使能TIM时钟TPAD_TIM_CLK_ENABLE();//使能通道引脚时钟TPAD_TIM_GPIO_CLK_ENABLE();//端口配置GPIO_InitStructure.Pin = TPAD_TIM_CH_PIN;//复用功能GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;GPIO_InitStructure.Alternate = TPAD_TIM_AF;GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;//不带上下拉GPIO_InitStructure.Pull = GPIO_NOPULL;HAL_GPIO_Init(TPAD_TIM_CH_PORT, &GPIO_InitStructure);//初始化TIM//设定计数器自动重装值TIM_Handle.Instance = TPAD_TIMx;TIM_Handle.Init.Prescaler = psc;TIM_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;TIM_Handle.Init.RepetitionCounter = 0;TIM_Handle.Init.Period = arr;TIM_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;HAL_TIM_IC_Init(&TIM_Handle);//上升沿触发sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;// 输入捕获选择sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;//配置输入分频,不分频sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;//配置输入滤波器 不滤波sConfigIC.ICFilter = 0;//初始化捕获通道HAL_TIM_IC_ConfigChannel(&TIM_Handle, &sConfigIC, TPAD_TIM_Channel_X);//启动TIMHAL_TIM_IC_Start(&TIM_Handle, TPAD_TIM_Channel_X);
}/****************************************** 为电容按键放电* 清除定时器标志及计数******************************************/
static void TPAD_Reset(void)
{GPIO_InitTypeDef GPIO_InitStructure;//配置引脚为普通推挽输出GPIO_InitStructure.Pin = TPAD_TIM_CH_PIN;GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;GPIO_InitStructure.Pull = GPIO_PULLDOWN;HAL_GPIO_Init(TPAD_TIM_CH_PORT, &GPIO_InitStructure);//输出低电平,放电HAL_GPIO_WritePin ( TPAD_TIM_CH_PORT, TPAD_TIM_CH_PIN ,GPIO_PIN_RESET);//保持一小段时间低电平,保证放电完全HAL_Delay(5);//清除更新标志__HAL_TIM_CLEAR_FLAG(&TIM_Handle,TIM_FLAG_CC1);__HAL_TIM_CLEAR_FLAG(&TIM_Handle,TIM_FLAG_UPDATE);//计数器归0__HAL_TIM_SET_COUNTER(&TIM_Handle,0);//引脚配置为复用功能,不上、下拉GPIO_InitStructure.Pin = TPAD_TIM_CH_PIN;GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;GPIO_InitStructure.Alternate = TPAD_TIM_AF;GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;GPIO_InitStructure.Pull = GPIO_NOPULL;HAL_GPIO_Init(TPAD_TIM_CH_PORT,&GPIO_InitStructure);
}/****************************************************** 得到定时器捕获值* 如果超时,则直接返回定时器的计数值.******************************************************/
static uint16_t TPAD_Get_Val(void)
{/* 先放电完全,并复位计数器 */TPAD_Reset();//等待捕获上升沿while(__HAL_TIM_GET_FLAG(&TIM_Handle,TIM_FLAG_CC1) == RESET ){//超时了,直接返回CNT的值if(__HAL_TIM_GET_COUNTER( &TIM_Handle)>TPAD_ARR_MAX_VAL-500)return __HAL_TIM_GET_COUNTER( &TIM_Handle);};/* 捕获到上升沿后输出TIMx_CCRx寄存器值 */return HAL_TIM_ReadCapturedValue(&TIM_Handle, TIM_CHANNEL_1);
}/****************************************************** 读取n次,取最大值* n:连续获取的次数* 返回值:n次读数里面读到的最大读数值******************************************************/
static uint16_t TPAD_Get_MaxVal(uint8_t n)
{uint16_t temp=0;uint16_t res=0;while(n--){temp=TPAD_Get_Val();//得到一次值if(temp>res)res=temp;};return res;
}/********************************************************
*
* 初始化触摸按键
* 获得空载的时候触摸按键的取值.
* 返回值:0,初始化成功;1,初始化失败
*
*********************************************************/
uint8_t TPAD_Init(void)
{uint16_t buf[10];uint32_t temp=0;uint8_t j,i;//设定定时器预分频器目标时钟为:9MHz(216Mhz/24)TIMx_CHx_Cap_Init(TPAD_ARR_MAX_VAL,24-1);for(i=0;i<10;i++)//连续读取10次{buf[i]=TPAD_Get_Val();HAL_Delay(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;//取中间的6个数据进行平均for(i=2;i<8;i++){temp+=buf[i];}tpad_default_val=temp/6;/* printf打印函数调试使用,用来确定阈值TPAD_GATE_VAL,在应用工程中应注释掉 *///printf("tpad_default_val:%d\r\n",tpad_default_val);//初始化遇到超过TPAD_ARR_MAX_VAL/2的数值,不正常!if(tpad_default_val>TPAD_ARR_MAX_VAL/2){return 1;}return 0;
}uint8_t TPAD_Scan(uint8_t mode)
{//0,可以开始检测;>0,还不能开始检测static uint8_t keyen=0;//扫描结果uint8_t res=0;//默认采样次数为3次uint8_t sample=3;//捕获值uint16_t rval;if(mode){//支持连按的时候,设置采样次数为6次sample=6;//支持连按keyen=0;}/* 获取当前捕获值(返回 sample 次扫描的最大值) */rval=TPAD_Get_MaxVal(sample);/* printf打印函数调试使用,用来确定阈值TPAD_GATE_VAL,在应用工程中应注释掉 *///printf("scan_rval=%d\n",rval);//大于tpad_default_val+TPAD_GATE_VAL,且小于10倍tpad_default_val,则有效if(rval>(tpad_default_val+ 100)&&rval<(10*tpad_default_val)){//keyen==0,有效if(keyen==0){res=1;}keyen=3; //至少要再过3次之后才能按键有效}if(keyen){keyen--;}return res;
}
接下来是tpad.h
/** Copyright (c) 2006-2021, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2025-06-14 c the first version*/
#ifndef APPLICATIONS_TPAD_H_
#define APPLICATIONS_TPAD_H_#define TPAD_TIMx TIM2
#define TPAD_TIM_CLK_ENABLE() __TIM2_CLK_ENABLE()#define TPAD_TIM_Channel_X TIM_CHANNEL_1
#define TPAD_TIM_GetCaptureX TIM_GetCapture1#define TPAD_TIM_GPIO_CLK_ENABLE() __GPIOA_CLK_ENABLE()
#define TPAD_TIM_CH_PORT GPIOA
#define TPAD_TIM_CH_PIN GPIO_PIN_5
#define TPAD_TIM_AF GPIO_AF1_TIM2/************************** TPAD 函数声明********************************/
uint8_t TPAD_Init(void);
uint8_t TPAD_Scan(uint8_t mode);#endif /* APPLICATIONS_TPAD_H_ */
最后是main.c
/** Copyright (c) 2006-2025, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2025-06-14 RT-Thread first version*/#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>#include <rtdevice.h>
#include <board.h>
#include <tpad.h>#define LED_R_PIN GET_PIN(H, 10)static rt_base_t led_r_stat = PIN_LOW;int main(void)
{TPAD_Init();rt_pin_mode(LED_R_PIN, PIN_MODE_OUTPUT);while (1){if (TPAD_Scan(0)) {led_r_stat = (led_r_stat == PIN_LOW) ? PIN_HIGH : PIN_LOW;rt_pin_write(LED_R_PIN, led_r_stat);}}return RT_EOK;
}
感觉代码太不RTthread了。直接从HAL库例程平移过来。不过不管怎么说,电容按键可以使用了。