基于STM32F103ZET6主控平台实现电容触摸按键 [基于TIM5_CH2(PA1)的输入捕获]

一个热爱代码的工程师,唯有凭借双手不断敲打,才可以快速提升实力!

本文谨以记录,日后相忘时再作复习,代码没有贵贱,既来之则安之。

本次实验中我们将用 TIM5 的通道 2( PA1)来做输入捕获,并实现一个简单的电容触摸按键,通过该按键控制 DS1 的亮灭。

电容触摸按键是STM32F1开发板自带的一个模块,该模块的引脚与PA1相邻,故可以通过PA1的输入捕获来获取电容触摸按键产生的电压值,检测该值是否有效便可执行相应的操作。

贴代码:

tpad.h

#ifndef __TPAD_H
#define __TPAD_H
#include "sys.h"
#include "tim.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 TIM5_CH2_Cap_Init(u16 arr,u16 pcs);//定时器5通道2的初始化
#endif

tpad.c

#include "tpad.h"
#include "delay.h"
#include "stdio.h"
//复位一次
//释放电容电量,并清除定时器的计数值
void TPAD_Reset(void)  //重置触摸区域
{
	GPIOA->CRL&=0XFFFFFF0F; //PA1 输入
	GPIOA->CRL|=0X00000030; //推挽输出
	GPIOA->ODR&=~(1<<1); //输出 0,放电
	delay_ms(5);
	TIM5->SR=0; //清除标记
	TIM5->CNT=0; //归零
	GPIOA->CRL&=0XFFFFFF0F; //PA1 输入
	GPIOA->CRL|=0X00000040; //浮空输入
}

#define TPAD_ARR_MAX_VAL 0XFFFF //最大的 ARR 值 
vu16 tpad_default_val=0;//空载的时候(没有手按下),计数器需要的时间
//初始化触摸按键
//获得空载的时候触摸按键的取值.
//systick:系统时钟频率
//返回值:0,初始化成功;1,初始化失败
u8 TPAD_Init(u8 systick) //触摸区域的初始化
{
	u16 buf[10];
	u16 temp;
	u8 j,i;
	TIM5_CH2_Cap_Init(TPAD_ARR_MAX_VAL,systick-1);//以 1Mhz 的频率计数
	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];//取中间的 8 个数据进行平均
	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;
}
//得到定时器捕获值
//如果超时,则直接返回定时器的计数值.
//返回值:捕获值/计数值(超时的情况下返回)
u16 TPAD_Get_Val(void) //获取触摸区域的电压值
{
	TPAD_Reset();
	while((TIM5->SR&0X04)==0)//等待捕获上升沿
	{
		if(TIM5->CNT>TPAD_ARR_MAX_VAL-500)return TIM5->CNT;//超时了
	};
	return TIM5->CCR2;
}
//读取 n 次,取最大值
//n:连续获取的次数
//返回值: n 次读数里面读到的最大读数值
u16 TPAD_Get_MaxVal(u8 n)//获取最大的电压值
{
	u16 temp=0;
	u16 res=0;
	while(n--)
	{
		temp=TPAD_Get_Val();//得到一次值
		if(temp>res)res=temp;
	};
	return res;
}

//扫描触摸按键
//mode:0,不支持连续触发(按一次必须松开才能按下一次);1,支持连续触发(可一直按下)
//返回值:0,没有按下;1,有按下; 
#define TPAD_GATE_VAL 100      //门限值,必须大于 tpad_default_val+TPAD_GATE_VAL
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+TPAD_GATE_VAL))
	//大于 tpad_default_val+TPAD_GATE_VAL,有效
	{
		if(keyen==0)res=1; //keyen==0,有效
		//printf("r:%d\r\n",rval);   
		keyen=3; //至少要再过 3 次之后才能按键有效
	}
	if(keyen)keyen--;
	return res;
}
void TIM5_CH2_Cap_Init(u16 arr,u16 pcs)//定时器5通道2的初始化
{
	RCC->APB1ENR|=1<<3;//TIM5 时钟使能
	RCC->APB2ENR|=1<<2;//使能 PORTA 时钟
	GPIOA->CRL&=0XFFFFFF0F;//PA0 清除之前设置
	GPIOA->CRL|=0X00000040;//PA0 浮空输入
	TIM5->ARR=arr;//设定计数器自动重装值
	TIM5->PSC=pcs;//预分频器
	TIM5->CCMR1|=1<<8;  //CC2S=01 选择输入端 IC2 映射到 TI2 上
	TIM5->CCMR1|=0<<12; //IC2F=0011 配置输入滤波器 8 个定时器时钟周期滤波
	TIM5->CCMR1|=0<<10; //IC2PS=00 配置输入分频,不分频
	TIM5->CCER|=0<<5;   //CC2P=0 上升沿捕获
	TIM5->CCER|=1<<4;   //CC2E=1 允许捕获计数器的值到捕获寄存器中
	TIM5->CR1|=0x01;    //使能定时器 5
}

main.c

#include "sys.h"
#include "usart.h"		
#include "delay.h"
#include "led.h"
#include "beep.h"
#include "key.h"
#include "exit.h"
#include "iwdg.h"
#include "tim.h"
#include "tpad.h"
void Input_While(void);
u8 Wl_stat=0;//while标志位

int main(void)
{		
	u32 temp=0;
	Stm32_Clock_Init(9);	  //系统时钟设置
	delay_init(72);	  		  //延时初始化
	uart_init(72,115200); 	//串口初始化为115200
	LED_Init();
	TPAD_Init(6);
	while(1)
	{
		if(TPAD_Scan(0))//成功捕获到了一次上升沿(此函数执行时间至少15ms)
		{
			LED0=!LED0;
		}
		temp++;
		if(temp==15)
		{
			temp=0;
			LED1=!LED1;
		}
		delay_ms(100);
	}
} 

void Input_While(void)
{
		if(Wl_stat==0)//代表进入WHILE循环
		{
			printf("Input While(1)-->\r\n");
			Wl_stat=1;
		}
}

该实验的大体流程:

1.TPAD_Init(6);初始化,6为分频系数-->对应频率为12MHz

-->TIM5_CH2_Cap_Init(TPAD_ARR_MAX_VAL,systick-1);//TIM5_CH2输入捕获初始化

2.TPAD_Scan(0);以不支持连续触摸模式在main函数的while循环中持续扫描触摸按键是否有触摸.

-->rval=TPAD_Get_MaxVal(sample);获取触摸按键最大值

---->temp=TPAD_Get_Val();//得到一次值

------>TPAD_Reset();//释放电容电量,并清除定时器的计数值

------>捕获上升沿,获取TIM5->CCR2 获值/计数值(如果超市直接返回计数值)

-->利用捕获到的触摸按键最大值与tpad_default_val+TPAD_GATE_VAL作比较,大于则为有效

3.TPAD_Scan(0);若该返回值为true,则可以根据此返回值进行一系列的操作。

编译通过后,烧录进STM32F103ZET6开发板,实现程序设计效果即可。

效果:【在完成软件设计之后,将我们将编译好的文件下载到精英 STM32 V1 上, 可以看到 DS0 慢速闪烁,此时,我们用手指触摸 ALIENTEK 精英 STM32F103 开发板上的 TPAD(右下角的白色头像),就可以控制 DS1 的亮灭了。不过你要确保 TPAD 和 ADC 的跳线帽连接上了哦!】

想太多,做太少,怎改变自己

谢谢大家的关注和支持,来自一个嵌入式软硬件工程师的内心情感!

PS:本文的代码参考正点原子

猜你喜欢

转载自blog.csdn.net/weixin_41586634/article/details/82839918