基于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:本文的代码参考正点原子