STM32F429第二十三篇之电容按键

Posted 海洋想想

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F429第二十三篇之电容按键相关的知识,希望对你有一定的参考价值。

文章目录

前言

本文主要介绍电容按键的原理与使用方法,主要使用的ARM资源为捕获模块,并不涉及新的模块。所以本文内容不涉及新的HAL库内容的介绍。

关于捕获模块部分,可以参考以下三篇博客:

本文主要参考资料:

  • 刘火良,杨森.STM32库开发实战指南——基于STM32F4.机械工业出版社

本实验的源代码如下所示:
https://github.com/zhenhaiyang/keil

本实验的主要功能为:

  1. 通过电容按键改变LED1的状态。每次点击电容按键,LED的状态置反。
  2. LED0按照1s的周期切换状态。

硬件分析


本实验的硬件如上图所示:

  1. 将TPAD与STM_ADC短接。
  2. STM_ADC即为PA5。

所以,相当于将按键TPAD与PA5直接短接。

原理

电容器就是可以容纳电荷的器件,在两块金属板之间添加绝缘体就构成了最简答的电容器。而在PCB中,可以设计为一块带有上拉电阻的铜块,而其被接地的铜块包围住,这就构成了最简单的电容接触按键。当电路板的形状固定时,电容接触按键的容值也基本上是固定的。

此时,若将手指接触到PCB板,电容接触按键的容值就会改变,这是因为在金属板和手指之间又新增了一个等效的电容Cs。如下图所示:


综上,电容接触按键的原理就是:当手指接触到按键的实收,按键的等效电容就会增大。因此,其充电时间增加。

所以,本实验只需要检测电容按键的充电时间,即可判断手指是否接触按键。

具体来说,可以大致分成以下几个步骤:

  1. 将PA5与电容短接——硬件分析已经实现。
  2. 将PA5用作推挽输出,且输出低电平,使电容放电。
  3. 将PA5用作TIM2CH1的捕获管脚。电容在上拉的作用下开始充电,PA5捕获电容充电时间。
  4. 通过判断充电时间长短,来确定按键是否被按下。

源程序

主函数

/**
  ******************************************************************************
  * @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;
        
    


主函数可以分成三个部分:

  1. 变量初始化
  2. 硬件初始化
  3. 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); //发送时间值

该函数用于获取电容的充电时间。

  1. 将PA5配置为推挽输出且输出低电平,延时一段时间,让电容充分放电。
  2. 将PA5配置为复用功能,复用做TIM2CH1的上升沿检测管脚。打开计数器开始计时。
  3. 通过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次连续的条件不符合才认为其为真。这是为了防止按键抖动。

以上是关于STM32F429第二十三篇之电容按键的主要内容,如果未能解决你的问题,请参考以下文章

STM32F429第二十八篇之ADC

STM32F429第二十八篇之ADC

STM32F429第二十八篇之ADC

STM32F429第二十七篇之DMA实验详解

STM32F429第二十七篇之DMA实验详解

STM32F429第二十七篇之DMA