当前位置: 首页 > news >正文

18.【NXP 号令者RT1052】开发——实战-电容触摸按键

18.【NXP 号令者RT1052】开发——实战-电容触摸按键

上一章,我们介绍了 RT1052 的输入捕获功能及其使用。这一章,我们将向大家介绍如何通过输入捕获功能,来做一个电容触摸按键。在本章中,我们将用 QTMR3 的通道 2(P118)来做输入捕获,并实现一个简单的电容触摸按键,通过该按键控制 DS1 的亮灭。

18.1 电容触摸按键简介

18.1.1 电容触摸按键原理简介

电容式触摸按键利用手指与覆铜间形成的寄生电容变化实现检测,寿命长、省空间、免开孔,已成为手机等轻薄设备的主流方案。

本章通过号令者 RT1052 的 TSI 模块读取板载 TPAD 覆铜电容值,一旦检测到触摸即点亮 DS1,手指离开则熄灭,全程仅需一块铜箔即可实现稳定可靠的触摸开关。
在这里插入图片描述
这里我们使用的是检测电容充放电时间的方法来判断是否有触摸,图中 R 是外接的电容充电电阻,Cs 是没有触摸按下时 TPAD 与 PCB 之间的杂散电容。而 Cx 则是有手指按下的时候,手指与 TPAD 之间形成的电容。图中的开关是电容放电开关(由实际使用时,由 RT1052 的 IO代替)。

先用开关将 Cs(或 Cs+Cx)上的电放尽,然后断开开关,让 R 给 Cs(或 Cs+Cx)充电,当没有手指触摸的时候,Cs 的充电曲线如图中的 A 曲线。而当有手指触摸的时候,手指和 TPAD之间引入了新的电容 Cx,此时 Cs+Cx 的充电曲线如图中的 B 曲线。从上图可以看出,A、B 两种情况下,Vc 达到 Vth 的时间分别为 Tcs 和 Tcs+Tcx。

其中,除了 Cs 和 Cx 我们需要计算,其他都是已知的,根据电容充放电公式:

Vc=V0*(1-e^(-t/RC))

其中 Vc 为电容电压,V0 为充电电压,R 为充电电阻,C 为电容容值,e 为自然底数,t 为充电时间。根据这个公式,我们就可以计算出 Cs 和 Cx。

在本章中,其实我们只要能够区分 Tcs 和 Tcs+Tcx,就已经可以实现触摸检测了,当充电时间在 Tcs 附近,就可以认为没有触摸,而当充电时间大于 Tcs+Tx 时,就认为有触摸按下(Tx 为检测阀值)。

本章,我们使用 P118(GPIO1_IO18,即 QTMR3_CH2))来检测 TPAD 是否有触摸,在每次检测之前,我们先配置 P118 为推挽输出,输出低电平,将电容 Cs(或 Cs+Cx)放电,然后配置 P118 为浮空输入,利用外部上拉电阻给电容 Cs(Cs+Cx)充电,同时开启 QTMR3_CH2 的输入捕获,检测上升沿,当检测到上升沿的时候,就认为电容充电完成了,完成一次捕获检测。

在 MCU 每次复位重启的时候,我们执行一次捕获检测(可以认为没触摸),记录此时的值,记为 tpad_default_val,作为判断的依据。在后续的捕获检测,我们就通过与 tpad_default_val 的对比,来判断是不是有触摸发生

18.1.2 四定时器输入捕获原理简介

