笔记14:STM32F4之电容触摸按键

一. 电容触摸按键原理
1.电路原理介绍
在这里插入图片描述

上图是基于整点原子STM32F4的原理图,其中蓝色TPAD部分就是我们电容触摸按键位置,红色的TPAD是悬空的,需要我们将其与其他引脚相连接起来。在开发板上,我们需要用跳线帽将STM ADC与TAPD连接起来,这样TAPD引脚就可以与PA5连接起来了。(也可以用其他引脚)
由于要用到捕获,因为我们需要在数据手册上查看PA5可以对应哪一个定时器通道。
在这里插入图片描述
由上图我们选择定时器2通道1。
在这里插入图片描述
上图是按键按下前后对比图。R:外接电容充放电电阻。
按键按下之前,电路之中只连接了一个电容Cs(TPAD和PCB间的杂散电容),按键按下之后,与Cs并联了一个Cx(手指按下时,手指和TPAD之间的电容),电路总电容变大。其中开关部分是电容放电开关,由STM32 IO口代替。也就是对应着第一张图片的红色TAPD脚。
根据电路知识可知,电容上的充放电公式为:
Vt = V0+(V1-V0)* [1-e(-t/RC)]
V0 为电容上的初始电压值;
V1 为电容最终可充到或放到的电压值;
Vt 为t时刻电容上的电压值。
RC为充电时间常数,是电容的端电压达到最大值的0.63倍时所需要的时间,通常认为时间达到5倍的充电时间常数后就认为充满了。时间常数越大,电容充放电就越慢(大电容和小电容相比肯定是大电容充电慢;大电阻和小电阻相比,大电阻起始电流较小,因此大电阻充电时间慢)。根据这个原理我便可涉及触摸按键的使用原理了。
2. 具体原理介绍
由以上分析我们得知,在电阻相同的条件下,电容越大,充电到某一电压值的时间越长,因此我们记录触摸按键没有触摸时电容的充电时间T1,之后的检测以这个时间做标准,按下TPAD,电容变大,所以充电时间为T2。我们可以通过检测充放电时间,来判断是否按下。如果T2-T1大于某个值,就可以判断有按键按下。
3. 检测电容触摸按键的过程
①. TPAD引脚设置为推挽输出,输出0,实现电容放电到0。(相当于将红色TPAD接地)
②. TPAD引脚设置为浮空输入(IO复位后的状态),电容开始充电。(相当于将红色TAPD断开)
③. 同时开启TPAD引脚的输入捕获开始捕获。
④. 等待充电完成(充电到底Vx,检测到上升沿)。
⑤. 计算充电时间。
二. 程序设计
1.具体程序及详细说明

头文件"tpad.h"里面就是对tapd.c里面的函数内容进行申明,具体内容如下:

#ifndef __TPAD_H
#define __TPAD_H
#include "sys.h"							   	    
void TPAD_Reset(void);
u16 TPAD_Get_Val(void);
u16 TPAD_Get_MaxVal(u8 n);
u8 TPAD_Init(u32 psc);		//初始化触摸按键,记录没有按下的电容充电时间,初始化成功返回0,未成功返回1;
u8 TPAD_Scan(u8 mode);
void TIM2_CH1_Cap_Init(u32 arr,u16 psc);

#endif

宏定义及头文件部分:对于代码vu16类型说明:
typedef __IO uint16_t vu16; 这是本身在stm2f4xx.h头文件里面定义了的。

#include "tpad.h"
#include "delay.h"		    
#include "usart.h"
#define TPAD_ARR_MAX_VAL  0XFFFFFFFF	//最大的ARR值(TIM2是32位定时器),防止捕获的时候CCRX>ARR	  
vu16 tpad_default_val=0;				//空载的时候(没有手按下),计数器需要的时间
#define TPAD_GATE_VAL 100							//触摸的门限值,也就是必须tpad_default_val+TPAD_GATE_VAL,才认为是有效触摸

捕获相关程序-----TIM2_CH1_Cap_Init(),当电容开始充电并且达到能够捕获上升沿的电压时,就会捕获到一个上升沿。

void TIM2_CH1_Cap_Init(u32 arr,u16 psc)		//定时器 2 通道 1 输入捕获配置
{
	GPIO_InitTypeDef GPIO_InitStruct;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_ICInitTypeDef TIM_ICInitStruct;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);			//TIM2时钟使能
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);		//GPIOA时钟使能
	
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;						//GPIOA5
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;						//复用功能
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_100MHz;
	GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;					//推挽复用输出
	GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;					//不带上下拉
	GPIO_Init(GPIOA,&GPIO_InitStruct);							//初始化PA5
	
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_TIM2);		//将GPIO5复用映射到TIM2
	
	TIM_TimeBaseInitStruct.TIM_Prescaler=psc;					//TIM2分频系数
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;	//TIM2向上计数模式
	TIM_TimeBaseInitStruct.TIM_Period=arr;						//重载值
	TIM_TimeBaseInitStruct.	TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);				//初始化定时器2
	
	TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;					//TIM2的通道1
	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;		//上升沿捕获
	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;	//IC1映射到TI1上
	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;					//不分频
	TIM_ICInitStruct.TIM_ICFilter=0000; 								//不滤波
	TIM_ICInit(TIM2,&TIM_ICInitStruct);							//初始化TIM2捕获通道1
	TIM_Cmd(TIM2,ENABLE);
}



