【STM32】HAL库 STM32CubeMX-正点原子TPAD(电容按键触摸)实验

前言:

本实验通过STM32CubeMX和HAL库结合,实现正点原子的TPAD实验。

使用工具:

芯片:STM32F103ZET6(精英版)

软件:STM32CubeMx软件、MDK-Keil软件

库:STMF1 HAL库

知识概括:

GPIO控制LED的亮灭

定时器输入捕获

STM32CubeMx创建过程

什么是TPAD

        我们使用的是检测电容充放电时间的方法来判断是否有触摸,图 中的 A、B 分别表示有无人体按下时电容的充放电曲线。其中 R 是外接的电容充电电阻, Cs 是没有触摸按下时 TPAD 与 PCB 之间的杂散电容。而 Cx 则是有手指按下的时候,手指与 TPAD 之间形成的电容。图中的开关是电容放电开关(实际使用时,由 STM32F1 的 IO 代替)。

先用开关将 Cs(或 Cs+Cx)上的电放尽,然后断开开关,让 R 给 Cs(或 Cs+Cx)充电, 当没有手指触摸的时候,Cs 的充电曲线如图中的 A 曲线。而当有手指触摸的时候,手指和 TPAD 之间引入了新的电容 Cx,此时 Cs+Cx 的充电曲线如图中的 B 曲线。从上图可以看出,A、B 两 种情况下,Vc 达到 Vth 的时间分别为 Tcs 和 Tcs+Tcx。 其中,除了 Cs 和 Cx 我们需要计算,其他都是已知的,根据电容充放电公式:

 

 我们使用 PA1(TIM2_CH2(精英版))来检测 TPAD 是否有触摸,在每次检测之前,我们先配置 PA1 为推挽输出,将电容 Cs(或 Cs+Cx)放电,然后配置 PA1 为浮空输入,利用外部上拉电阻 给电容 Cs(Cs+Cx)充电,同时开启 TIM5_CH2 的输入捕获,检测上升沿,当检测到上升沿的时 候,就认为电容充电完成了,完成一次捕获检测。

每次复位重启的时候,我们执行一次捕获检测(可以认为没触摸),记录此时的值, 记为 tpad_default_val,作为判断的依据。在后续的捕获检测,我们就通过与 tpad_default_val 的 对比,来判断是不是有触摸发生。

原理图:

由于设计时 PA1 不直接连接到电容触摸按键,而是引到了插针上,我们需要通过跳线帽把 P10 上标为 ADC 的引脚与标号为“TPAD”的标号连接到一起。

简单来说:

TAPD是一个电容,我们先对电容进行放电,之后电容进行充电的过程中,IO端口会由低电平转换为高电平,通过定时器的输入捕获获取电容从低电平转换为高电平的时间。时间为Tcs,手指按下时,电容会增大,低电平转换为高电平的时间会加长,为Tcx。通过两次时间对比判断TPAD是否有触摸。

注:以上部分内容转自正点原子

工程创建

1设置RCC

设置高速外部时钟HSE,选择外部时钟源

 2设置时钟

3设置LED

 将PE5设置为输出模式,上拉电阻,同理设置PB5

 4定时器配置

设置TIM2的通道2

        预分频系数为71,频率为72MHz  / (71+1) = 1us

        自动加载值为最大值65535

        上升沿捕获

        滤波值为0

同时在NVIC中使能TIM2的中断 

5项目文件

 

 6创建工程文件

点击右上角CENERATE CODE 创建工程

代码部分:

创建tapd.c        tapd.h两个文件

tapd.h

#ifndef __TPAD_H__
#define __TPAD_H__

#include "main.h"

void tpad_init(void);               // 初始化
uint8_t tapd_scan(uint8_t mode);    // 扫描函数

#endif

tapd.c

#include "tpad.h"
#include "tim.h"        // 需要使用到定时器的捕获功能

/*
    @brief      复位TPAD
    #node       将TPAD按键看作是一个电容,手指按下和不按下电容值有变化
                先将GPIO设置为推挽输出,输出0,进行放电,在设置为GOIP为
                浮空输入,等待电容充电,并且捕获上拉
*/

static uint8_t flag = 0;                        // 检测是否执行了输入捕获回调函数
uint16_t temp = 0;                              // 存取捕获的值
uint16_t tpad_default_val;                      // 手指不按下时,电容充电到高电平的时间

