零基础RT-thread第四节:电容按键

电容按键 其实只需要理解,手指按上去后充电时间变长,我们可以利用定时器输入捕获功能计算充电时间,超过无触摸时的充电时间一定的阈值就认为是有手指触摸。

基本原理就是这样,我们开始写代码:

其实,看过了上一章内容,就知道,我们只需要把TIM环境配置好,就相当于把HAL库搬了过来,直接使用HAL库的例程就可以了。

这是一个放电的过程,我们需要它一开始时是没有电的

c 复制代码
static void TPAD_Reset(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    //配置引脚为普通推挽输出
    GPIO_InitStructure.Pin = TPAD_TIM_CH_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(TPAD_TIM_CH_PORT, &GPIO_InitStructure);

    //输出低电平,放电
    HAL_GPIO_WritePin ( TPAD_TIM_CH_PORT, TPAD_TIM_CH_PIN ,GPIO_PIN_RESET);
    //保持一小段时间低电平,保证放电完全
    HAL_Delay(5);

    //清除更新标志
    __HAL_TIM_CLEAR_FLAG(&TIM_Handle,TIM_FLAG_CC1);
    __HAL_TIM_CLEAR_FLAG(&TIM_Handle,TIM_FLAG_UPDATE);
    //计数器归0
    __HAL_TIM_SET_COUNTER(&TIM_Handle,0);
    //引脚配置为复用功能,不上、下拉
    GPIO_InitStructure.Pin = TPAD_TIM_CH_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStructure.Alternate = TPAD_TIM_AF;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(TPAD_TIM_CH_PORT,&GPIO_InitStructure);
}

然后是tpad.c的完整代码:

c 复制代码
/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2025-06-14     c       the first version
 */

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include <tpad.h>

#define TPAD_ARR_MAX_VAL    0XFFFF

//保存没按下时定时器计数值
__IO uint16_t tpad_default_val=0;

/***********************************
 *
 * 定时器输入捕获配置
 *
 ***********************************/
 TIM_HandleTypeDef TIM_Handle;
static void TIMx_CHx_Cap_Init(uint32_t arr,uint16_t psc)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    TIM_IC_InitTypeDef sConfigIC;
    //使能TIM时钟
    TPAD_TIM_CLK_ENABLE();
    //使能通道引脚时钟
    TPAD_TIM_GPIO_CLK_ENABLE();
    //端口配置
    GPIO_InitStructure.Pin = TPAD_TIM_CH_PIN;
    //复用功能
    GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStructure.Alternate = TPAD_TIM_AF;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    //不带上下拉
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(TPAD_TIM_CH_PORT, &GPIO_InitStructure);
    //初始化TIM
    //设定计数器自动重装值
    TIM_Handle.Instance = TPAD_TIMx;
    TIM_Handle.Init.Prescaler = psc;
    TIM_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    TIM_Handle.Init.RepetitionCounter = 0;
    TIM_Handle.Init.Period = arr;
    TIM_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_IC_Init(&TIM_Handle);
    //上升沿触发
    sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
    // 输入捕获选择
    sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
    //配置输入分频,不分频
    sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
    //配置输入滤波器 不滤波
    sConfigIC.ICFilter = 0;
    //初始化捕获通道
    HAL_TIM_IC_ConfigChannel(&TIM_Handle, &sConfigIC, TPAD_TIM_Channel_X);
    //启动TIM
    HAL_TIM_IC_Start(&TIM_Handle, TPAD_TIM_Channel_X);
}

/****************************************
 *
 * 为电容按键放电
 * 清除定时器标志及计数
 *
 *****************************************/