关于四定时器(QTMR)我们在第十五章和第十六章有过一些介绍,RT1052 内部包含了 4个四定时器,每个四定时器又有 4 个通道,每个通道又可以作为 PWM 输出或者作为输入捕获。
本章,我们就是利用四定时器 3 的通道 2(QTMR3_CH2)来做输入捕获。
在这里插入图片描述图中 CNTR 是 QTMR3 的计数器,向上计数模式,范围为 0~65535;CAPT1 是没触摸按下时的输入捕获值;CAPT2 是有触摸按下时的输入捕获值;Vth 为 RT1052 IO 口的高电平阈值,查数据手册可知为:0.7NVCC_GPIOx=0.73.3=2.31V ,QTMR3 定时器工作在上升沿捕获模式,只要检测到通道 2(TPAD)有上升沿,就立即读取 CAPT 的值,以此为依据来判定是否有电容触摸按键按下了。
没触摸按下时,识别过程为:首先控制捕获 IO 口(P118)为推挽输出,然后输出低电平,给 TPAD 和 PCB 之间的杂散电容(Cs)放电,让 TPAD 电压恢复低电平,并重置 CNTR 计数器(t1 时刻),然后设置捕获 IO 为浮空输入并立即启动定时器(工作在上升沿捕获模式),然后等待 IO 口变为高电平(电压≥Vth,t2 时刻),此时可以将捕获到的值(记为 CAPT1)保存起来,完成一次检查,再经过一段时间的充电,TPAD 的电压将达到 3.3V(t3 时刻)。
有触摸按下时,识别过程为:首先控制捕获 IO 口(P118)为推挽输出,然后输出低电平,给手指+TPAD 和 PCB 之间的杂散电容(Cx)放电,让 TPAD 电压恢复低电平,并重置 CNTR计数器(t5 时刻),然后设置捕获 IO 为浮空输入并立即启动定时器(工作在上升沿捕获模式),然后等待 IO 口变为高电平(电压≥Vth,t6 时刻),此时可以将捕获到的值(记为 CAPT2)保存起来,完成一次检查。
因为有手指按下 TPAD 时,杂散电容(Cx)大于没按下时的(Cs),因此有触摸时,TPAD的上升沿会缓慢一些,从而 CAPT2 也会大一些。因此可以通过 CAPT2 和 CAPT1 的比较,来识别是否有触摸按键按下,这就是电容式触摸按键的工作原理。


RT1052 QTIMER3 输入捕获配置步骤

1. 设置 GPIO1_IO18 复用功能

IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_02_QTIMER3_TIMER2,0); 
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_02_QTIMER3_TIMER2,0x10C1);

2. 使能 QTIMER3 时钟

CLOCK_EnableClock(kCLOCK_Timer3);

此函数会被 QTMR_Init 调用,无需显式调用。


3. 初始化 QTIMER3

qtmr_config_t qtimer3_config;
QTMR_GetDefaultConfig(&qtimer3_config); //先设置为默认配置
qtimer3_config.primarySource=kQTMR_ClockDivide_4; //设置第一时钟源
QTMR_Init(TMR3,kQTMR_Channel_2,&qtimer3_config); //初始化 QTIMER

这里选择 IPG_CLK_ROOT 的 4 分频作为 QTIMER3 第一时钟源。


4. 设置 QTIMER3_TIMER2 为输入捕获

void QTMR_SetupInputCapture(TMR_Type * base,qtmr_channel_selection_t channel,qtmr_input_source_t capturePin,bool inputPolarity,bool reloadOnCapture,qtmr_input_capture_edge_t captureMode)
输入源枚举
typedef enum _qtmr_input_source
{kQTMR_Counter0InputPin = 0, //counter0 输入引脚kQTMR_Counter1InputPin,     //counter1 输入引脚kQTMR_Counter2InputPin,     //counter2 输入引脚kQTMR_Counter3InputPin      //counter3 输入引脚
} qtmr_input_source_t;
捕获边沿枚举
typedef enum _qtmr_input_capture_edge
{kQTMR_NoCapture = 0,       //关闭捕获kQTMR_RisingEdge,          //上升沿捕获kQTMR_FallingEdge,         //下降沿捕获kQTMR_RisingAndFallingEdge //双边沿捕获
} qtmr_input_capture_edge_t;
示例代码
QTMR_SetupInputCapture(TMR3,kQTMR_Channel_2,kQTMR_Counter2InputPin,0,0,kQTMR_RisingEdge);

5. 开启 QTIMER3

//通道 0 在 primary 时钟源的上升沿计数
QTMR_StartTimer(TMR3, kQTMR_Channel_2, kQTMR_PriSrcRiseEdge); 

18.2 硬件设计

本实验用到的硬件资源有:

  1. 指示灯 DS0 和 DS1
  2. 定时器 QTMR3
  3. 触摸按键 TPAD
    前面两个之前均有介绍,我们需要通过 QTMR3_TIMER2(P118)采集 TPAD 的信号,所以本实验需要用跳线帽短接多功能端口(P11)的 TPAD 和 ADC,以实现 TPAD 连接到 P118。

在这里插入图片描述
在这里插入图片描述硬件设置(用跳线帽短接多功能端口的 ADC 和 TPAD 即可)好之后,下面我们开始软件设计.

18.3 软件设计

软件设计我们在之前的工程上面增加,在 HARDWARE 文件夹下新建 TPAD 的文件夹。然后打开 USER 文件夹下的工程,新建一个 tpad.c 的文件和 tpad.h 的头文件,保存在 TAPD 文件夹下,并将 TPAD 文件夹加入头文件包含路径.
tpad.c