TAPD_Reset(),主要作用就是让电容放电

void TPAD_Reset(void)		//复位一次,并让电容放电,清零定时计数器
{
	
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;				//PA5
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;			//普通输出模式
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_100MHz;		//速度100MHZ
	GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;			//推挽输出
	GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_DOWN;			//下拉
	GPIO_Init(GPIOA,&GPIO_InitStruct);      			//初始化GPIOA
	
	GPIO_ResetBits(GPIOA,GPIO_Pin_5);					//PA5输出0,电容放电
	delay_ms(5);										//等待放电结束
	TIM_SetCounter(TIM2,0);								//TIM2计数器清0
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;				//PA5
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;				//复用功能
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_100MHz;		//速度100MHZ
	GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;			//推挽复用输出
	GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;			//既不上拉也不下拉
	GPIO_Init(GPIOA,&GPIO_InitStruct);					//初始化GPIOA
	
}

TPAD_Get_Val,这个函数作用是获取到达上升沿时的计数器里面的计数值,为计算电容充电时间提供数据。

u16 TPAD_Get_Val(void)			//得到定时器的捕获值
{
	TPAD_Reset();
	while(TIM_GetFlagStatus(TIM2, TIM_IT_CC1) == RESET)//等到电容充电到一个值后产生上升沿捕获
	{
		if(TIM_GetCounter(TIM2)>TPAD_ARR_MAX_VAL-500)		//超过正常按下时间,直接返回定时器计数值
			return TIM_GetCounter(TIM2);
	}
	return TIM_GetCapture1(TIM2);		//返回定时器捕获值
		
}

TAPD_Get_MaxVal,这个函数的作用是为了后面判断按键触摸时触摸是否有效,取最大值判断更精确。

u16 TPAD_Get_MaxVal(u8 n)//n为读取的次数
{
	u16 temp=0,res=0;
	while(n--)
	{
		temp=TPAD_Get_Val();
		if(temp>res)
			res=temp;
		
	}
	return res;
}				

TAPD_InIT()

u8 TPAD_Init(u32 psc)		//初始化触摸按键,记录没有按下的电容充电时间,初始化成功返回0,未成功返回1
{
	
	u16 ChargingTimeData[10],temp;
	u8 i,j;
	TIM2_CH1_Cap_Init(TPAD_ARR_MAX_VAL,psc-1);
	for(i=0;i<10;i++)		//读取10次值
	{
		ChargingTimeData[i]=TPAD_Get_Val();
		delay_ms(10);
	}
	for(i=0;i<9;i++)			//数组升序排列
	{
		for(j=0;j<10;j++)
		{	
			if(ChargingTimeData[i]>ChargingTimeData[j])
				temp=ChargingTimeData[i];
				ChargingTimeData[i]=ChargingTimeData[j];
				ChargingTimeData[j]=temp;
		}
		
	}
	temp=0;
	for(i=2;i<8;i++)		//取中间6个数据做平均值
		temp+=ChargingTimeData[i];
	tpad_default_val=temp/6;
	printf("tpad_default_val:%d\r\n",tpad_default_val);	//输出初始化时的电容充电相关数据
	if(tpad_default_val>TPAD_ARR_MAX_VAL/2)
		return 1;	//初始化遇到超过 TPAD_ARR_MAX_VAL/2 的数值,不正常!
		return 0;
}

触摸按键扫描部分

u8 TPAD_Scan(u8 mode) //mode=1支持一直按下,mode=0,不支持一直按下
						//返回值为1有按下,返回值为0没有按下
{
	static u8 keyen=0; //0,可以开始检测;>0,还不能开始检测
	u8 res=0,sample=3; //默认采样次数为 3 次
	u16 rval;
	if(mode)
	{
		sample=6; //支持连按的时候,设置采样次数为 6 次
		keyen=0; //支持连按
	}
	//不支持连按的情况下
	rval=TPAD_Get_MaxVal(sample);
	if(rval>(tpad_default_val+TPAD_GATE_VAL)&&rval<(10*tpad_default_val))	//大于 tpad_default_val+TPAD_GATE_VAL,且小于 10 倍 tpad_default_val,则有效
	{
		if((keyen==0)&&(rval>(tpad_default_val+TPAD_GATE_VAL)))				//大于 tpad_default_val+TPAD_GATE_VAL,有效
			res=1;
			keyen=3; //至少要再过 3 次之后才能按键有效
	}
	if(keyen)
		keyen--;
	return res;
		
	
	
}

整体思路:
在这里插入图片描述
流程图:

在这里插入图片描述

  • 7
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值