1、系列目录
- 基本计时实验
- 输入捕获实验(实验3的基础)
- 电容按键检测实验
- 输出PWM实验
- PWM驱动无刷电机实验
2、电容按键检测原理
原理其实非常简单,通过TIM定时器的输入捕获功能判断电容的充电时间(没有触摸的充电时间短,有触摸的充电时间长)经过对比来确定是否有手指触碰。
图片来源:零死角玩转STM32(库函数版)刘火良由上面两个图片可以看出,没有手指触摸时电容为Cx,有手指触摸时电容为Cx+Cs
根据电容充电时间公式:
Vc=V0*(1-e(-t/RC))
明显可以看出有触摸的时间(t2)较长,所以在设计程序是只需要测量出无触摸的时间(t1),若充电时间超出一定范围即可判定为有手指触摸。
3、程序设计思路
- TIM定时器初始化(TIM5_Config)
- 电容按键初始化,并设置触摸阈值(TPAD_Init)
- 获取没有触摸的充电时间t0(TPAD_GetVal)
- 循环获取充电时间tx并将与t0对比(TAPD_GetMaxVal)
- 若tx超过触摸阈值,判定为有触摸(TPAD_Scan)
4、代码设计
(0)宏定义
#define TIM_GPIO_PORT GPIOA
#define TIM_GPIO_PIN GPIO_Pin_1
#define TIM_CHANNEL TIM_Channel_2
#define TIMx TIM5
#define TIMx_IRQn TIM5_IRQn
#define TIMx_PENDINGBIT TIM_IT_Update | TIM_IT_CC2
#define TPAD_ARR_MAX_VAL 0xFFFF
#define TPAD_GATE_VAL 100 //触摸阈值,ARR必须大于tpad_default_val + TPAD_GATE_VAL才可以被判断为有触摸
vu16 tpad_default_val = 0;//没有触摸时的计数器值
(1)TIM5_Config函数
//TIM5 通道2 PA1
void TIM5_Config(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//PA1 配置
GPIO_InitStructure.GPIO_Pin = TIM_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(TIM_GPIO_PORT,&GPIO_InitStructure);
printf("GPIO is ready\r\n");
//TIM5基础配置
//计时器时钟 = APB总线时钟/分频因子
// psc
TIM_TimeBaseStructure.TIM_Period = arr;//重装载值/最大计数值
TIM_TimeBaseStructure.TIM_Prescaler = psc;//预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIMx,&TIM_TimeBaseStructure);
//TIM5输入捕获配置
TIM_ICInitStructure.TIM_Channel = TIM_CHANNEL;//选择TIM5的CH1通道
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//上升沿捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//将IC1映射到IT1
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//信号分频值,不分频
TIM_ICInitStructure.TIM_ICFilter = 0x03;//8个定时器周期滤波
TIM_ICInit(TIMx,&TIM_ICInitStructure);
printf("TIM is ready\r\n");
TIM_Cmd(TIMx,ENABLE); //使能TIM5
printf("TIM begin\r\n");
}
(2)按键初始化
/** 触摸按键初始化程序
* 功能:获得没有触摸时的定时器取值
* 入口参数:psc,定时器初始化中的预分频值
* 返回值:0,初始化成功;1,初始化失败
*/
u8 TPAD_Init(u8 psc)
{
u16 buf[10];
u16 temp;
u8 i=0,j=0;
TIM5_Config( TPAD_ARR_MAX_VAL , psc-1);
printf("11\r\n");
for(i=0;i<10;i++)
{
buf[i] = TPAD_Get_Val();
delay_ms(10);
}
for(i=0;i<10;i++)
{
for(j=i+1;j<9;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];
}
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;
}
(3)触摸按键复位函数
/** 按键复位程序
* 功能:触摸按键复位
* 入口参数:无
* 返回值:无
*/
void TPAD_Reset(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = TIM_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(TIM_GPIO_PORT,&GPIO_InitStructure);
GPIO_ResetBits(TIM_GPIO_PORT,TIM_GPIO_PIN); //电容放电
delay_ms(5);
TIM_SetCounter(TIMx,0);
TIM_ClearITPendingBit(TIMx,TIM_IT_Update | TIM_IT_CC2);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(TIM_GPIO_PORT,&GPIO_InitStructure);//准备充电
}
(4)获取充电(触摸)时间
/** 获得TIMx的ARR值
* 功能:获取定时器捕获值
* 入口参数:无
* 返回值:计数器捕获值,若超过最大量程则返回最大值。
*/
u16 TPAD_Get_Val(void)
{
TPAD_Reset();
while(TIM_GetFlagStatus(TIMx,TIM_IT_CC2 == RESET))
{
if(TIM_GetCounter(TIMx)>TPAD_ARR_MAX_VAL-500)
{
return TIM_GetCounter(TIMx);
}
}
return TIM_GetCapture2(TIMx);
}
(5)获取最大触摸按键时间
/**
* 功能:获取最大值
* 入口参数: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;
}
(6)按键扫描函数
/** 按键扫描
* 功能:检测按键是否按下
* 入口参数:无
* 返回值:0,没有按下;1按下
*/
u8 TPAD_Scan(void)
{
static u8 keyen=0;
u8 res=0;
u8 sample=3;
u16 rval;
rval = TPAD_Get_MaxVal(sample);
if(rval > (tpad_default_val + TPAD_GATE_VAL))
{
if(keyen == 0)
res = 1;
keyen = 3;
}
if(keyen)
keyen--;
return res;
}
5、总结
这一次实验没有什么难度,就是输入捕获的一个实际应用,但是有一个问题我始终不明白,在TIM5_Config函数中,如果我开启了TIM5的计数中断和CC2溢出中断,程序就会跑死,删除这句后,程序也可以正常执行,不理解,各位大佬如果知道可以评论告诉我,谢谢~