锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

stm32学习笔记——电容触摸按键的实现

时间:2022-12-31 13:30:00 244j1200电容电容203jstm8电容stm电容触摸电容的if

目录

一、电容触摸按键原理

正点原子战舰开发板对应的硬件原理图?-toc" style="margin-left:0px;">二、正原子战舰开发板对应的硬件原理图

三、实现代码

1.定时器的初始配置函数(这里使用通用定时器TIM5通道2,对应端口为)

2.电容触摸按键的初始化

3.捕获充电时间的函数

按键检测函数

总结


一、电容触摸按键原理

触摸电容按钮的工作原理:当手按下时,形成额外的电容。按下和松开的区别在于电路中的电容值不同。电容值的差异可以反映在充电时间上。因此,按钮检测可以使用输入捕获充电时间。

二、正原子战舰开发板对应的硬件原理图

1.电容式触摸按钮电路为片外电路。为了实现电容式充放电状态切换,可以将其连接到芯片端口。通过端口状态从泄漏输出或推拉输出到浮动输入,实现从放电到充电的状态转换。

2.补充说明:

①关于为什么开漏和推挽输出可以实现电容的放电?

端口设置为推拉输出或泄漏输出,放置0,可实现电容器放电(根本原因:无论是泄漏模式还是推拉模式,放置0是电容器接地,可实现放电)

②为什么充电时端口要设置为浮空输入?

将相应的端口放置为浮动输入(浮动是指芯片内部电流不影响外部电路电容器充电)。当充电到一定值时,电容器上的电压达到一定值,使施密特殊触发器产生一定的输出,即检测上升边缘,触发输入捕获时间。

3.这里使用通用定时器TIM5通道2,对应端口PA.1.正点原子战舰开发板P7.1和P7.2.用跳线帽连接。即实现端口PA.1与外电路触摸电容器连接,可控制外电路电容器充放电,实现输入捕获充电时间。

三、实现代码

1.定时器的初始配置函数(这里使用通用定时器TIM5通道2,对应端口为)

代码如下:

void TIM5_IC2Init(uint16_t pre,uint16_t reload) {  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;  TIM_ICInitTypeDef TIM_ICInitStruct;  GPIO_InitTypeDef GPIO_InitStructure;    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;   GPIO_Init(GPIOA,&GPIO_InitStructure);  GPIO_ResetBits(GPIOA,GPIO_Pin_1);  delay_ms(100)      TIM_TimeBaseInitStruct.TIM_Prescaler = pre;  TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;  TIM_TimeBaseInitStruct.TIM_Period = reload;  TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;  TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStruct);    TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;  TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;  TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;  TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;  TIM_ICInitStruct.TIM_ICFilter = 0x///不使用滤波器 &nsp;   TIM_ICInit(TIM5,&TIM_ICInitStruct);    

 
    TIM_Cmd(TIM5,ENABLE);
    
    
}

2.电容触摸按键初始化

说明:利用该函数获得未按下时的充电时间,与后面按下时的充电时间作比较,进而可以判断是否按下。

代码如下:

