本文主要介绍电容按键的原理与使用方法,主要使用的ARM资源为捕获模块,并不涉及新的模块。所以本文内容不涉及新的HAL库内容的介绍。
关于捕获模块部分,可以参考以下三篇博客:
本文主要参考资料:
本实验的源代码如下所示:
https://github.com/zhenhaiyang/keil
本实验的主要功能为:
所以,相当于将按键TPAD与PA5直接短接。
电容器就是可以容纳电荷的器件,在两块金属板之间添加绝缘体就构成了最简答的电容器。而在PCB中,可以设计为一块带有上拉电阻的铜块,而其被接地的铜块包围住,这就构成了最简单的电容接触按键。当电路板的形状固定时,电容接触按键的容值也基本上是固定的。
此时,若将手指接触到PCB板,电容接触按键的容值就会改变,这是因为在金属板和手指之间又新增了一个等效的电容Cs。如下图所示:
综上,电容接触按键的原理就是:当手指接触到按键的实收,按键的等效电容就会增大。因此,其充电时间增加。
所以,本实验只需要检测电容按键的充电时间,即可判断手指是否接触按键。
具体来说,可以大致分成以下几个步骤:
/**
******************************************************************************
* @file main.c
* @author zhy
* @version 1.0
* @date 2021-04-26
* @brief 电容按键使用:
* 1.通过按下电容按键,改变LED1的状态
* 2.LED0按照1s的频率改变其状态
* 管脚分配:
* PA5:用于捕获电容充电时间
* 使用资源:
* TIM2CH1
******************************************************************************
*/
#include "stdio.h"
#include "stm32f4xx_hal.h"
#include "tpad.h"
#include "capture.h"
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
int main()
{
/* 1.变量初始化 */
TpadStatus tpadStatus = TPAD_HIGH;
uint32_t timeInMs = HAL_GetTick();
uint32_t timeNewMS = 0;
/* 2.硬件初始化 */
HAL_Init();
SystemClock_Config();
UartInit();
LedInit();
CaptureInit();
TpadInit();
/* 3.while循环 */
while (1)
{
timeNewMS = HAL_GetTick();
TpadScan(&tpadStatus);
if (tpadStatus == TPAD_RISING)
{
LED1 = !LED1;
}
if (timeNewMS - timeInMs >= 1000)
{
timeInMs = timeNewMS;
LED0 = !LED0;
}
}
}
主函数可以分成三个部分:
在硬件初始化中,CaptureInit()
初始化TIM2CH1。其初始化的方法与之前博客介绍的基本相同,不再详细介绍。TpadInit()
为电容触摸按键初始化,下文详细分析。
在while循环中,函数TpadScan()
将按键的处理结果通过传递到变量tpadStatus
中。若检测到上升沿,则将LED1置反。需要注意的是,此处的上升沿是逻辑上的上升沿指的是:按键由无效状态变成有效状态。然后通过Tick时钟判断当前时间,从而实现LED0的1s周期变化。
/**
* @brief 触摸按键初始化——将按键放电
* @note 无
* @param {*}无
* @retval 无
*/
void TpadInit(void)
{
/* 1.时钟初始化 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* 2.GPIO初始化 */
GPIO_InitTypeDef initGpio;
initGpio.Pin = GPIO_PIN_5; //pin5
initGpio.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
initGpio.Speed = GPIO_SPEED_FAST; //速度:快
HAL_GPIO_Init(GPIOA, &initGpio); //初始化PA5
/* 3.初始化 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); //PA5输出低电平
/* 4.获取为触摸时的电容充电时间 */
GetTimeUntoched();
}
该函数十分简单。首先,将PA5初始化为输出低电平的推挽输出。然后调用GetTimeUntoched()
函数来求取未接触按键时,按键的充电时间。
/**
* @brief 获取初始时间
* @note 无
* @param {*}无
* @retval 无
*/
void GetTimeUntoched(void)
{
int16_t temp[10] = {
0};
for (uint8_t i = 0; i < 10; i++)
{
GetTimeCharge((uint16_t *)(void *)temp + i);
}
int16_t min = 0;
int16_t max = 0;
int16_t sub = 0;
int16_t average = 0;
for (uint8_t i = 1; i < 10; i++)
{
sub = temp[i] - temp[0]; //获取差值
min = min > sub ? sub : min; //差值的最小值
max = max < sub ? sub : max; //差值的最大值
average += sub; //差值的和
}
average -= min + max; //剔除最小与最大值
average >>= 3; //差值的平均
timeUntouched = average + temp[0]; //实际的平均值
printf("timeUntouched:%d\n", timeUntouched); //输出未按下按键时的充电时间
}
通过调用函数GetTimeCharge()
来获取10次充电时间。默认认为在函数初始化的时候,手指没有触碰按键。然后通过10次求平均的方式,来获得比较准确的按键的充电时间。将其存储在全局变量timeUntouched
中,且通过串口输出。
/**
* @brief 获取捕获时间
* @note 无
* @param {uint32_t} *time 捕获时间
* @retval 无
*/
void GetTimeCharge(uint16_t *time)
{
/* PA5:推挽输出 */
GPIOA->MODER &= ~GPIO_MODER_MODE5;
GPIOA->MODER |= GPIO_MODER_MODE5_0;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_5;
/* 输出低电平 */
PAout.bit5 = 0;
delay_us(50);
/* PA5:复用为TIM2CH1 */
GPIOA->MODER &= ~GPIO_MODER_MODE5;
GPIOA->MODER |= GPIO_MODER_MODE5_1;
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL5;
GPIOA->AFR[0] |= GPIO_AFRL_AFRL5_0; //0b0001:AF1
TIM2->SR = 0x0000; //清空中断标志
TIM2->CR1 |= TIM_CR1_CEN; //打开计数器
while (!(TIM2->SR & TIM_SR_CC1IF)) //等待充电完成
;
TIM2->SR ^= TIM_SR_CC1IF; //清除中断标记
TIM2->CNT = 0; //清空计数器
TIM2->CR1 &= ~TIM_CR1_CEN; //关闭计数器
*time = TIM2->CCR1; //获取捕获值
printf("time:%d\n", *time); //发送时间值
}
该函数用于获取电容的充电时间。
/**
* @brief 判断按键是否有按下
* @note 无
* @param {TpadStatus} *tpadStatus 按键状态
* @retval 无
*/
void TpadScan(TpadStatus *tpadStatus)
{
static uint8_t timeHigh = 0;
uint16_t timeCharge = 0;
GetTimeCharge(&timeCharge);
if (timeCharge > timeUntouched * 5 >> 2) //若是平均未触碰充电时间的5/4,则判断是按键按下
{
timeHigh = 3;
switch (*tpadStatus)
{
case TPAD_LOW:
*tpadStatus = TPAD_RISING;
break;
case TPAD_RISING:
*tpadStatus = TPAD_HIGH;
break;
default:
break;
}
}
else //若检测到时间不足
{
*tpadStatus = TPAD_HIGH;
if (timeHigh > 0)
{
timeHigh--;
}
else
{
*tpadStatus = TPAD_LOW;
}
}
}
首先,判断充电时间是否为原有时间的5/4。若复合条件,则认为手指接触了按键,否则,则认为手指离开了按键。
若条件由不符合变为复合,则认为这是一个上升沿。否则,其处于高电平或者低电平。
变量timeHigh
相当于为滤波器,至少3次连续的条件不符合才认为其为真。这是为了防止按键抖动。