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上接出一块小的触摸板,这实验是可以做成功的,这没有什么疑问。

相关推荐
cwtlw4 分钟前
CSS学习记录20
前端·css·笔记·学习
汇能感知27 分钟前
光谱相机的工作原理
经验分享·笔记·科技·相机
紫罗兰盛开27 分钟前
分布式调度框架学习笔记
笔记·学习
汇能感知33 分钟前
光谱相机在农业中的具体应用案例
经验分享·笔记·科技
Kobebryant-Manba39 分钟前
kafka基本概念
分布式·学习·kafka
地球空间-技术小鱼1 小时前
YUM(Yellowdog Updater, Modified)和DNF(Dandified YUM)简介
linux·运维·服务器·笔记·学习
小码的头发丝、1 小时前
Java进阶学习笔记|面向对象
java·笔记·学习
坊钰2 小时前
【Java 数据结构】移除链表元素
java·开发语言·数据结构·学习·链表
Purple Coder2 小时前
第6章 图论
笔记
阿七想学习3 小时前
数据结构《排序》
java·数据结构·学习·算法·排序算法