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

一、电容触摸按键原理

触摸电容按键工作原理:当手按下时,形成附加电容,按下和松开两种状态的区别是电路中的电容值不同。而电容值的不同可以体现在充电时间上。所以可以利用输入捕获充电时间实现按键检测。

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

 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 = 0x00;//不采用滤波器
    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]<buf[j+1])//从大到小排序
            {
                temp = buf[j];
                buf[j] = buf[j+1];
                buf[j+1] = temp;
            }
            
            
        }
    }
    
    temp = 0;
    
    for(i=2;i<8;i++)
    {
        temp += buf[i];
    }
    TPAD_Init_val = temp/6;
    
    
}

补充说明:为了提高可靠性,捕获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、如果连续三次检测不能满足按下条件则认为松开了,则改变标志变量,进而可以检测下一次按下

总结

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

·

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

  • 9
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值