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

#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

#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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/665132.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用Prompt,轻松实现你的第一个全栈项目

前言 还有程序员没有应用过大模型技术吗&#xff1f;是工具也可以&#xff01;如果你还未使用过大模型技术&#xff0c;那么我劝你尽早行动&#xff0c;它会成为你开发的一大神器。如果你对大模型感兴趣&#xff0c;同时想使用大模型技术来开发产品&#xff0c;我接下来这个实…

【JavaEE】JVM中内存区域划分和类加载机制详解

一.初步了解JVM的基本 JVM 是 Java Virtual Machine 的简称&#xff0c;意为 Java虚拟机。是运行Java代码的核心部分&#xff0c;主要负责将Java字节码翻译为机器语言&#xff0c;并且提供了运行时的环境。JVM作为Java平台的一部分&#xff0c;隐藏了操作系统和硬件的差异性&am…

《SpringBoot3+Vue3实战》系列文章目录

前后端分离&#xff08;Frontend-Backend Separation&#xff09;是一种软件架构设计模式&#xff0c;它将传统的Web应用中的前端&#xff08;用户界面&#xff09;和后端&#xff08;服务器逻辑和数据存储&#xff09;从应用层面进行解耦&#xff0c;使得两者可以独立地开发、…

HTTP --tcp

TCP TCP连接 tcp/ip是全球计算机以及网络设备都在使用的一种常见的分组交换网络分层协议集&#xff0c;客户端可以打开一条tcp/ip连接&#xff0c;连接到可能运行在世界各地的服务器应用程序&#xff0c;一旦连接建立起来了&#xff0c;在客户端和服务器的计算机之间交换的报…

部署Envoy

Envoy常用术语 envoy文档官网 Life of a Request — envoy 1.31.0-dev-e543e1 documentationhttps://www.envoyproxy.io/docs/envoy/latest/intro/life_of_a_request#terminology 基础总结 &#xff08;1&#xff09;Envoy Envoy自己本身是工作在L7层的一个proxy&#xff…

知了汇智携手川农大,为计算机学子打造实战型综合项目实训

随着数字化产业的迅猛发展和产业数字化转型的不断深入&#xff0c;产业对数字人才的需求也在发生变化。为了培养适应市场需求的高素质应用型人才&#xff0c;5月24日&#xff0c;知了汇智携手四川农业大学&#xff0c;为信息工程学院计算机科学与技术专业22级学子带来一场兼具实…

NV link

NV link比PCIe有什么厉害的地方 NV link是并行总线 NV link是去CPU中心化的 NV link只针对GPU 实际上&#xff0c;PCIe依然会和NV link一起使用

2024年大屏幕互动源码+动态背景图和配乐素材+搭建教程

2024年大屏幕互动源码动态背景图和配乐素材搭建教程 php宝塔搭建部署活动现场大屏幕互动系统php源码 运行环境&#xff1a;PHPMYSQL 下载源码地址&#xff1a;极速云

【c++入门】this指针

this指针引出&#xff1a; 我们知道一个类可以有多个实例化对象&#xff0c;但是这多个实例化对象所调用的成员函数是在公共代码区&#xff1b; 我们先来定义一个Date类&#xff1a; class Date { public:void init(int year, int month, int day){_year year;_month month;…

在Ubuntu乌班图上安装Docker

最近在学习乌班图相关的内容&#xff0c;找了一些文档安装的都是报错的&#xff0c;于是记录一下学习过程&#xff0c;希望也能帮助有缘人&#xff0c;首先查看乌班图的系统版本&#xff0c;我的是如下的&#xff1a; cat /proc/version以下是在Ubuntu 20.04版本上安装Docker。…

excel怎么对非数字求和汇总?

如&#xff1a;学生小王的成绩为&#xff1a;A&#xff0c;A&#xff0c;A&#xff0c;A&#xff0c;B&#xff0c;B-……想得到的成绩汇总求和为&#xff1a;2A,2A,1B,1B- 如果在低版本里&#xff0c;用公式计算可能相当复杂&#xff0c;但是有了TEXTJOIN函数和UNIQUE函数&…

【Linux系列】深入解析 `kill` 命令:Linux 下的进程管理利器

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

JDK JRE JVM 三者的关系

总结&#xff1a; 1. jdk 中 的 javac 编译器将 .java 文件编译为 .class 字节码文件 &#xff08;编译&#xff09; 2. jre 执行 .class 字节码文件 &#xff08;运行&#xff09; 3. jre 通过 jvm 运行程序&#xff0c;确保程序能够在不同平台上正确执行&#xff08;实现跨平…

一文学懂Base64编码原理

前言 Base64编码与ASCII编码一样&#xff0c;也是一种编码方式。不同的是ASCII码采用7位二进制数表示&#xff08;包括大小写字母、数字、标点符号和一些不可见字符&#xff09;&#xff0c;而Base64采用6位二进制数表示&#xff08;包括大小写字母、0~9数字、和/&#xff09;…

越洗越黑”的Pandas数据清洗

引言 先来一个脑筋急转弯活跃一下枯燥工作日常&#xff0c;问&#xff1a;“什么东西越洗越黑&#xff1f;” 有没有猜到的&#xff1f;猜不到我告诉你吧&#xff01; 答案是“煤球”。那么这个脑机急转弯跟我们要讨论的话题有没有关系呢&#xff1f; 嗯是的&#xff0c;还是沾…

CUDA学习(1)

(一)CUDA简介 CUDA&#xff0c;全称Compute Unified Device Architecture&#xff0c;是由NVIDIA公司开发的一种计算平台和编程模型。它允许软件开发者和程序员使用NVIDIA的图形处理单元&#xff08;GPU&#xff09;来进行非常复杂的计算任务。简单来说&#xff0c;CUDA让普通…

安全风险 - 检测设备是否为模拟器

在很多安全机构的检测中&#xff0c;关于模拟器的运行环境一般也会做监听处理&#xff0c;有的可能允许执行但是会提示用户&#xff0c;有的可能直接禁止在模拟器上运行我方APP 如何判断当前 app 是运行在Android真机&#xff0c;还是运行在模拟器? 可能做 Framework 的朋友思…

DKTCDR:Domain-Oriented Knowledge Transfer for Cross-Domain Recommendation

Domain-Oriented Knowledge Transfer for Cross-Domain Recommendation IEEE(CCF B.SCI 1)-Guoshuai Zhao, Xiaolong Zhang, Hao Tang, Jialie Shen, and Xueming Qian-2024 思路 在CDR中,构建连接两个域的桥梁是实现跨域推荐的基础。然而现在的CDR方法往往在连接两个域时忽…

Usage - hackthebox

简介 靶场&#xff1a;hackmyvm 靶机&#xff1a;Usage(10.10.11.18) 难度&#xff1a;Easy 靶机链接:https://app.hackthebox.com/machines/Usage 攻击机1&#xff1a;ubuntu22.04 (10.10.16.21) 攻击机2&#xff1a;windows11(10.10.14.33) 扫描 nmap起手 nmap -sT …

身份认证与口令攻击

身份认证与口令攻击 身份认证身份认证的五种方式口令认证静态口令动态口令(一次性口令)动态口令分类 密码学认证一次性口令认证S/KEY协议改进的S/KEY协议 其于共享密钥的认证 口令行为规律和口令猜测口令规律口令猜测 口令破解操作系统口令破解Windows密码存储机制Windows密码破…