
本文共 5501 字,大约阅读时间需要 18 分钟。
文章目录
前言
本文主要介绍电容按键的原理与使用方法,主要使用的ARM资源为捕获模块,并不涉及新的模块。所以本文内容不涉及新的HAL库内容的介绍。
关于捕获模块部分,可以参考以下三篇博客:
本文主要参考资料:
- 刘火良,杨森.STM32库开发实战指南——基于STM32F4.机械工业出版社
本实验的源代码如下所示:
本实验的主要功能为:
- 通过电容按键改变LED1的状态。每次点击电容按键,LED的状态置反。
- LED0按照1s的周期切换状态。
硬件分析
- 将TPAD与STM_ADC短接。
- STM_ADC即为PA5。
所以,相当于将按键TPAD与PA5直接短接。
原理
电容器就是可以容纳电荷的器件,在两块金属板之间添加绝缘体就构成了最简答的电容器。而在PCB中,可以设计为一块带有上拉电阻的铜块,而其被接地的铜块包围住,这就构成了最简单的电容接触按键。当电路板的形状固定时,电容接触按键的容值也基本上是固定的。
此时,若将手指接触到PCB板,电容接触按键的容值就会改变,这是因为在金属板和手指之间又新增了一个等效的电容Cs。如下图所示:

所以,本实验只需要检测电容按键的充电时间,即可判断手指是否接触按键。
具体来说,可以大致分成以下几个步骤:
- 将PA5与电容短接——硬件分析已经实现。
- 将PA5用作推挽输出,且输出低电平,使电容放电。
- 将PA5用作TIM2CH1的捕获管脚。电容在上拉的作用下开始充电,PA5捕获电容充电时间。
- 通过判断充电时间长短,来确定按键是否被按下。
源程序
主函数
/** ****************************************************************************** * @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; } }}
主函数可以分成三个部分:
- 变量初始化
- 硬件初始化
- while循环
在硬件初始化中,CaptureInit()
初始化TIM2CH1。其初始化的方法与之前博客介绍的基本相同,不再详细介绍。TpadInit()
为电容触摸按键初始化,下文详细分析。
在while循环中,函数TpadScan()
将按键的处理结果通过传递到变量tpadStatus
中。若检测到上升沿,则将LED1置反。需要注意的是,此处的上升沿是逻辑上的上升沿指的是:按键由无效状态变成有效状态。然后通过Tick时钟判断当前时间,从而实现LED0的1s周期变化。
TpadInit
/** * @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()
函数来求取未接触按键时,按键的充电时间。
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
中,且通过串口输出。
GetTimeCharge
/** * @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); //发送时间值}
该函数用于获取电容的充电时间。
- 将PA5配置为推挽输出且输出低电平,延时一段时间,让电容充分放电。
- 将PA5配置为复用功能,复用做TIM2CH1的上升沿检测管脚。打开计数器开始计时。
- 通过while循环等待上升沿触发,然后关闭计时器,清零计数器等操作后,将捕获的电容充电时间通过参数输出,且用串口打印。
TpadScan
/** * @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次连续的条件不符合才认为其为真。这是为了防止按键抖动。
发表评论
最新留言
关于作者
