一、电容触摸按键原理
RC电路充放电原理:
R为电阻,CX为电容,按下按钮CX开始充电V1相同。
RC电路充放电公式:Vt=V0 (V1-V0)*[1-exp(-t/RC)]
V0是电容器上的初始电压值;V1.电容器最终可以充放的电压值;VtT时刻电容上的电压值;
当V0=公式简化为:0Vt=V1*[1-exp(-t/RC)]
也就是说,在相同的条件下,电容值C与时间t成正比。电容器越大,充电时间越长。(零状态响应)
电容触摸按键原理图:
R:充放电电阻的外部电容;Cs:TPAD和PCB杂散电容;Cx:按下手指时,手指和TPAD之间形成电容。 开关:电容放电开关由STM32IO口代替;
检测电容触摸按键过程:
1、TPAD引脚设置为推拉输出,输出0,实现电容放电到0。
2、TPAD引脚设置为浮空输入(IO电容器在复位后开始放电。
3、同时开启TPAD引脚的输入捕获开始捕获。
4.等充电完成(充电到底)Vx,检测到上升边)。
5.计算充电时间。
二、程序设计思路
重要函数:
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)函数:初始化输入捕获通道
任何定时器都可以使用,M3用定时器5,M4用定时器2。
#define TPAD_ARR_MAX_VAL 0XFFFFFFFF vu16 tpad_default_val=0; u8 TPAD_Init(u8 psc) { u16 buf[10]; u16 temp; u8 i,j; 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 ) temp =buf[i]; 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; return 0; } void TPAD_Reset(void) { GPIO_InitTypeDef GPIO_InitABC; GPIO_InitABC.GPIO_Mode=GPIO_Mode_OUT; GPIO_InitABC.GPIO_OType=GPIO_OType_PP; GPIO_InitABC.GPIO_Pin=GPIO_Pin_5; GPIO_InitABC.GPIO_PuPd=GPIO_PuPd_DOWN; GPIO_InitABC.GPIO_Speed=GPIO_Speed_100MHz; GPIO_Init(GPIOA,&GPIO_InitABC); GPIO_ResetBits(GPIOA,GPIO_Pin_5); delay_ms(5); TIM_ClearITPendingBit(TIM2,TIM_IT_CC1|TIM_IT_Update); TIM_SetCounter(TIM2,0); GPIO_InitABC.GPIO_Mode=GPIO_Mode_AF; GPIO_InitABC.GPIO_OType=GPIO_OType_PP; GPIO_InitABC.GPIO_Pin=GPIO_Pin_5; GPIO_InitABC.GPIO_PuPd=GPIO_PuPd_NOPULL; GPIO_InitABC.GPIO_Speed=GPIO_Speed_100MHz; GPIO_Init(GPIOA,&GPIO_InitABC); } 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); } return TIM_GetCapture1(TIM2); } u16 TPAD_Get_MaxVal(u8 n) { u16 temp=0; u16 res=0; while(n--) { if(temp>res) res=temp; } return res; } #define TPAD_GATE_VAL 100 u8 TPAD_Scan(u8 mode) { static u8 keyen=0; u8 res=0; u8 sample=3; u16 rval; if(mode) { sample=6; keyen=0; } rval=TPAD_Get_MaxVal(sample); 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) { GPIO_InitTypeDef GPIO_InitABC; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitABC; TIM_ICInitTypeDef TIM_ICInitABC; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_TIM2); GPIO_InitABC.GPIO_Mode=GPIO_Mode_AF; GPIO_InitABC.GPIO_OType=GPIO_OType_PP; GPIO_InitABC.GPIO_Pin=GPIO_Pin_5; GPIO_InitABC.GPIO_PuPd=GPIO_PuPd_NOPULL; GPIO_InitABC.GPIO_Speed=GPIO_Speed_100MHz; GPIO_Init(GPIOA,&GPIO_InitABC); TIM_TimeBaseInitABC.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInitABC.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInitABC.TIM_Period=arr; TIM_TimeBaseInitABC.TIM_Prescaler=psc; TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitABC); TIM_ICInitABC.TIM_Channel=IM_Channel_1;
TIM_ICInitABC.TIM_ICFilter=0X00;
TIM_ICInitABC.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInitABC.TIM_ICPrescaler=TIM_ICPSC_DIV1;
TIM_ICInitABC.TIM_ICSelection=TIM_ICSelection_DirectTI;
TIM_ICInit(TIM2,&TIM_ICInitABC);
TIM_Cmd(TIM2,ENABLE);
}
int main(void)
{
u8 t=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_ms(168);
uart_init(115200);
LED_Init();
TPAD_Init(8);
while(1)
{
if(TPAD_Scan(0))
{
LED1=!LED1;
}
t++;
if(t==15)
{
t=0;
LED0=!LED0;
}
delay_ms(10);
}
}
STM32电容触摸实验