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

发布于:2025-06-21 ⋅ 阅读:(15) ⋅ 点赞:(0)

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

基本原理就是这样,我们开始写代码:
其实,看过了上一章内容,就知道,我们只需要把TIM环境配置好,就相当于把HAL库搬了过来,直接使用HAL库的例程就可以了。

在这里插入图片描述

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

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的完整代码:

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

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

/*
 * 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库例程平移过来。不过不管怎么说,电容按键可以使用了。


网站公告

今日签到

点亮在社区的每一天
去签到