static void TPAD_Reset(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    //配置引脚为普通推挽输出
    GPIO_InitStructure.Pin = TPAD_TIM_CH_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(TPAD_TIM_CH_PORT, &GPIO_InitStructure);

    //输出低电平,放电
    HAL_GPIO_WritePin ( TPAD_TIM_CH_PORT, TPAD_TIM_CH_PIN ,GPIO_PIN_RESET);
    //保持一小段时间低电平,保证放电完全
    HAL_Delay(5);

    //清除更新标志
    __HAL_TIM_CLEAR_FLAG(&TIM_Handle,TIM_FLAG_CC1);
    __HAL_TIM_CLEAR_FLAG(&TIM_Handle,TIM_FLAG_UPDATE);
    //计数器归0
    __HAL_TIM_SET_COUNTER(&TIM_Handle,0);
    //引脚配置为复用功能,不上、下拉
    GPIO_InitStructure.Pin = TPAD_TIM_CH_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStructure.Alternate = TPAD_TIM_AF;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(TPAD_TIM_CH_PORT,&GPIO_InitStructure);
}

/****************************************************
 *
 * 得到定时器捕获值
 * 如果超时,则直接返回定时器的计数值.
 *
 *****************************************************/
static uint16_t TPAD_Get_Val(void)
{
  /* 先放电完全,并复位计数器 */
    TPAD_Reset();
    //等待捕获上升沿
    while(__HAL_TIM_GET_FLAG(&TIM_Handle,TIM_FLAG_CC1) == RESET )
    {
        //超时了,直接返回CNT的值
        if(__HAL_TIM_GET_COUNTER( &TIM_Handle)>TPAD_ARR_MAX_VAL-500)
            return __HAL_TIM_GET_COUNTER( &TIM_Handle);
    };
    /* 捕获到上升沿后输出TIMx_CCRx寄存器值 */
    return HAL_TIM_ReadCapturedValue(&TIM_Handle, TIM_CHANNEL_1);
}

/****************************************************
 *
 * 读取n次,取最大值
 * n:连续获取的次数
 * 返回值:n次读数里面读到的最大读数值
 *
 *****************************************************/
static uint16_t TPAD_Get_MaxVal(uint8_t n)
{
    uint16_t temp=0;
    uint16_t res=0;
    while(n--)
    {
        temp=TPAD_Get_Val();//得到一次值
        if(temp>res)res=temp;
    };
    return res;
}

/********************************************************
*
* 初始化触摸按键
* 获得空载的时候触摸按键的取值.
* 返回值:0,初始化成功;1,初始化失败
*
*********************************************************/
uint8_t TPAD_Init(void)
{
    uint16_t buf[10];
    uint32_t temp=0;
    uint8_t j,i;

    //设定定时器预分频器目标时钟为:9MHz(216Mhz/24)
    TIMx_CHx_Cap_Init(TPAD_ARR_MAX_VAL,24-1);
    for(i=0;i<10;i++)//连续读取10次
    {
        buf[i]=TPAD_Get_Val();
        HAL_Delay(10);
    }
    for(i=0;i<9;i++)//排序
    {
        for(j=i+1;j<10;j++)
        {
            if(buf[i]>buf[j])//升序排列
            {
                temp=buf[i];
                buf[i]=buf[j];
                buf[j]=temp;
            }
        }
    }
    temp=0;
    //取中间的6个数据进行平均
    for(i=2;i<8;i++)
    {
      temp+=buf[i];
    }

    tpad_default_val=temp/6;
    /* printf打印函数调试使用,用来确定阈值TPAD_GATE_VAL,在应用工程中应注释掉 */
    //printf("tpad_default_val:%d\r\n",tpad_default_val);

    //初始化遇到超过TPAD_ARR_MAX_VAL/2的数值,不正常!
    if(tpad_default_val>TPAD_ARR_MAX_VAL/2)
    {
        return 1;
    }

    return 0;
}

