STM32 TIM定时器的使用(3)——电容按键检测

1、系列目录

  1. 基本计时实验
  2. 输入捕获实验(实验3的基础)
  3. 电容按键检测实验
  4. 输出PWM实验
  5. PWM驱动无刷电机实验

2、电容按键检测原理

原理其实非常简单,通过TIM定时器的输入捕获功能判断电容的充电时间(没有触摸的充电时间短,有触摸的充电时间长)经过对比来确定是否有手指触碰。
在这里插入图片描述
在这里插入图片描述
图片来源:零死角玩转STM32(库函数版)刘火良

由上面两个图片可以看出,没有手指触摸时电容为Cx,有手指触摸时电容为Cx+Cs
根据电容充电时间公式:
Vc=V0*(1-e(-t/RC))
在这里插入图片描述
图片来源:零死角玩转STM32(库函数版)刘火良
明显可以看出有触摸的时间(t2)较长,所以在设计程序是只需要测量出无触摸的时间(t1),若充电时间超出一定范围即可判定为有手指触摸。

3、程序设计思路

  1. TIM定时器初始化(TIM5_Config)
  2. 电容按键初始化,并设置触摸阈值(TPAD_Init)
  3. 获取没有触摸的充电时间t0(TPAD_GetVal)
  4. 循环获取充电时间tx并将与t0对比(TAPD_GetMaxVal)
  5. 若tx超过触摸阈值,判定为有触摸(TPAD_Scan)

4、代码设计

(0)宏定义

#define TIM_GPIO_PORT 		GPIOA
#define TIM_GPIO_PIN 		GPIO_Pin_1
#define TIM_CHANNEL			TIM_Channel_2
#define TIMx				TIM5
#define	TIMx_IRQn			TIM5_IRQn
#define	TIMx_PENDINGBIT		TIM_IT_Update | TIM_IT_CC2

#define TPAD_ARR_MAX_VAL 	0xFFFF

#define TPAD_GATE_VAL		100 //触摸阈值,ARR必须大于tpad_default_val + TPAD_GATE_VAL才可以被判断为有触摸 
vu16 	tpad_default_val 	= 0;//没有触摸时的计数器值

(1)TIM5_Config函数

//TIM5 通道2 PA1
void TIM5_Config(u16 arr,u16 psc)
{
    
    
	GPIO_InitTypeDef 			 GPIO_InitStructure;		
	TIM_TimeBaseInitTypeDef		 TIM_TimeBaseStructure;
	TIM_ICInitTypeDef			 TIM_ICInitStructure;		
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//PA1 配置
	GPIO_InitStructure.GPIO_Pin = TIM_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_Init(TIM_GPIO_PORT,&GPIO_InitStructure);	
	printf("GPIO is ready\r\n");
	
	//TIM5基础配置	
	//计时器时钟 = APB总线时钟/分频因子
	//							psc
	TIM_TimeBaseStructure.TIM_Period = arr;//重装载值/最大计数值
	TIM_TimeBaseStructure.TIM_Prescaler = psc;//预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIMx,&TIM_TimeBaseStructure);
	
	//TIM5输入捕获配置
	TIM_ICInitStructure.TIM_Channel = TIM_CHANNEL;//选择TIM5的CH1通道
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//上升沿捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//将IC1映射到IT1
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//信号分频值,不分频
	TIM_ICInitStructure.TIM_ICFilter = 0x03;//8个定时器周期滤波
	TIM_ICInit(TIMx,&TIM_ICInitStructure);
	printf("TIM is ready\r\n");
	
	TIM_Cmd(TIMx,ENABLE);  //使能TIM5	
	printf("TIM begin\r\n");
	
}

(2)按键初始化

/**  触摸按键初始化程序
  *  功能:获得没有触摸时的定时器取值
  *  入口参数:psc,定时器初始化中的预分频值
  *  返回值:0,初始化成功;1,初始化失败
  */
u8 TPAD_Init(u8 psc)
{
    
    
	u16 buf[10];
	u16 temp;
	u8 i=0,j=0;
	TIM5_Config( TPAD_ARR_MAX_VAL , psc-1);
	printf("11\r\n");
	for(i=0;i<10;i++)
	{
    
    
		buf[i] = TPAD_Get_Val();
		delay_ms(10);
	}
	for(i=0;i<10;i++)
	{
    
    
		for(j=i+1;j<9;j++)
		{
    
    
			if(buf[i]>buf[j])
			{
    
    
				temp	= buf[i];
				buf[i]	= buf[j];
				buf[j]	= temp;
			}
		}
	}
	temp = 0;
	for(i=2;i<8;i++)
	{
    
    
		temp += buf[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;
	}
	return 0;
}

(3)触摸按键复位函数

/**  按键复位程序
  *  功能:触摸按键复位
  *  入口参数:无
  *  返回值:无
  */
void TPAD_Reset(void)
{
    
    
	GPIO_InitTypeDef 			 GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = TIM_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_Init(TIM_GPIO_PORT,&GPIO_InitStructure);
	GPIO_ResetBits(TIM_GPIO_PORT,TIM_GPIO_PIN);	//电容放电
	
	delay_ms(5);
	
	TIM_SetCounter(TIMx,0);
	TIM_ClearITPendingBit(TIMx,TIM_IT_Update | TIM_IT_CC2);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(TIM_GPIO_PORT,&GPIO_InitStructure);//准备充电
}

(4)获取充电(触摸)时间

/**  获得TIMx的ARR值
  *  功能:获取定时器捕获值
  *  入口参数:无
  *  返回值:计数器捕获值,若超过最大量程则返回最大值。
  */
u16 TPAD_Get_Val(void)
{
    
    
	TPAD_Reset();
	while(TIM_GetFlagStatus(TIMx,TIM_IT_CC2 == RESET))
	{
    
    
		if(TIM_GetCounter(TIMx)>TPAD_ARR_MAX_VAL-500)
		{
    
    
			return TIM_GetCounter(TIMx);
		}		
	}
	return TIM_GetCapture2(TIMx);
}

(5)获取最大触摸按键时间

/**  
  *  功能:获取最大值
  *  入口参数:n,n次读数
  *  返回值:n次取值后的最大值
  */
u16 TPAD_Get_MaxVal(u8 n)
{
    
    
	u16 temp = 0;
	u16 res = 0;
	while(n--)
	{
    
    
		temp = TPAD_Get_Val();
		if( temp > res)
			res = temp;
	};
	return res;
}

(6)按键扫描函数

/**  按键扫描
  *  功能:检测按键是否按下
  *  入口参数:无
  *  返回值:0,没有按下;1按下
  */
u8 TPAD_Scan(void)
{
    
    
	static u8 keyen=0;
	u8 res=0;
	u8 sample=3;
	u16 rval;
	rval = TPAD_Get_MaxVal(sample);
	if(rval > (tpad_default_val + TPAD_GATE_VAL))
	{
    
    
		if(keyen == 0)
			res = 1;
		keyen = 3;
	}
	if(keyen)
		keyen--;
	return res;
}

5、总结

这一次实验没有什么难度,就是输入捕获的一个实际应用,但是有一个问题我始终不明白,在TIM5_Config函数中,如果我开启了TIM5的计数中断和CC2溢出中断,程序就会跑死,删除这句后,程序也可以正常执行,不理解,各位大佬如果知道可以评论告诉我,谢谢~

猜你喜欢

转载自blog.csdn.net/qq_44011116/article/details/113243069