#include "tpad.h"
#include "delay.h"		    
#include "lpuart.h"#define TPAD_CAP_MAX_VAL 0XFFFF			//捕获最大值
vu16 tpad_default_val=0;				//空载的时候(没有手按下),计数器需要的时间//初始化触摸按键
//获得空载的时候触摸按键的取值.
//psc:分频系数,越小,灵敏度越高.
//返回值:0,初始化成功;1,初始化失败
u8 TPAD_Init(u8 psc)
{u16 buf[10];u16 temp;u8 j,i;QTMR3_CH2_CAP_Init(psc);	//设置分频系数for(i=0;i<10;i++)			//连续读取10次{				 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];//取中间的6个数据进行平均tpad_default_val=temp/6;printf("tpad_default_val:%d\r\n",tpad_default_val);	if(tpad_default_val>TPAD_CAP_MAX_VAL/2)return 1;//初始化遇到超过TPAD_ARR_MAX_VAL/2的数值,不正常!return 0;		     	    					   
}//复位一次
//释放电容电量,并清除定时器的计数值
void TPAD_Reset(void)
{gpio_pin_config_t tpad_config;	IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_02_GPIO1_IO18,0);	  IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_02_GPIO1_IO18,0x10B0);tpad_config.direction=kGPIO_DigitalOutput;	//输出tpad_config.interruptMode=kGPIO_NoIntmode;	//不使用中断功能tpad_config.outputLogic=1;					//默认高电平GPIO_PinInit(GPIO1,18,&tpad_config); 	    //初始化GPIO1_3GPIO_PinWrite(GPIO1,18,0);                  //GPIO1_IO18输出0,放电delay_ms(5);QTMR_ClearStatusFlags(TMR3,kQTMR_Channel_2,kQTMR_EdgeFlag); //清除边沿捕获标记TMR3->CHANNEL[kQTMR_Channel_2].CNTR=0;			            //归零    //配置QTIMER_TIMER3相关IO(GPIO_AD_B1_02)的功能IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_02_QTIMER3_TIMER2,0);	  IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_02_QTIMER3_TIMER2,0x10C1);
}//得到定时器捕获值
//如果超时,则直接返回定时器的计数值.
//返回值:捕获值/计数值(超时的情况下返回)
u16 TPAD_Get_Val(void)
{				   TPAD_Reset();while((QTMR_GetStatus(TMR3,kQTMR_Channel_2)&(kQTMR_EdgeFlag))!=kQTMR_EdgeFlag)//等待捕获上升沿{if(TMR3->CHANNEL[kQTMR_Channel_2].CNTR>TPAD_CAP_MAX_VAL-500)return TMR3->CHANNEL[kQTMR_Channel_2].CNTR;//超时了,直接返回CNT的值};	return TMR3->CHANNEL[kQTMR_Channel_2].CAPT;	  
} 	//读取n次,取最大值
//n:连续获取的次数
//返回值:n次读数里面读到的最大读数值
u16 TPAD_Get_MaxVal(u8 n)
{ u16 temp=0; u16 res=0; u8 lcntnum=n*2/3;//至少2/3*n的有效个触摸,才算有效u8 okcnt=0;while(n--){temp=TPAD_Get_Val();//得到一次值if(temp>(tpad_default_val*5/4))okcnt++;//至少大于默认值的5/4才算有效if(temp>res)res=temp;}if(okcnt>=lcntnum)return res;//至少2/3的概率,要大于默认值的5/4才算有效else return 0;
}  
//扫描触摸按键
//mode:0,不支持连续触发(按下一次必须松开才能按下一次);1,支持连续触发(可以一直按下)
//返回值:0,没有按下;1,有按下;										  
u8 TPAD_Scan(u8 mode)
{static u8 keyen=0;	//0,可以开始检测;>0,还不能开始检测	 u8 res=0;u8 sample=3;	//默认采样次数为3次	 u16 rval;if(mode){sample=6;	//支持连按的时候,设置采样次数为6次keyen=0;	//支持连按	  }rval=TPAD_Get_MaxVal(sample); if(rval>(tpad_default_val*4/3)&&rval<(10*tpad_default_val))//大于tpad_default_val+(1/3)*大于tpad_default_val,且小于10倍tpad_default_val,则有效{							 if(keyen==0)res=1;	//keyen==0,有效 //printf("r:%d\r\n",rval);		     	    					   keyen=3;				//至少要再过3次之后才能按键有效   } if(keyen)keyen--;		   							   		     	    					   return res;
}	 //初始化QTMR3定时器CH2 输入捕获 
//prisrc : 第一时钟源选择
//         0000~0011,通道0~3的输入引脚.
//         0100~0111,通道0~3的输出.可用于级联.
//         1000~1111,IPG_CLK_ROOT时钟的:1,2,4,8,16,32,64,128分频.
//捕获计时频率=QTMR3_CLK=IPG_CLK_ROOT/2^(prisrc-8); 
//假设prisrc=1011,则QTMR3_CLK=IPG_CLK_ROOT/8=18.75MHz. 
void QTMR3_CH2_CAP_Init(u8 prisrc)
{qtmr_config_t qtimer3_config;qtmr_primary_count_source_t qtimer3_source;u32 ipgclk=CLOCK_GetFreq(kCLOCK_IpgClk);     //获取IPG_CLK=150Mhzqtimer3_source=(qtmr_primary_count_source_t)prisrc;//IO设置    IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_02_QTIMER3_TIMER2,0);	  IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_02_QTIMER3_TIMER2,0x10C1);//IOMUXC->SELECT_INPUT[kIOMUXC_QTIMER3_TIMER2_SELECT_INPUT]=1;	//选择TMR3_CH2的输入接到GPIO_AD_B1_02(GPIO1_IO18,ALT1)QTMR_GetDefaultConfig(&qtimer3_config);             //先设置为默认配置,后面在根据实际情况配置qtimer3_config.primarySource=qtimer3_source;        //设置第一时钟源QTMR_Init(TMR3,kQTMR_Channel_2,&qtimer3_config);    //初始化QTIMER//设置输入捕获QTMR_SetupInputCapture(TMR3,kQTMR_Channel_2,kQTMR_Counter2InputPin,0,0,kQTMR_RisingEdge);//开始通道0QTMR_StartTimer(TMR3, kQTMR_Channel_2,kQTMR_PriSrcRiseEdge);    //通道0在primary时钟源的上升沿计数 
}

