STM32学习和实践笔记(32):电容触摸按键实验

**1.**电容触摸按键原理介绍

触摸按键与传统的机械按键相比,不仅美观而且耐用、寿命长,它颠覆了传统意义上的机械按键控制,只要轻轻触摸,就可以实现按键开关的控制、量化调节甚至方向控制。触摸按键已广泛应用于手机、DVD、洗衣机等消费类电子产品中。本章我们就介绍一种简单的触摸按键:电容式触摸按键。

我们PZ6806D开发板上的电容触摸按键其实就是一小块覆铜区域,也称之为触摸感应区。

通常我们会将四周的铜片与电路板地信号连通,触摸感应区设计成方便手指触摸大小,并将其连接在输入捕获通道上。

触摸感应区与四周的铜片区域就形成了一个电容,通过检测电容充放

电时间即可判断是否有触摸。实现原理:

电容充放电公式:Vc=V0*(1-e^(-t/RC))

在上图中,R是外接电阻,开关就是STM32管脚的内部开关。CS是触摸感应区与电路板GND之间的杂散电容。当手指按到触摸区时,等于增加了一个CX电容并到CS上,所以电源通过RC对电容的充电时间就会变长。

本实验中,使用TIM5的通道2(PA1管脚)来检测触摸按键是否按下。具体步骤是:

1)在每次检测前,我们需要先将电容Cs(或 Cs+Cx)放电,即配置PA1引脚为推挽输出模式,输出一个低电平,才能使电容放电。这等效于上图中的开关闭合。

2)然后配置PA1 为浮空输入模式,利用外部上拉电阻给电容 Cs(Cs+Cx)充电,同时开启TIM5_CH2的输入捕获,配置极性为上升沿,当检测到上升沿的时候,就认为电容充电完

成了,完成一次捕获检测。 每次系统重启时,我们执行一次捕获检测(可认为没有触摸),记录此时捕获到上升沿时,需要多少时间即TCS的值。

3)在后续的捕获检测中,即不断重复上面的第1步和第2步,我们就可以通过与记录的值进行对比,判断是否发生触摸。很明显,如果没有发生触摸,每次捕获发生的时间是基本上相等的,如果有触摸,那么时间必然明显延长。这样,就知道了是否发生触摸了

这就是电容触摸工作的原理!搞清楚了就很简单。

**2.**编写电容触摸按键控制程序

本实验所要实现的功能是:通过TIM5的通道2(PA1)捕获电容触摸按键输入信号的高电平脉宽,根据捕获到的高电平时间长短,来判断是否有按键按下,如果有按下,则翻转D2指示灯的状态以提示检测到了一次按下。同时D1指示灯不断闪烁表示系统正常运行。程序框架如下:

(1)初始化PA1管脚为TIM5通道2输入捕获功能,设置上升沿捕获等

(2)读取一次捕获高电平的值

(3)电容触摸按键初始化

(4)检测电容触摸按键是否按下

(5)编写主函数

main.c

cpp 复制代码
#include "system.h"
#include "led.h"
#include "SysTick.h"
#include "usart.h"
#include "input.h"
#include "touch_key.h"



int main()
{
	
	u8 i=0;
	SysTick_Init(72);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级分组
	LED_Init();
	USART1_Init(9600);
	Touch_Key_Init(6);//72M 6分频后12M
	
	while(1)
	{
		
		if((Touch_Key_Scan(0)==1))//判断触摸是否有效
		{
			led2=led2;//有效则翻转指示灯
		}
		
		
		i++;
		if(i%20 ==0)
		{
			led1=!led1;//LED1闪,用来指示主程序循环是否运行
			
		}
		delay_ms(50);
		
	}
}

touch_key.c

cpp 复制代码
#include "touch_key.h"
#include "SysTick.h"
#include "usart.h"

#define Touch_ARR_MAX_Value 0xffff
u16 touch_default_value =0;

void TIM5_CH2_Input_Init(u16 period,u16 prescaler)
{
	GPIO_InitTypeDef GPIO_InitStructure; 
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//结构体变量声明
	TIM_ICInitTypeDef TIM_ICInitStructure;

	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//使能TIM5时钟

	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1; 
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;	//
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	TIM_TimeBaseInitStructure.TIM_Period=period;   //装入函数传过来的自动装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler=prescaler; //装入函数传过来的分频系数
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//1分频(没有分频)
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式:从0开始计数到自动重载值后溢出产生中断
	TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);//初始化TIM5各参数:自动重装值、分频系统、计数方式等

	TIM_ICInitStructure.TIM_Channel=TIM_Channel_2; //通道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;//直接映射到TI1
	TIM_ICInit(TIM5,&TIM_ICInitStructure);
	
	TIM_Cmd(TIM5,ENABLE );//使能定时器
	
}

