1.电容触摸按键介绍:
- R是外接的充电电阻, Cs是没有触摸按下的触摸感应区和四周覆铜区域的一个杂散的电容;当使用手指去触摸感应区时,手指和感应区形成一个电容Cx,开关是电容放电的一个开关,在实际设计中是利用单片机IO口来替代这个开关,实现放电的功能;
- 当开关闭合时,有触摸按下时,Cx和Cs的值通过开关形成了一个回路,将上面的电释放掉。然后再断开开关,断开开关后Cs不能放电,通过Vcc经过R给Cs或Cx电容进行充电,当没有手指时只有Cs这个电容,充电曲线如图A所示;
- 当充电完成到达Vth时,所花费的时间为Tcs,如果当有手触摸时,此时充电的时间曲线为B,B充满电所需的时间是Tcs+Tcx的时间,可以利用充电时间来判断有没有触摸按下,有触摸按下时间大于Tcs;
- 定时器有输入捕获功能,可以通过捕获它的上升沿,当充电完成就来了一个高电平,从低一直慢慢充到满,捕获到上升沿有一段时间,当捕获到的时间与现在(Tcs+Tcx)时间进行比较,如果按键没有按下,它有一个充电时间,当有手触摸到感应区,手指跟感应区会形成一个电容,根据电容的并联关系它的两个值是累加的,电容变大则充电时间变大;
- Vc:电容的电压、V0:充电的电压、R:充电的电阻、T:充电的时间;
- 使用定时器5通道2,对于PA1管脚配置为推挽输出模式,让PA1输出为低电平,输出低电平相当于这个开关闭合,闭合的时候Cx+Cs两个电容的并联值,就会形成一个放电的回路,把电释放干净,当释放干净之后的一段时间再让PA1配置为浮空输入模式,相当于开关断开,利用外部的电源经过充电电阻,给电容充电,可以利用定时器的输入捕获功能来捕获上升沿的时间,根据这个时间和手按下和没有按下的时间进行对比,首先得到手没有按下时捕获到一次上升沿时间,这是一个参考有没有按下的基准,当手按下再次捕获则时间大于Tcs,在软件设计时,通常设置一个检测的阀值,假设为t,该值可以根据实际调试。
2.电容触摸按键实验:
通过TIM5的通道2(PA1)捕获电容触摸按键输入信号的高电平脉宽,根据捕获到高电平时间大小控制LED1指示灯开关,通过LED0指示灯不断闪烁表示系统正常运行。
(1)原理图:
(2)主函数:
#include "delay.h"
#include "led.h"
#include "usart1.h"
#include "touch.h"
int main(){
u8 i=0;
u32 indata=0; //累计整个从上升沿到下降沿的次数
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置分组
delay_init(); //延时初始化
LED_Init();
usart1_Init(9600); //串口通信初始化
Touch_Key_Init(6); //电容按键进行初始化->12MHz
while(1){
if(Touch_Key_Scan(0)==1)
{
LED1=!LED1; //检测到有触摸按键按下
}
i++;
if(i%20==0)
{
LED0=!LED0;
}
delay_ms(20);
}
}
(3)头文件:
#ifndef __TOUCH_H
#define __TOUCH_H
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
void TIM5_CH2_Input_Init(u16 arr,u16 psc); //定时器5通道2,输入捕获初始化
void Touch_Reset(void); //将电容上的电释放
u16 Touch_Get_Val(void); //读取输入捕获上升沿的值
u8 Touch_Key_Init(u8 psc); //对电容按键进行初始化
u16 Touch_Get_MaxVal(u8 n); //得到最大采集值
u8 Touch_Key_Scan(u8 mode); //检测触摸按键是否按下
#endif
(4)电容触摸按键功能函数:
#include "stm32f10x.h"
#include "stdio.h"
#include "delay.h"
#include "touch.h"
/*
功能:定时器5通道2,配置输入捕获
变量:arr:自动重装载值 psc:预分频系数
返回值:无
*/
void TIM5_CH2_Input_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_ICInitTypeDef TIM_ICInitStruct;
//1.使能定时器时钟和端口时钟,并设置引脚模式;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//配置端口参数
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1; //PA1
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//2.初始化定时器参数;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1; //1分频
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInitStruct.TIM_Period=arr; //自动重装载值
TIM_TimeBaseInitStruct.TIM_Prescaler=psc; //预分频系数
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStruct);
//3.设置通用定时器的输入捕获参数,开启输入捕获功能;
TIM_ICInitStruct.TIM_Channel=TIM_Channel_2; //通道2
TIM_ICInitStruct.TIM_ICFilter=0x00; //不使用滤波
TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising; //捕获极性为上升沿捕获
TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1; //1分频
TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI; //直接映射
TIM_ICInit(TIM5,&TIM_ICInitStruct);
//4.开启定时器
TIM_Cmd(TIM5,ENABLE);
}
/*
功能:将电容上的电释放
变量:无
返回值:无
*/
void Touch_Reset(void)
{
//1.设置端口模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1; //PA1
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//2.放电
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
delay_ms(20);
//3.清除标志位
TIM_ClearFlag(TIM5,TIM_FLAG_Update|TIM_FLAG_CC2);
//4.当放电完成时,将定时器的cnt(计数值)进行清零
TIM_SetCounter(TIM5,0);
//5.等待充电过程,将PA1配置为浮空输入模式,等待充电电阻进行充电
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入模式
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1; //PA1
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
/*
功能:读取输入捕获上升沿的值,一般不会有溢出事件
变量:无
返回值:输入捕获的值
*/
#define TOUCH_ARR_MAX_VAL 0xffff //定义计数器的最大值
#define TOUCH_GATA_VAL 100 //设置的阀值标志
u16 Touch_Get_Val(void)
{
//1.对电容进行放电
Touch_Reset();
//2.获取输入捕获的值
while(TIM_GetFlagStatus(TIM5,TIM_FLAG_CC2)==0) //捕获中->捕获未完成
{
if(TIM_GetCounter(TIM5)>(TOUCH_ARR_MAX_VAL-TOUCH_GATA_VAL)) //获取到输入捕获的值是否大于阀值->这里表示超过了阀值
{
return TIM_GetCounter(TIM5); //返回当前计数器的值
}
}
return TIM_GetCapture2(TIM5); //返回通道2的捕获值
}
/*
功能:对电容按键进行初始化
变量:psc:预分频系数
返回值:返回1:不正常 返回0:正常
*/
u16 TOUCH_DEFAULT_VAL=0;
u8 Touch_Key_Init(u8 psc)
{
u8 i=0,j=0;
u16 buf[10];
u32 sum=0;
TIM5_CH2_Input_Init(TOUCH_ARR_MAX_VAL,psc); //对输入捕获进行初始化
for(i=0;i<10;i++)
{
buf[i]=TOUCH_GATA_VAL; //进行十次输入捕获,将每次输入捕获到的值存放在buf数组中
delay_ms(10);
}
//进行滤波防止干扰,去掉最大和最小值
for(i=0;i<10-1;i++)
{
for(j=0;j<10-i-1;j++)
{
if(buf[j]>buf[j+1])
{
u8 temp=0;
temp=buf[j];
buf[j]=buf[j+1];
buf[j+1]=temp;
}
}
}
for(i=1;i<9;i++)
{
sum+=buf[i];
}
//得到平均的输入捕获值
TOUCH_DEFAULT_VAL=sum/8;
printf("输入捕获滤波后的值:%d\r\n",TOUCH_DEFAULT_VAL);
//判断捕获是否正常
if(TOUCH_DEFAULT_VAL>TOUCH_ARR_MAX_VAL/2) //不正常
{
return 1;
}
return 0; //正常
}
/*
功能:获取最大采集值
变量:n:采集次数
返回值:最大采集值
*/
u16 Touch_Get_MaxVal(u8 n)
{
u16 temp=0;
u16 result=0; //存储最大采集值
while(n--) //采集的次数
{
temp=Touch_Get_Val();
if(temp>result)
{
result=temp;
}
}
return result;
}
/*
功能:检测触摸按键是否按下
变量:mode=0:单次扫描 mode=1:连续扫描
返回值:返回1:触摸有效 返回0:触摸无效
*/
u8 Touch_Key_Scan(u8 mode)
{
u8 sample=5; //设置采样次数
u16 rval=0; //存储最大值
static u8 key=0; //设置单次和连续按下的标志
u8 result=0;
if(mode==1) //当设置为连续按下时,将key一直处于没有被按下的标志
{
key=0;
}
rval=Touch_Get_MaxVal(sample); //获取最大采集值
//采集的最大值与默认值相比较
if(rval>(TOUCH_DEFAULT_VAL+TOUCH_GATA_VAL)&&(10*TOUCH_DEFAULT_VAL)) //触摸有效
{
if((key==0)&&(rval>(TOUCH_DEFAULT_VAL+TOUCH_GATA_VAL)))
{
result=1;
}
}
else //触摸无效
{
key=0;
}
return result; //触摸无效
}