此部分代码包含 6 个函数,接下来我们分别介绍这几个函数。
TPAD_Init 函数,用于初始化输入捕获,并获取默认的 TPAD 值。该函数有一个参数,用来设置 QTMR3 的第一时钟源,其实是为了配置 QTMR3_CH2_CAP_Init 的计数频率。在该函数中连续 10 次读取 TPAD 值,将这些值升序排列后取中间 6 个值再做平均(这样做的目的是尽量减少误差),并赋值给 tpad_default_val,用于后续触摸判断的标准。

TPAD_Reset 函数,用于复位 TPAD,其实就是为了给 TPAD 的杂散电容放电,以便启动下一次测量。该函数先设置 P118 为推挽输出,然后输出低电平,延时 5ms,充分放电。然后再清除 QTMR3 通道 2 的捕获标志,并清零其计数器,最后配置 P118 为浮空输入,以便后续检测。

TPAD_Get_Val 函数,用于获取一次捕获值,相当于扫描一次 TPAD 按键。该函数先复位TPAD,然后等待捕获上升沿,并返回捕获值。

TPAD_Get_MaxVal 函数,用于连续获取 n 次捕获值,如果其中 2/3 的概率获取的值大于默认值的 5/4,则返回 n 次捕获里面的最大值。否则返回 0,视为无效。这里的 2/3 和 5/4 是自己定义的,大家可以根据自己的需要,进行修改。

TPAD_Scan 函数,用于扫描 TPAD 是否有触摸,该函数的参数 mode,用于设置是否支持连续触发。返回值如果是 0,说明没有触摸,如果是 1,则说明有触摸。该函数包含了一个静态变量,用于检测控制,类似第七章的 KEY_Scan 函数。所以该函数同样是不可重入的。在函数中,我们通过连续读取 3 次(不支持连续按的时候)TPAD 的值,取这他们的最大值,和tpad_default_val4/3 比较(且必须小于 10tpad_default_val),如果大于则说明有触摸,如果小于,则说明无触摸。其中 tpad_default_val 是我们在调用 TPAD_Init 函数的时候得到的值,然后取其 4/3 为门限值。该函数,我们还做了一些其他的条件限制,让触摸按键有更好的效果,这个就请大家看代码自行参悟了。