uint16_t TPAD_Init_val;//全局变量用于存储未按下时的充电时间
void TPAD_Init(void)
{
    uint16_t buf[10];
    uint8_t i;
    uint8_t j;
    uint16_t temp;
    for(i=1;i<=10;i++)
    {
        buf[i-1]= get_val();
        delay_ms(10);
        
    }
    for(i=0;i<9;i++)
    {
        for(j=0;j<9-i;j++)
        {
            if(buf[j]

补充说明:为了提高可靠性,捕获10次充电时间,并去掉两个最大和最小值,再取平均值。

3、捕获一次充电时间的函数 

说明:要捕获一次充电时间要经过一下几个步骤:①放电②计数器清零、标志位清零③充电④根据标志位读取定时器输入捕获的充电时间,并返回

代码如下:


uint16_t get_val(void)
{
	uint16_t temp;
	
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);
	delay_ms(5);//放电
	

	TIM_ClearITPendingBit(TIM5,TIM_IT_CC2);


	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA,&GPIO_InitStructure);//充电
	
	TIM_SetCounter(TIM5,0);//计数器清零
	
	
	
	while( TIM_GetFlagStatus(TIM5,TIM_IT_CC2)!=SET)
	{
		
		if(TIM_GetCounter(TIM5)>max_count-500)//认为已经超时了
		{
			
			return TIM_GetCounter(TIM5);
		}
	}
	temp = TIM_GetCapture2(TIM5);
	return temp;
	
	
	
}

补充说明:

1、注意标志位的判断的库函数一定不要使用错误!!!要用TIM_GetFlagStatus而不能用

TIM_GetITStatus。二者的区别是,库函数FlagStatus单纯判断标志位是否置位,即当事件发生但是没有使能对应事件的中断时,标志位仍然置位,但没有发生中断。而库函数ITStatus,不仅判断了标志位还判断了使能位,只有当中断发生(事件发生+中断使能)才返回SET。

2、注意分清标志位和中断的概念区别

当某个模块(比如串口、定时器)含有状态寄存器则涉及标志位和中断之间的区别,进而有库函数FlagStatus和ITStatus的使用区别。
标志位置位,是指当某事件发生时,无论对应的中断是否使能都会使得相应的标志位置位。
而当对应的中断也使能时,可以产生中断,此时要进行中断优先级配置、初始化配置、编写中断服务函数。

而此实验中只使用了标志位,而没有使用中断,所以不用进行中断优先级配置、初始化配置、编写中断服务函数。

3、关于TIM_ClearITPendingBit(TIM5,TIM_IT_CC2);

此处虽然没有用到中断,且中断和标志位的概念不同。但是清除中断标志位和标志位都是对SR寄存器操作,故可以替代使用。

4、按键检测函数

说明:不断检测是否按下,本质是不断捕获充电时间,如果充电时间大于设置的门限值(实验确定),则认为按下。

代码如下:


uint8_t TPAD_Scan(void)
{
	//实现不支持连按的第一种思路(以松开的检测值作为松开标志)
	static uint8_t flag = 0;
	uint16_t temp = get_val_max(sampletime);
	
	if(temp < TPAD_Init_val+10 )
	{
		flag = 0;
	}
	
	if((temp>TPAD_Init_val+TPAD_gate_val)&&(!flag))
	{
		printf("get_val_max %d\r\n",temp);
		flag = 1;
		return 1;
		
		
	}else return 0;
	
	
	//实现不支持连按的第二种思路,以不满足按下条件作为松开标志
	//第二种思路的结果性能更稳定:可以通过改变标志变量的值改变检测按下与松开状态切换的时间间隔。
	
	//编写代码思路:从简入手,即先实现支持连按的,再加标志
	/*
	uint8_t res = 0;
	static uint8_t flag = 0;
	if(get_val_max(sampletime)>TPAD_Init_val+TPAD_gate_val)//TPAD_gate_val是宏定义的门限值(具体值通过实验确定)
	{
		if(flag == 0)
		{
			res = 1;
		}
		flag = 3;
	}
	if(flag)
	{
		flag--;
	}

	return res;
	*/
}

 补充说明:

1、实现按键检测思路(不支持连按)


 2、一次检测涉及三次输入捕获,取最大值,提高可靠性

三次输入捕获获得最大值的代码如下:

uint16_t get_val_max(uint8_t n)
{
	uint16_t temp;
	uint16_t val_max;
	val_max = get_val();
	while(n-1)
	{
		temp = get_val();
		printf("get_val is %d\r\n",temp);
		if(temp > val_max)
		{
			val_max = temp;
		}
		n--;
	}
	
	return val_max;
}

 3、如果连续三次检测不能满足按下条件则认为松开了,则改变标志变量,进而可以检测下一次按下

总结

以上就是今天要讲的内容,本文简单介绍了正点原子开发板触摸按键的使用以及代码编写过程中的注意事项。

·

希望大家多多支持,一键三连呀~~~

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章