uint8_t TPAD_Scan(uint8_t mode)
{
    //0,可以开始检测;>0,还不能开始检测
    static uint8_t keyen=0;
    //扫描结果
    uint8_t res=0;
    //默认采样次数为3次
    uint8_t sample=3;
  //捕获值
    uint16_t rval;

    if(mode)
    {
        //支持连按的时候,设置采样次数为6次
        sample=6;
        //支持连按
        keyen=0;
    }
    /* 获取当前捕获值(返回 sample 次扫描的最大值) */
    rval=TPAD_Get_MaxVal(sample);
    /* printf打印函数调试使用,用来确定阈值TPAD_GATE_VAL,在应用工程中应注释掉 */
    //printf("scan_rval=%d\n",rval);

    //大于tpad_default_val+TPAD_GATE_VAL,且小于10倍tpad_default_val,则有效
    if(rval>(tpad_default_val+ 100)&&rval<(10*tpad_default_val))
    {
    //keyen==0,有效
        if(keyen==0)
        {
            res=1;
        }
        keyen=3;                //至少要再过3次之后才能按键有效
    }

    if(keyen)
    {
        keyen--;
    }
    return res;
}

接下来是tpad.h

c 复制代码
/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2025-06-14     c       the first version
 */
#ifndef APPLICATIONS_TPAD_H_
#define APPLICATIONS_TPAD_H_

#define TPAD_TIMx                   TIM2
#define TPAD_TIM_CLK_ENABLE()       __TIM2_CLK_ENABLE()

#define TPAD_TIM_Channel_X          TIM_CHANNEL_1
#define TPAD_TIM_GetCaptureX        TIM_GetCapture1

#define TPAD_TIM_GPIO_CLK_ENABLE()  __GPIOA_CLK_ENABLE()
#define TPAD_TIM_CH_PORT            GPIOA
#define TPAD_TIM_CH_PIN             GPIO_PIN_5
#define TPAD_TIM_AF                 GPIO_AF1_TIM2

/************************** TPAD 函数声明********************************/
uint8_t TPAD_Init(void);
uint8_t TPAD_Scan(uint8_t mode);


#endif /* APPLICATIONS_TPAD_H_ */

最后是main.c

c 复制代码
/*
 * Copyright (c) 2006-2025, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2025-06-14     RT-Thread    first version
 */

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include <rtdevice.h>
#include <board.h>
#include <tpad.h>

#define LED_R_PIN    GET_PIN(H, 10)

static rt_base_t led_r_stat = PIN_LOW;

int main(void)
{
    TPAD_Init();

    rt_pin_mode(LED_R_PIN, PIN_MODE_OUTPUT);

    while (1)
    {
        if (TPAD_Scan(0)) {
            led_r_stat = (led_r_stat == PIN_LOW) ? PIN_HIGH : PIN_LOW;
            rt_pin_write(LED_R_PIN, led_r_stat);
        }
    }

    return RT_EOK;
}

感觉代码太不RTthread了。直接从HAL库例程平移过来。不过不管怎么说,电容按键可以使用了。

相关推荐
亿道电子Emdoor1 小时前
【ARM】MDK Debug模式下Disassembly窗口介绍
stm32·单片机·嵌入式硬件
点灯小铭1 小时前
基于STM32单片机的无线鼠标设计
stm32·单片机·计算机外设·毕业设计·课程设计
嵌入式小李5 小时前
stm32项目(24)——基于STM32的汽车CAN通信系统
stm32·嵌入式硬件·汽车
我怕是好18 小时前
STM32 输入捕获,串口打印,定时器,中断综合运用
stm32·单片机·嵌入式硬件
点灯小铭1 天前
基于STM32单片机的OneNet物联网环境检测系统
stm32·单片机·物联网·毕业设计·课程设计
滴啦嘟啦哒1 天前
【从0到1制作一块STM32开发板】5. 整体布局
stm32·单片机·嵌入式硬件
普中科技1 天前
【普中STM32精灵开发攻略】--第 11 章 SysTick系统定时器
stm32·单片机·嵌入式硬件·物联网·arm·普中科技
Hello_Embed1 天前
STM32HAL 快速入门(二):用 CubeMX 配置点灯程序 —— 从工程生成到 LED 闪烁
笔记·stm32·单片机·学习·嵌入式软件
JasmineX-11 天前
STM32的SPI通信(软件读写W25Q64)
c语言·stm32·单片机·嵌入式硬件
小杨爱搞嵌入式2 天前
【STM32】GPIO的输入输出
c语言·笔记·stm32·单片机·学习