基于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
我们把采集到的有效数据视为高电平,无效的视为低电平。
**非连按:**初始化后到采集第一次数据成功经历了电平从低到高,想要不连续,下一次的有效数据采集一定是低电平,才能进行下一次有效数据的采集,这样就实现了非连按
连按:不必经过从低电平到高电平阶段,在一定时间内,手为松开电容触摸按键,则自动视为采集下一次有效数据开始
细节问题
关于门槛值的定义,一定要先采集自己板子上的实际值再开始数据判断,每个开发板的实际值有差异,否则可能导致一直不成功反复修改代码