基于STM32F103ZET6实现电容触摸按键控制LED

依然采用输入捕获的原理来采集是否产生电容触摸。

实验目的:

我们将用 TIM5 的通道 2(PA1)来做输入捕获,并实现一个简单的电容触摸按键,通过该按键控制 DS1 的亮灭。

实验原理

电容式触摸按键。
在这里插入图片描述
如图为内部结构图,我们是通过充放电时间(到达一个固定电压值)来判定是否有触摸电容按键,当开关打开时,带内容CX为电容触摸按键的平面,里面包含一定量的散杂(本身存在)电容,这时,电容处于放电状态,当放电完毕,关闭开关,这时电容CX开始充电,充电时间遵从公式Vc=V0*(1-e^(-t/RC)),算出T,可以得到未触摸时的充电时间值,可以作为初始化的步骤。
在这里插入图片描述
如图为有触摸(B)和无触摸(A)到达固定电压的时间值,只要我们通过分析实际测量的值,做一个门槛值,就可以准确判断是否存在触摸情况。

流程

说了实验原理,想要实现我们的功能,少不了的是梳理实验流程。
想要实时的实现我们的输入捕获,需要经历一个不断充放电的过程,检测充放电时间来判定是否有按下电容触摸按键,一直循环方可实现触摸按键的扫描,实现led灯反转。
解释及补充
led能否反转,关键在于充电的时间,我们在(mcu重启)初始化时,就将未触摸时的充电时间算出来,保存在一个十六位的变量中,与后面触摸时的充电时间作比较判断。
我们使用 PA1(TIM5_CH2)来检测 TPAD 是否有触摸,在每次检测之前,我们先配置PA1 为推挽输出,将电容 Cx放电,然后配置 PA1 为浮空输入,利用外部上拉电阻给电容 Cx充电,同时开启 TIM5_CH2 的输入捕获,检测上升沿,当检测到上升沿的时候,就认为电容充电完成了,完成一次捕获检测。

硬件配置

精英版的用跳线帽短接
在这里插入图片描述
在这里插入图片描述

算法梳理

我们需要的:
(1):需要未触摸时的充电时间,取多次求平均值,定义为static变量
(2):需要时刻采集电容触摸按键上的充电时间,多次采集,取最大值与未触摸的做比较,
(3):支持连续触摸(持续采集)还是非连续(按下去必须松开才会进行下一次采集)

程序初始化流程

(1):GPIO,定时器时钟开启
(2):定时器初始化,初始化GPIO为开漏输出
(3):开启定时器捕获,不开启中断,采用循环检测标志位判断是否成功采集捕获脉冲
(4):编写充电放电过程函数,从推挽输出放电到浮空输入
(5):循环检测标志位,获取触摸时的充电时间值
(6):采集数据的准确度处理,算法自行编写,取出最大值,对采集到的合理值进行支持连按和非连按处理
(7):在主函数main.c里循环检测调用数据采集函数

电容触摸按键头文件tpad.h

#ifndef TPAD_H
#define TPAD_H
#include "sys.h"
extern void tpad_init(u16 arr,u16 prer);
extern void tapd_reset();
extern int value_get(void);
extern int scan_max(int );
extern int scan(int );
extern int tapd_initvalue();
#endif

电容触摸按键源文件tapd.c

#include "tpad.h"
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "timepwm.h"
#define TPAD_GATE_VAL 30
short int basevalue;

void tpad_init(u16 arr,u16 prer)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_InitStructure;
	TIM_ICInitTypeDef  TIM_ICInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);


  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);
	
	TIM_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_InitStructure.TIM_Period=arr;
	TIM_InitStructure.TIM_Prescaler=prer;
	TIM_TimeBaseInit(TIM5, &TIM_InitStructure);
	
	TIM_ICInitStructure.TIM_Channel=TIM_Channel_2;
	TIM_ICInitStructure.TIM_ICFilter=0x00;
	TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
	TIM_ICInitStructure.TIM_ICPrescaler= TIM_ICPSC_DIV1;
	TIM_ICInitStructure.TIM_ICSelection= TIM_ICSelection_DirectTI;
	TIM_ICInit(TIM5, &TIM_ICInitStructure);
	
	TIM_ITConfig( TIM5,TIM_IT_CC1,ENABLE);
	
	TIM_Cmd(TIM5,ENABLE );
}
void tpad_reset(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);	
	
	delay_ms(5);
	TIM_SetCounter(TIM5,0);
	TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
}
int value_get(void)
{
	tpad_reset();
	while(TIM_GetFlagStatus(TIM5, TIM_IT_CC2)==0)
	{
		if(TIM_GetCounter(TIM5)>(65536-500))
			return TIM_GetCounter(TIM5);
	}
	return TIM_GetCapture2(TIM5);	
}
int scan_max(int n)
{u16 temp=0;
	u16 res=0;
	while(n--)
	{
		temp=value_get();
		if(temp>res)res=temp;
	};
	return res;
}
int tapd_initvalue(void)
{
	int buf[10];
	int temp;
	int i;
	int j;
	int d;
	tpad_init(0xffff,71);
	for( d=0;d<10;d++)
	{
		buf[d]=value_get();
	}
	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;
	for( i=2;i<8;i++)temp+=buf[i];
	basevalue=temp/6;
	if(basevalue>0xffff/2)return 1;
	return 0;		     	    					    
}
int scan(int a)
{
	static unsigned char  flag=0;
	int b;
	int e=0;
	b=scan_max(a);
	if(b<(TPAD_GATE_VAL+basevalue))
	{
		flag++;
	}
	if((flag>=3)&&(b>TPAD_GATE_VAL+basevalue))
	{
		e=1;
		flag=0;
	}
	else 
	{
		e=0;
	}
	return e;
}

电容触摸按键主函数main.c

#include "delay.h"
#include "sys.h"
#include "tpad.h"
#include "led.h"
int main(void)
{
	delay_init();
	LED_Init();
	tapd_initvalue();
	 while(1)
	 {
		  delay_ms(10);
		 if(scan(10))
     {
			 LED1=!LED1;
		 }
	 }
}

连按与非连按

我们定义有效数据定义为采集时间大于某一个设定值如:b>TPAD_GATE_VAL+basevalue
我们把采集到的有效数据视为高电平,无效的视为低电平。
**非连按:**初始化后到采集第一次数据成功经历了电平从低到高,想要不连续,下一次的有效数据采集一定是低电平,才能进行下一次有效数据的采集,这样就实现了非连按
连按:不必经过从低电平到高电平阶段,在一定时间内,手为松开电容触摸按键,则自动视为采集下一次有效数据开始

细节问题

关于门槛值的定义,一定要先采集自己板子上的实际值再开始数据判断,每个开发板的实际值有差异,否则可能导致一直不成功反复修改代码

发布了10 篇原创文章 · 获赞 14 · 访问量 4071

猜你喜欢

转载自blog.csdn.net/weixin_42271802/article/details/104711701