void Touch_Reset()
{
	GPIO_InitTypeDef GPIO_InitStructure; 
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1; 
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//先设为输出模式以方便输出低电平
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;	//
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);//将IO口输出低电平以将触摸按键的电容放电
	delay_ms(5);
	TIM_ClearFlag(TIM5,TIM_FLAG_Update|TIM_FLAG_CC2);//清除定时器的状态标志
	TIM_SetCounter(TIM5,0); //设定定时器初值为0以重新计数
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//重新改为浮空模式以对电容充电
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	

}



u16 Touch_Get_Value()
{
	Touch_Reset();
	while(TIM_GetFlagStatus(TIM5,TIM_FLAG_CC2)==0)//
	{
		if(TIM_GetCounter(TIM5)>Touch_ARR_MAX_Value-500)
		{
			return TIM_GetCounter(TIM5);//如果没有发生捕获事件,计数器计到最大值-500后返回当前计数器的值
		}
	}
	return TIM_GetCapture2(TIM5);//如果发生了捕获事件,则返回捕获发生时的计数器值
	
}

u8 Touch_Key_Init(u8 psc)
{
	u8 i,j;
	u16 buf[10];
	u16 temp;
	TIM5_CH2_Input_Init(Touch_ARR_MAX_Value,psc);//定时器5通道2输入捕获初始化
	for (i=0; i<10;i++)
	{
		buf[i]= Touch_Get_Value();//得到10个值
		delay_ms(10);
	}
	
	//将得到的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;
		}
		
	}
	
	//去掉最小的两个,去掉最大的两个,余下的6个取平均值
	temp=0;
	for(i=2; i<8;i++)
	{
		temp+=buf[i];
	}
	touch_default_value = temp/6;
	
	
	printf("touch_default_value=%d\r\n",touch_default_value);
	
	if(touch_default_value>touch_default_value/2)//这个判断条件是通过实际调试测试决定的
		return 1;//如果这个值大于最大值的一半,也就对应前面的没有发生捕获的情况,那么认为初始化失败
							//返回1 表明初始化失败
		
	return 0; //返回0表示初始化成功,得到了没有触摸时的缺省充电时间
	
}

u16 Touch_Get_MaxVal(u8 n)//得到n次捕获中的最大值
{
	u16 temp=0;
	u16 max=0;
	
	while(n--)
	{
		temp=Touch_Get_Value();
		if(temp>max)
			max=temp;
	}
	
	return max;
		
	
}

//mode =0 单次扫描,mode =1,连续扫描
//返回Touch_Status,其值为1则表示触摸有效
#define TOUCH_GATE_VAL 100
u8 Touch_Key_Scan(u8 mode)
{
	u8 Touch_Status;
	u8 sample =3;
	u16 MaxVal =0;
	static u8 keyen =0;
	
	if(mode)//mode如为1,则表示连续扫描,因此,每次调用Touch_Key_Scan时都将keyen=0,所以每次都能得到触摸值
	{				//反之,因为keyen后续=3,所以需要三次之后,才能降为0,才能得到触摸值
		sample =6;
		keyen=0;
	}
	
	
	MaxVal=Touch_Get_MaxVal(sample);//得到三次采样中的最大值
	
	if(MaxVal>(touch_default_value+TOUCH_GATE_VAL)&&MaxVal<(10*touch_default_value))
	{
		if((keyen==0)&&(touch_default_value+TOUCH_GATE_VAL))
		{
			Touch_Status=1;
		}
		
	}
	printf("触摸后捕获高电平值为:%d\r\n",MaxVal);
	keyen=3;
	if(keyen) keyen--;
	
	return Touch_Status;
	
}

touch_key.h

cpp 复制代码
#ifndef _touch_key_H
#define _touch_key_H

#include "system.h"

void TIM5_CH2_Input_Init(u16 period,u16 prescaler);
u8 Touch_Key_Init(u8 psc);
u8 Touch_Key_Scan(u8 mode);

#endif

这个程序因为只能在PZ6806D开发板上才可以运行,而我手上的是PZ6806L,所以没有实际烧录测试,但原理我是完全理解的。

因此,如果在PZ6806L上接出一块小的触摸板,这实验是可以做成功的,这没有什么疑问。

相关推荐
知识分享小能手1 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
汇能感知3 小时前
摄像头模块在运动相机中的特殊应用
经验分享·笔记·科技
阿巴Jun4 小时前
【数学】线性代数知识点总结
笔记·线性代数·矩阵
茯苓gao4 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾4 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT5 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa5 小时前
HTML和CSS学习
前端·css·学习·html
ST.J5 小时前
前端笔记2025
前端·javascript·css·vue.js·笔记
Suckerbin5 小时前
LAMPSecurity: CTF5靶场渗透
笔记·安全·web安全·网络安全
点灯小铭6 小时前
基于STM32单片机的智能粮仓温湿度检测蓝牙手机APP设计
stm32·单片机·智能手机·毕业设计·课程设计