STM32编写触摸按键
先看接线图,挺简单的,就用串口1,和两个led灯一个1MΩ电阻一个触摸导线(如果做电路板的话,就是一个手指大小的铺铜)
接下来我把几个主要的函数都整理了一下:
接下来挨个的整理一下:
void TIM2_CH2_Input_Init(u16 arr, u16 psc)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启TIM2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //开启GPIO的时钟
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; //PA0下拉输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct); //GPIO初始换
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = arr; //定时时间=(ARR+1)X(PSC+1) / 时钟频率
TIM_TimeBaseInitStruct.TIM_Prescaler = psc; //
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct); //时基单元初始换
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
TIM_ICInitStruct.TIM_ICFilter = 0;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM2,&TIM_ICInitStruct); //输入单元初始化
TIM_Cmd(TIM2,ENABLE);
}
/*
触摸复位函数:无返回值,无参数。
主要功能就是初始化pin1端口为推完输出模式,并低电平放电;放电5毫秒后清除中断标志位和计数器为0设置pin1端口为浮空输入模式
*/
void Touch_Reset(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_ResetBits(GPIOA,GPIO_Pin_1); //初始化pin1端口为推完输出模式,并低电平放电
Delay_ms(5);
TIM_ClearFlag(TIM2,TIM_IT_Update | TIM_IT_CC2);
TIM_SetCounter(TIM2,0);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStruct); //放电5毫秒后清除中断标志位和计数器为0设置pin1端口为浮空输入模式
}
/*
获取触摸捕获寄存器的值:返回16位的寄存器值,参数无。
本函数的主要功能就是获取从复位端口后到收到捕获标志时的寄存器的值,并返回
*/
u16 Touch_Get_Val(void)
{
Touch_Reset(); //触摸复位一下
while(TIM_GetFlagStatus(TIM2,TIM_FLAG_CC2) == 0) //如果没有捕获到上升沿
{
if(TIM_GetCounter(TIM2) >TOUCH_ARR_MAX_VAL - 500) // 如果计数器的值大于了我允许的最大值减500
{
return TIM_GetCounter(TIM2); //那就返回计数器的值
}
}
return TIM_GetCapture2(TIM2); //返回捕获寄存器2 的值
}
/*
电容触摸按键的初始化(其实就是对前面写的进行一个封装)
*/
u8 Touch_Key_Init(u8 psc)
{
u8 i = 0;
u8 j = 0;
u16 buf[10];
u32 temp = 0;
TIM2_CH2_Input_Init(TOUCH_ARR_MAX_VAL, psc);
for(i=0; i<10; i++)
{
buf[i]= Touch_Get_Val(); //读取10的值放到数组内,每次等待10毫秒
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];
}
touch_default_val = temp/6;
printf("此次捕获到的上升沿的值touch_default_val = %d微秒\r\n",touch_default_val);
if(touch_default_val > TOUCH_ARR_MAX_VAL/2) return 1;
return 0; //如果捕获到的值大于预定的阈值的一半,就认为无效返回1, 否则返回0有效
}
/*
获取触摸按键最大值得函数; 返回16位的最大值 参数:采集的次数
*/
u16 Touch_Get_MaxVal(u8 n)
{
u16 temp=0;
u16 res=0;
while(n--)
{
temp = Touch_Get_Val(); //捕获一次值放到临时变量里
if(temp > res) res = temp; //如果临时变量的值大于最大值,就把最大值更新为临时变量的值
}
return res;
}
/*
触摸按键的扫描函数;8位返回值 1:有触摸; 0:没有触摸 参数:是mode(单次扫描还是多次扫描)
*/
#define TOUCH_GATE_VAL 100 //有触摸时比没有触摸时的差值定为阈值
u8 Touch_Key_Scan(u8 mode)
{
u8 sample = 3; //扫描的次数
u16 rval = 0; //最大值
static u8 keyen=0;
u8 res=0;
if(mode)keyen=0; //如果mode是1就是连续扫描,否则keyen是1不能进入条件
rval = Touch_Get_MaxVal(sample); //扫描3次,获得最大值放到最大值内
if(rval > (touch_default_val + TOUCH_GATE_VAL) && rval < (touch_default_val * 10))
//如果刚刚采集到的值在初始化后得到的默认值得1到10倍之间
{
if((keyen == 0) && rval > touch_default_val) //如果keyen=0就只能进入一次,只有再一次的触摸才能进入
{
res=1;
}
printf("触摸后捕获的高电平时间为:%d 微秒\r\n",rval);
keyen = 1;
}
else keyen = 0;
return res;
}
到这里需要的几个函数就都介绍清除了,下面把整个文件展示一下(Touch_key.c):
#include "touch_key.h"
#define TOUCH_ARR_MAX_VAL 0xFFFF
u16 touch_default_val = 0; //默认采集到上升沿时存放的值
void TIM2_CH2_Input_Init(u16 arr, u16 psc)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启TIM2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //开启GPIO的时钟
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; //PA0下拉输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct); //GPIO初始换
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = arr; //定时时间=(ARR+1)X(PSC+1) / 时钟频率
TIM_TimeBaseInitStruct.TIM_Prescaler = psc; //
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct); //时基单元初始换
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
TIM_ICInitStruct.TIM_ICFilter = 0;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM2,&TIM_ICInitStruct); //输入单元初始化
TIM_Cmd(TIM2,ENABLE);
}
/*
触摸复位函数:无返回值,无参数。
主要功能就是初始化pin1端口为推完输出模式,并低电平放电;放电5毫秒后清除中断标志位和计数器为0设置pin1端口为浮空输入模式
*/
void Touch_Reset(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_ResetBits(GPIOA,GPIO_Pin_1); //初始化pin1端口为推完输出模式,并低电平放电
Delay_ms(5);
TIM_ClearFlag(TIM2,TIM_IT_Update | TIM_IT_CC2);
TIM_SetCounter(TIM2,0);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStruct); //放电5毫秒后清除中断标志位和计数器为0设置pin1端口为浮空输入模式
}
/*
获取触摸捕获寄存器的值:返回16位的寄存器值,参数无。
本函数的主要功能就是获取从复位端口后到收到捕获标志时的寄存器的值,并返回
*/
u16 Touch_Get_Val(void)
{
Touch_Reset(); //触摸复位一下
while(TIM_GetFlagStatus(TIM2,TIM_FLAG_CC2) == 0) //如果没有捕获到上升沿
{
if(TIM_GetCounter(TIM2) >TOUCH_ARR_MAX_VAL - 500) // 如果计数器的值大于了我允许的最大值减500
{
return TIM_GetCounter(TIM2); //那就返回计数器的值
}
}
return TIM_GetCapture2(TIM2); //返回捕获寄存器2 的值
}
/*
电容触摸按键的初始化(其实就是对前面写的进行一个封装)
*/
u8 Touch_Key_Init(u8 psc)
{
u8 i = 0;
u8 j = 0;
u16 buf[10];
u32 temp = 0;
TIM2_CH2_Input_Init(TOUCH_ARR_MAX_VAL, psc);
for(i=0; i<10; i++)
{
buf[i]= Touch_Get_Val(); //读取10的值放到数组内,每次等待10毫秒
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];
}
touch_default_val = temp/6;
printf("此次捕获到的上升沿的值touch_default_val = %d微秒\r\n",touch_default_val);
if(touch_default_val > TOUCH_ARR_MAX_VAL/2) return 1;
return 0; //如果捕获到的值大于预定的阈值的一半,就认为无效返回1, 否则返回0有效
}
/*
获取触摸按键最大值得函数; 返回16位的最大值 参数:采集的次数
*/
u16 Touch_Get_MaxVal(u8 n)
{
u16 temp=0;
u16 res=0;
while(n--)
{
temp = Touch_Get_Val(); //捕获一次值放到临时变量里
if(temp > res) res = temp; //如果临时变量的值大于最大值,就把最大值更新为临时变量的值
}
return res;
}
/*
触摸按键的扫描函数;8位返回值 1:有触摸; 0:没有触摸 参数:是mode(单次扫描还是多次扫描)
*/
#define TOUCH_GATE_VAL 100 //有触摸时比没有触摸时的差值定为阈值
u8 Touch_Key_Scan(u8 mode)
{
u8 sample = 3; //扫描的次数
u16 rval = 0; //最大值
static u8 keyen=0;
u8 res=0;
if(mode)keyen=0; //如果mode是1就是连续扫描,否则keyen是1不能进入条件
rval = Touch_Get_MaxVal(sample); //扫描3次,获得最大值放到最大值内
if(rval > (touch_default_val + TOUCH_GATE_VAL) && rval < (touch_default_val * 10))
//如果刚刚采集到的值在初始化后得到的默认值得1到10倍之间
{
if((keyen == 0) && rval > touch_default_val) //如果keyen=0就只能进入一次,只有再一次的触摸才能进入
{
res=1;
}
printf("触摸后捕获的高电平时间为:%d 微秒\r\n",rval);
keyen = 1;
}
else keyen = 0;
return res;
}
#ifndef _touch_key_H
#define _touch_key_H
#include "system.h"
#include "Delay.h"
#include "usart.h"
u8 Touch_Key_Init(u8 psc);
u8 Touch_Key_Scan(u8 mode);
#endif
接下来就是主函数了:
总体结果就是led1用来体现程序正在运行,不停的闪烁,led用来指示有触摸,还是没有触摸,串口不停地接收到触摸的计数值。