static void tpad_reset(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    GPIO_InitStruct.Pin = GPIO_PIN_1;                       // 将PA1设置为开漏输出
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;                     // 上拉电阻
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);   // 将PA1置0,对电容进行放电
    
    htim2.Instance->SR = 0;     // 清除标记
    htim2.Instance->CNT = 0;    // 归零
    
    GPIO_InitStruct.Pin = GPIO_PIN_1;                       // 设置为输入模式,进行输入捕获
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 设置为上升沿捕获
    __HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_RISING);
    
    HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);    // 开启定时器捕获
}

static void tpad_get_val(void)
{
    tpad_reset();                                   // 先进行复位(放电,置位,开启定时器捕获)
    while(flag == 0)                                // 判断捕获中断回调函数是否捕获完成
    {
        HAL_Delay(1);
    }
    flag = 0;                                       // 捕获完成后再次将flag置0
}

// 捕获中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    UNUSED(htim);
    HAL_TIM_IC_Stop_IT(&htim2, TIM_CHANNEL_2);      // 先关闭定时器捕获
    if (TIM2 == htim->Instance){
        temp = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2);    // 获取当前捕获值
        flag = 1;                                   // 将flag置1,说明捕获完成
    }
}

/**
* @brief 读取 n 次, 取最大值
* @param n :连续获取的次数
* @retval n 次读数里面读到的最大读数值
*/

static uint16_t tapt_get_maxval(uint8_t n)
{
    uint16_t maxval = 0;
    while (n--) {
        tpad_get_val();     // 进行数据捕获,捕获成功后temp的值更新为最新的捕获值
        if (temp > maxval) {
            maxval = temp;  // 取捕获的最大值
        }
    }
    return maxval;          // 将数值返回
}    

// 初始化触摸按键
void tpad_init(void)
{
    uint16_t buf[10];
    uint16_t m;
    uint8_t i, j;
    
    for (i = 0; i < 10; i++) {  // 获取10个数据
        tpad_get_val();
        buf[i] = temp;
    }
    
    for (i = 0; i < 9; i++) {   // 进行排序
        for (j = i+1; j < 10; j++) {
            if (buf[i] < buf[j]){
                m = buf[i];
                buf[i] = buf[j];
                buf[j] = m;
            }
        }
    }
    m = 0;
    for (i = 2; i < 8; i++) {       // 取中间的6个数据
        m += buf[i];
    }
    
    tpad_default_val = m / 6;       // 求平均值作为没有触摸时的值
}

/**
* @brief    扫描触摸按键
* @param    mode :扫描模式
* @arg      0, 不支持连续触发(按下一次必须松开才能按下一次);
* @arg      1, 支持连续触发(可以一直按下)
* @retval   0, 没有按下; 1, 有按下;
*/

uint8_t tapd_scan(uint8_t mode)     // 扫秒模式
{
    static uint8_t keyen = 0;       /* 0, 可以开始检测; >0, 还不能开始检测; */
    uint8_t res = 0;
    uint8_t sample = 3;             /* 默认采样次数为 3 次 */
    uint16_t rval = 0;
    
    if (mode) {                     // mode = 1为扫描模式,
        sample = 6;                 /* 支持连按的时候,设置采样次数为 6 次 */
        keyen = 0;                  /* 支持连按, 每次调用该函数都可以检测 */
    }
    rval = tapt_get_maxval(sample); // 获取读取的值
    if (rval > (tpad_default_val + 15)) {
        if (keyen == 0) {
            res = 1;
        }
        keyen = 3;
    }
    if (keyen) keyen--;             // 单次触发松手三次后将keyen置0,然后才能将res赋值为1
    return res;
}

在main函数的中添加一下语句:

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

#include "tpad.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  tpad_init();              // TPAD电容按键初始化
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  uint8_t t = 0;
 
  while (1)
  {
      if (tapd_scan(0)) {       // 非连续触发模式,返回1按下,返回0没有按下
          HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
      }
      t++;
      if (t == 15) {
          t = 0;
          HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);
      }
      HAL_Delay(100);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

结果:

PE5连接的LED不停的闪烁说明程序在正常运行,按下电容按键后PB5连接的LED状态取反实现LED的亮灭。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值