1.电容触摸按键原理
?R:外接电容充放电电阻;Cs:TPAD和PCB间的杂散电容;Cx:手指按下时,手指和TPAD之间的电容;开关:电容放电开关,由STM32IO口代替。
2.电容充电时间与电容大小的关系
同样的条件下,电容值C跟时间t成正比关系,电容越大,充电到达某个临界值的时间越长。
3.检测电容触摸按键过程
(1)TPAD引脚设置为推挽输出,输出0,实现电容放电到0;
(2)TPAD引脚设置为浮空输入(IO口复位后的状态),电容开始充电;
(3)同时开启TPAD引脚的输入捕获,开始捕获;
(4)等待充电完成(充电到底Vx,检测到上升沿);
(5)计算充电时间。
没有按下的时候,充电的时间为T1(default)。按下TPAD,电容变大,所以充电时间为T2。我们可以
通过检测充放电的时间,来判断是否按下。如果T2-T1大于某个值,就可以有按键按下。
实验内容
手触摸按键后,LED1灯翻转。
硬件连接?
?软件设计
?1.相关库函数
(1)void TPAD_Reset(void)函数:复位TPAD
设置IO口为推挽输出输出0,电容放电。等待放电完成之后,设置为浮空输入,从而开始充电。同时把计数器的CNT设置为0;
(2)TPAD_Get_Val()函数:获取一次捕获值(得到充电时间)
?复位TPAD,等待捕获上升沿,捕获之后,得到定时器的值,计算充电时间;
(3)TPAD_Get_MaxVal()函数:
多次调用TPAD_Get_Val函数获取充电时间。获得最大值。
(4)TPAD_Init()函数:初始化TPAD
在系统初启动后,初始化输入捕获。先10次调用TPAD_Get_Val()函数获取10次充电时间,然后获取中间N(N=8或者6)次的平均值,作为在没有电容触摸按键按下时的时候的充电时间缺省值tpad_default_val;
(5)TPAD_Scan()函数:扫描TPAD
调用TPAD_Get_MaxVal()函数获取多次充电中最大的充电时间,跟tpad_default_val比较,如果大于某个阈值,则认为有触摸动作;
(6)void TIM5_CH2_Cap_Init(u16 arr,u16 psc)函数:输入捕获通道初始化
2.程序思路
3.实验代码
tpad.h ?
#ifndef __TPAD_H__
#define __TPAD_H__
#include "sys.h"
void TPAD_Reset(void);
u16 TPAD_Get_Val(void);
u16 TPAD_Get_MaxVal(u8 n);
u8 TPAD_Init(u8 psc);
u8 TPAD_Scan(u8 mode);
void TIM2_CH1_Cap_Init(u32 arr,u16 psc);
#endif
?tpad.c
#include "tpad.h"
#include "usart.h"
#include "sys.h"
#include "delay.h"
#define TPAD_ARR_MAX_VAL 0xFFFFFFFF //最大的ARR值
vu16 tpad_default_val=0; //空载的时候,计数器需要的时间
void TPAD_Reset(void) //TPAD复位
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //PA5
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;//普通输出
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度100MHz
GPIO_InitStructure.GPIO_OType=GPIO_OType_pp; //推挽
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN; //下拉
GPIO_Init(GPIOA,&GPIO_InitStructure) //初始化PA5
GPIO_ResetBits(GPIOA,GPIO_Pin_5); //输出0,放电
delay_ms(5);
TIM_ClearITPendingBit(TIM2,TIM_IT_CC1|TIM_IT_Update);//清除中断标志
TIM_SetCounter(TIM2,0); //归0
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //复用输出
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度100MHz
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; //推挽
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL; //不带上下拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA5
}
u16 TPAD_Get_Val(void) //得到定时器捕获值
{
TPAD_Reset();
while(TIM_GetFlagStatus(TIM2,TIM_IT_CC1)==RESET)//等待捕获上升沿
{
if(TIM_GetCounter(TIM2)>TPAD_ARR_MAX_VAL-500)
return TIM_GetCounter(TIM2);//超时了,直接返回CNT的值
}
return TIM_GetCapture1(TIM2);//若没超时,返回TIM2_CCR2的值
}
u16 TPAD_Get_MaxVal(u8 n) //读取n次,取最大值
{
u16 temp=0,res=0;
while(n--)
{
temp=TPAD_Get_Val(); //得到一次值
if(temp>res)res=temp;
}
return res;
}
u8 TPAD_Init(u8 psc) //初始化触摸按键
{
u16 buf[10],temp;
u8 j,i;
TIM2_CH1_Cap_Init(TPAD_ARR_MAX_VAL,psc-1); //设置分频系数
for(i=0;i<10;i++)
{
buf[i]=TPAD_Get_Val();
delay_ms(10);
}
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++)//取中间的6个数据进行平均
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;
//初始化遇到超过TPAD_ARR_MAX_VAl/2的数值,不正常
return 0;
}
#define TPAD_GATE_VAL 100 //触摸的门限值
u8 TPAD_Scan(u8 mode) //扫描触摸按键
{
static u8 keyen=0; //0,可以开始检测;>0,还不能开始检测
u8 res=0; //res=0说明按键无效,res=1说明按键有效
sample=3; //默认采样次数为3次
u16 rval;
if(mode) //mode:0,不支持连续触发
{
sample=6; //支持连按的时候,设置采样次数为6次
keyen=0; //支持连按
}
rval=TPAD_Get_MaxVal(sample); //连续采样3次,取最大值
if(rval>(tpad_default_val+TPAD_GATE_VAL)&&rval<(10*tpad_default_val))
{
if((keyen==0)&&(rval>(tpad_default_val+TPAD_GATE_VAL)))res=1;
keyen=3;
}
if(keyen)keyen--;
return res;
}
void TIM2_CH1_Cap_Init(u32 arr,u16 psc) //定时器2通道2输入捕获
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM2_ICInitTypeDef TIM2_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //TIM2时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能PORTA时钟
GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_TIM2); //PA5复用为定时器2
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //GPIOA5
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL; //不带上下拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA5
TIM_TimeBaseInitStructure.TIM_Period=arr; //设计定时器自动重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //预分频器
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//TIM向上计数
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); //初始化定时器2
TIM2_ICInitStructure.TIM_Channel=TIM_Channel_1; //选择输入端IC1映射到TIM2
TIM2_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising //上升沿捕获
TIM2_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
TIM2_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM2_ICInitStructure.TIM_ICFilter=0x00; //不滤波
TIM_ICInit(TIM2,TIM2_ICInitStructure); //使能定时器2
TIM_Cmd(TIM2,ENABLE); //使能定时器2
}
main.c
#include "sys.h"
#include "tpad.h"
#include "led.h"
#include "usart.h"
#include "delay.h"
int main(void)
{
U8 t=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGruop_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率115200
LED_Init(); //初始化LED
TPAD_Init(8); //初始化触摸按键
while(1)
{
if(TPAD_Scan(0)) //成功捕获到了一次上升沿
{
LED1=!LED1; //LED1取反
}
t++;
if(t==15)
{
t=0;
LED0=!LED0; //LED0取反,提示程序正在运行
}
delay_ms(10);
}
}
|