QTMR3_CH2_CAP_Init 函数,用于初始化四定时器 3 的通道 2,用作输入捕获功能,其初始化步骤和我们 18.1.2 节介绍的步骤一样。最终配置 QTMR3 的通道 2 作为上升沿捕获。

tpad.h

在这里插入代码片#ifndef __TPAD_H
#define __TPAD_H
#include "sys.h"	//空载的时候(没有手按下),计数器需要的时间
//这个值应该在每次开机的时候被初始化一次
extern vu16 tpad_default_val;void TPAD_Reset(void);
u16  TPAD_Get_Val(void);
u16  TPAD_Get_MaxVal(u8 n);
u8   TPAD_Init(u8 systick);
u8   TPAD_Scan(u8 mode);
void QTMR3_CH2_CAP_Init(u8 prisrc);    
#endif

main.c

#include "sys.h"
#include "lpuart.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "tpad.h" int main(void)
{u8 t=0;MPU_Memory_Protection();    //初始化MPURT1052_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);//中断优先级分组4RT1052_Clock_Init();	    //配置系统时钟DELAY_Init(600);		    //延时函数初始化LPUART1_Init(115200);       //初始化串口1LED_Init();				    //初始化LED  KEY_Init();                 //初始化按键TPAD_Init(10); 	            //初始化触摸按键,以150/1=150Mhz频率计数while(1){					  						  		 if(TPAD_Scan(0))		//成功捕获到了一次上升沿(此函数执行时间至少15ms){LED1_Toggle;	    //LED1取反}t++;if(t==15)		 {t=0;LED0_Toggle;	    //LED0取反,提示程序运行}delay_ms(10);}
}

该 main 函数比较简单,TPAD_Init(10)函数执行之后,就开始触摸按键的扫描,当有触摸的时候,对 DS1 取反,而 DS0 则有规律的间隔取反,提示程序正在运行。注意在修改 main 函数之后,还需要在 test.c 里面添加 tpad.h 头文件,否则会报错哦。
这里还要提醒一下大家,不要把 lpuart1_init(115200)去掉,因为在 TPAD_Init 函数里面,我们有用到 printf,如果你去掉了 uart_init,就会导致 printf 无法执行,从而死机。
至此,我们的软件设计就完成了

编译,下载!

同时大家可以打开串口调试助手,每次复位的时候,会收到 tpad_default_val 的值,一般
为 74 左右。

总结

电容触摸按键的原理是利用手指靠近或触摸电极时引入额外电容,使得电极与电阻形成的充放电时间发生变化;通过定时器输入捕获检测电容充电达到阈值电压的时间差,就能区分是否有触摸发生,从而实现按键功能。
OK!谢谢大家!

http://www.dtcms.com/a/613694.html

相关文章:

  • 三种硬盘检测工具推荐CrytalDiskMark ,DiskGenius,AS SSD Benchmark
  • 解密VQVAE中的Codebook
  • Qt Widgets 模块中的函数详解
  • 怎样才能被百度秒收录我的网站(百度不收录网站怎么办)
  • nginx-file-server
  • 18.PHP基础-递归递推算法
  • 郑州软件开发公司网站广西医院的网站建设
  • 费县做网站职业教育专业建设验收网站
  • 第五章 防火墙设备互联
  • 建导航网站seo企业优化顾问
  • 2025.11.15 力扣每日一题
  • LeetCode算法日记 - Day 104: 通配符匹配
  • RDMA内存保护概念---MR,PD
  • 11月13号作业
  • 怎样建立网站目录结构炒股网站开发
  • 【STM32MP157 异核通信框架学习篇】(10)Linux下Remoteproc相关API (上)
  • 东莞企业建站平台中企动力 做网站 怎么样
  • 虚拟机的未来:从云计算到量子模拟
  • 前端响应式设计资源,框架+模板
  • 品牌网站建设服务网络品牌塑造
  • C语言编译系统 | 高效编译与优化技术分析
  • L2层差错控制与HARQ协议介绍
  • 4. Qt深入 线程和QObject
  • 印尼游戏出海合规指南:法律框架、税务政策与运营挑战
  • 【Java Web学习 | 第11篇】JavaScript(5)BOM
  • 打造您专属的高效DNS解析器:RethinkDNS
  • 网上书店网站建设方案策划如何建设好一个网站
  • Spring Framework 中文官方文档
  • 深度剖析 C++ vector的底层实现
  • USDe:站在稳定币、永续化与资产代币化三大趋势交汇点的新型美元