STM32——电容触摸按键充电时间测量实验

1电容触摸按键

在这里插入图片描述
无手指触摸:上电时,电阻作用下,电容Cs进行充电,直到电容充满,这时候会有一个充电时间Tcs。
有手指触摸:上电时,电阻作用下,电容Cs和Cx进行充电,电容充满时间会变长,得到充电时间Tcx。
注意:充电过程可以看成是一个信号从低电平变为高电平的过程。STM32认为高电平的最低电压值(1.8V)、

2检测电容触摸按键实验过程

在这里插入图片描述
没有按下的时候,充电时间为T1(default)。按下TPAD,电容变大,所以充电时间为T2。
我们可以通过检测充电时间来判断是否按下。如果T2-T1大于某个值,就可以判断触摸按键按下。

3.重点函数及调用过程

tpad_reset函数:复位TPAD(放电且开始充电,并清空定时器计数器CNT值)
tpad_get_val函数:获取一次捕获值(复位TPAD,等待捕获上升沿得到充电时间)
tpad_get_maxval函数:多次调用tpad_get_val函数获取充电时间,获取最大的值
tpad_init函数:初始化TPAD,调用tpad_get_val获取电容触摸按键没有按下的默认充电时间值
tpad_scan函数:扫描TPAD,调用tpad_get_maxval获取多次充电中最大充电时间进行判断
tpad_timx_cap_init函数:输入捕获通道初始化
在这里插入图片描述

4.电容触摸按键充电时间测量实验实战

4.1 tpad.c

#include "./BSP/TPAD/tpad.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"

/* 空载的时候(没有手按下),计数器需要的时间
 * 这个值应该在每次开机的时候被初始化一次
 */
//1.空载时计数器时间
volatile uint16_t g_tpad_default_val = 0;           /* 空载的时候(没有手按下),计数器需要的时间 */

/*定时器输入边沿捕获*/
static TIM_HandleTypeDef g_timx_cap_chy_handler;    /* 定时器x句柄 */
static TIM_IC_InitTypeDef g_timx_ic_cap_chy_handler;

/**
 * @brief       初始化触摸按键
 * @param       psc     : 分频系数(值越小, 越灵敏, 最小值为: 1)
 * @retval      0, 初始化成功; 1, 初始化失败;
 */
 uint8_t tpad_init(uint16_t psc)
 {
     //1.定义变量,包括存放10个空载充电时间值数值、6个值累加和,以及i,j
     uint16_t buf[10];
     uint16_t temp;
     uint8_t i,j;
     //2.调用定时器输入捕获初始化函数
     tpad_timx_cap_init(TPAD_ARR_MAX_VAL, psc-1);  /* 以Ft / (psc - 1) Mhz的频率计数 @Ft = 定时器工作频率 */
     //3.连续获取十次值
     for( i = 0; i<10;i++)
     {
       buf[i]= tpad_get_val();
       delay_ms(10);
     }
     //4.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;
             }
         }
     
     }
     //5.去中间六个值的平均值
     temp = 0;
     for(i=2;i<8;i++)
     {
        temp += buf[i];
     }
     
    g_tpad_default_val = temp / 6;
    printf("g_tpad_default_val:%d\r\n", g_tpad_default_val);

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

    return 0;
     

 }
 /**
 * @brief       得到定时器捕获值
 *   @note      如果超时, 则直接返回定时器的计数值
 *              我们定义超时时间为: TPAD_ARR_MAX_VAL - 500
 * @param       无
 * @retval      捕获值/计数值(超时的情况下返回)
 */
 static uint16_t tpad_get_val(void)              //得到定时器捕获值
 {
     //1.判断为哪个通道
     uint32_t flag =(TPAD_TIMX_CAP_CHY == TIM_CHANNEL_1)? TIM_FLAG_CC1:\
                    (TPAD_TIMX_CAP_CHY == TIM_CHANNEL_2)? TIM_FLAG_CC2:\
                    (TPAD_TIMX_CAP_CHY == TIM_CHANNEL_3)? TIM_FLAG_CC3:TIM_FLAG_CC4;
                    
     
     //2.调用复位函数,初始化
     tpad_reset(); 
     //3.通道CHY捕获上升沿
     while(__HAL_TIM_GET_FLAG(&g_timx_cap_chy_handler,flag)== RESET)
     {
        if (g_timx_cap_chy_handler.Instance->CNT > TPAD_ARR_MAX_VAL - 500)
        {
            return g_timx_cap_chy_handler.Instance->CNT;                /* 超时了,直接返回CNT的值 */
        }
     }
     //4.返回捕获比较值寄存器值
     return TPAD_TIMX_CAP_CHY_CCRX;
 
 }
 
 /**
 * @brief       复位TPAD
 *   @note      我们将TPAD按键看做是一个电容, 当手指按下/不按下时容值有变化
 *              该函数将GPIO设置成推挽输出, 然后输出0, 进行放电, 然后再设置
 *              GPIO为浮空输入, 等待外部大电阻慢慢充电
 * @param       无
 * @retval      无
 */
 static void tpad_reset(void)                     //复位
 {
    GPIO_InitTypeDef gpio_init_struct;
    
    gpio_init_struct.Pin = TPAD_GPIO_PIN;                               /* 输入捕获的GPIO口 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;                        /* 复用推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLDOWN;                              /* 下拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                      /* 高速 */
    HAL_GPIO_Init(TPAD_GPIO_PORT, &gpio_init_struct);

    HAL_GPIO_WritePin(TPAD_GPIO_PORT, TPAD_GPIO_PIN, GPIO_PIN_RESET);   /* TPAD引脚输出0, 放电 */
    delay_ms(5);

    g_timx_cap_chy_handler.Instance->SR = 0;                            /* 清除标记 */
    g_timx_cap_chy_handler.Instance->CNT = 0;                           /* 归零 */

    gpio_init_struct.Pin = TPAD_GPIO_PIN;                               /* 输入捕获的GPIO口 */
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;                            /* 复用推挽输出 */
    gpio_init_struct.Pull = GPIO_NOPULL;                                /* 不带上下拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                      /* 高速 */
    gpio_init_struct.Alternate = TPAD_GPIO_AF;                          /* PA5复用为TIM2通道1 */
    HAL_GPIO_Init(TPAD_GPIO_PORT, &gpio_init_struct);                   /* TPAD引脚浮空输入 */
 }


//定时器输入捕获初始化设置
static void tpad_timx_cap_init(uint32_t arr, uint16_t psc) //定时器输入捕获初始化
{
    GPIO_InitTypeDef gpio_init_struct;
    TPAD_GPIO_CLK_ENABLE();                                // TPAD引脚 时钟使能 
    TPAD_TIMX_CAP_CHY_CLK_ENABLE();                        // 定时器 时钟使能 
    
    gpio_init_struct.Pin = TPAD_GPIO_PIN;                               /* 输入捕获的GPIO口 */
    gpio_init_struct.Mode = GPIO_MODE_INPUT;                            /* 输入 */
    gpio_init_struct.Pull = GPIO_PULLDOWN;                                /* 不带上下拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;                      /* 下拉 */
    gpio_init_struct.Alternate = TPAD_GPIO_AF;                           /* PA5复用为TIM2_CH1 */
    HAL_GPIO_Init(TPAD_GPIO_PORT, &gpio_init_struct);                   /* TPAD引脚复用 */
    
    g_timx_cap_chy_handler.Instance = TPAD_TIMX_CAP;                    /* 定时器2 */
    g_timx_cap_chy_handler.Init.Prescaler = psc;                        /* 定时器分频 */
    g_timx_cap_chy_handler.Init.CounterMode = TIM_COUNTERMODE_UP;       /* 向上计数模式 */
    g_timx_cap_chy_handler.Init.Period = arr;                           /* 自动重装载值 */
    g_timx_cap_chy_handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; /* 时钟分频因子 */
    HAL_TIM_IC_Init(&g_timx_cap_chy_handler);
    
    g_timx_ic_cap_chy_handler.ICPolarity = TIM_ICPOLARITY_RISING;                                       /* 上升沿捕获 */
    g_timx_ic_cap_chy_handler.ICSelection = TIM_ICSELECTION_DIRECTTI;                                   /* 映射到TI1上 */
    g_timx_ic_cap_chy_handler.ICPrescaler = TIM_ICPSC_DIV1;                                             /* 配置输入分频,不分频 */
    g_timx_ic_cap_chy_handler.ICFilter = 0;                                                             /* 配置输入滤波器,不滤波 */
    HAL_TIM_IC_ConfigChannel(&g_timx_cap_chy_handler, &g_timx_ic_cap_chy_handler, TPAD_TIMX_CAP_CHY);   /* 配置TIM2通道1 */
    HAL_TIM_IC_Start(&g_timx_cap_chy_handler,TPAD_TIMX_CAP_CHY);                                        /* 使能输入捕获和定时器 */

}   
/**
 * @brief       扫描触摸按键
 * @param       mode :扫描模式
 *   @arg       0, 不支持连续触发(按下一次必须松开才能按下一次);
 *   @arg       1, 支持连续触发(可以一直按下)
 * @retval      0, 没有按下; 1, 有按下;
 */
uint8_t tpad_scan(uint8_t mode)                 //TPAD 扫描 函数 
{
    static uint8_t keyen = 0;       /* 0, 可以开始检测;  > 0, 还不能开始检测; */
    uint8_t res = 0;
    uint8_t sample = 3;             /* 默认采样次数为3次 */
    uint16_t rval;

    if (mode)
    {
        sample = 6;                 /* 支持连按的时候,设置采样次数为6次 */
        keyen = 0;                  /* 支持连按, 每次调用该函数都可以检测 */
    }

    rval = tpad_get_maxval(sample);
    
    if (rval > (g_tpad_default_val + TPAD_GATE_VAL))    /* 大于tpad_default_val + TPAD_GATE_VAL,有效 */
    {
        if (keyen == 0)
        {
            res = 1;                /* keyen==0, 有效 */
        }

//      printf("r:%d\r\n", rval);   /* 输出计数值, 调试的时候才用到 */
        keyen = 3;                  /* 至少要再过3次之后才能按键有效 */
    }

    if (keyen)keyen--;

    return res;
}

/**
 * @brief       读取n次, 取最大值
 * @param       n       :连续获取的次数
 * @retval      n次读数里面读到的最大读数值
 */
static uint16_t tpad_get_maxval(uint8_t n)
{
    uint16_t temp = 0;
    uint16_t maxval = 0;

    while (n--)
    {
        temp = tpad_get_val();  /* 得到一次值 */

        if (temp > maxval){
            maxval = temp;
        }
    }

    return maxval;
}

4.2 tpad.h

#ifndef __TPAD_H
#define __TPAD_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
//TPAD引脚 TIM定时器定义
/* 我们使用定时器的输入捕获功能, 对TPAD进行检测
 * 这里的输入捕获使用定时器TIM2_CH1, 捕获TPAD按键的输入
 */
 //TPAD引脚
 #define TPAD_GPIO_PORT                          GPIOA
#define  TPAD_GPIO_PIN                           GPIO_PIN_5
#define  TPAD_GPIO_CLK_ENABLE()                  do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)     /* PA口时钟使能 */
#define  TPAD_GPIO_AF                            GPIO_AF1_TIM2                                   /*端口复用为TIM2通道1*/

//TIM2_CH1定义
#define TPAD_TIMX_CAP                           TIM2
#define TPAD_TIMX_CAP_CHY                       TIM_CHANNEL_1                                   /* 通道Y,  1<= Y <=4 */
#define TPAD_TIMX_CAP_CHY_CCRX                  TIM2->CCR1                                      /* 通道Y的捕获/比较寄存器 */
#define TPAD_TIMX_CAP_CHY_CLK_ENABLE()          do{ __HAL_RCC_TIM2_CLK_ENABLE(); }while(0)      /* TIM5 时钟使能 */

/* 触摸的门限值, 也就是必须大于 g_tpad_default_val + TPAD_GATE_VAL
 * 才认为是有效触摸, 改大 TPAD_GATE_VAL, 可以降低灵敏度, 反之, 则可以提高灵敏度
 * 根据实际需求, 选择合适的 TPAD_GATE_VAL 即可
 */

#define TPAD_GATE_VAL     100                     //触摸的门限值, 也就是必须大于 g_tpad_default_val + TPAD_GATE_VAL, 才认为是有效触摸 
#define TPAD_ARR_MAX_VAL  0xFFFF              /* 最大的ARR值, 一般设置为定时器的ARR最大值 */

extern volatile uint16_t g_tpad_default_val;      //空载时需要的时间,即手指没按下时

//接口函数,可在其他.c中调用
uint8_t tpad_init(uint16_t psc);                 //TPAD 初始化 函数
uint8_t tpad_scan(uint8_t mode);                 //TPAD 扫描 函数 

//静态函数,只在tpad.c中调用
static void tpad_reset(void);                    //复位
static uint16_t tpad_get_val(void);              //得到定时器捕获值
static uint16_t tpad_get_maxval(uint8_t n);      //读取n次 获取最大值
static void tpad_timx_cap_init(uint32_t arr, uint16_t psc); //定时器输入捕获初始化 

#endif

4.3 main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/TPAD/tpad.h"

int main(void)
{
    uint8_t t = 1;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    tpad_init(8);                           /* 初始化触摸按键 */
    
    while (1)
    {
        if (tpad_scan(0))                   /* 成功捕获到了一次上升沿(此函数执行时间至少15ms) */
        {
            LED1_TOGGLE();                  /* LED1翻转 */
        }

        t++;
        if (t == 15)
        {
            t = 0;
            LED0_TOGGLE();                  /* LED0翻转 */
        }

        delay_ms(10);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/322444.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2719. 统计整数数目

给你两个数字字符串 num1 和 num2 &#xff0c;以及两个整数 max_sum 和 min_sum 。如果一个整数 x 满足以下条件&#xff0c;我们称它是一个好整数&#xff1a; num1 < x < num2min_sum < digit_sum(x) < max_sum. 请你返回好整数的数目。答案可能很大&#xff…

jquery(一)

目录 &#x1f338;基本使用 &#x1f341;两大特性 &#x1f338;操作文档 &#x1f338;样式操作 &#x1f338;属性操作 &#x1f338;文档操作 &#x1f341;内部追加 &#x1f340;原来界面 &#x1f340;追加后界面 &#x1f341;外部追加 &#x1f340;原来界…

进阶Docker2:数据卷和挂载目录

目录 准备 删除容器 创建并运行一个容器 数据卷&#xff08;Volumes&#xff09; 挂载数据卷 虚拟机端口映射 挂载目录&#xff08;Bind mounts&#xff09; 挂载目录 挂载文件 部署在线项目 docker 在容器中管理数据主要有两种方式&#xff1a; - 数据卷&#xff0…

陪诊小程序开发|陪诊软件定制|陪诊系统成品功能包含哪些?

陪诊小程序是一种便捷的工具&#xff0c;为用户提供一系列服务和功能&#xff0c;方便患者在就医过程中获得更好的体验和效果。接下来我们将介绍几个主要的陪诊小程序功能。 陪诊小程序开发功能&#xff1a; 一、预约挂号功能。陪诊小程序能够连接用户和医疗机构的系统&#x…

ART-Adversarial Robustness Toolbox检测AI模型及对抗攻击的工具

一、工具简介 Adversarial Robustness Toolbox 是 IBM 研究团队开源的用于检测模型及对抗攻击的工具箱&#xff0c;为开发人员加强 AI模型被误导的防御性&#xff0c;让 AI 系统变得更加安全&#xff0c;ART支持所有流行的机器学习框架 &#xff08;TensorFlow&#xff0c;Ker…

什么是JAVA的包装类?用了有什么好处?

目录 一、包装类概述 二、包装类和基本数据类型的转换 三、使用包装类的ValueOf方法 四、基本类型和包装类的自动转换 一、包装类概述 Java的包装类是为了方便操作基本数据类型而提供的类。Java的基本数据类型&#xff08;如int、char、boolean等&#xff09;是非对象的&a…

【备战蓝桥杯】——Day1

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-xKn7nmq36s9pgUXR {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

Django教程第4章 | Web开发实战-三种验证码实现

系列&#xff1a;Django学习教程 验证码的存在是为了防止系统被暴力破解攻击&#xff0c;几乎每个系统都有验证码。下面将介绍三种生成验证码方式。 您可以根据你自己的需要进行学习。 手动生成验证码 安装绘图依赖&#xff0c;利用的是画图模块 PIL 以及随机模块 random 在后…

python解决求最短路径、最短时间问题

对于一个求最短路径的经常遇到的问题&#xff0c;对于从某一个节点到其余全部节点所需要的最短时间的问题&#xff0c;可以使用广度优先搜索算法的思路来进行解决&#xff0c;这是一个广度优先搜索算法在二维空间的应用。 问题描述为给定一个节点总数为N和一个列表list&#x…

fastadmin答题考试系统开源二次开发带拍照搜题版本

应用介绍 应用介绍 一款基于FastAdminThinkPHPUniapp开发的小程序答题考试系统&#xff0c;提供全部前后台无加密源代码&#xff0c;支持私有化部署 前端截图&#xff1a; 后台截图&#xff1a; 功能介绍&#xff1a;

LeetCode 每日一题 Day 37-43

终于考完试了&#xff0c;寒假期间将会每天持续更新&#xff01; 447. 回旋镖的数量(Day 37) 给定平面上 n 对 互不相同 的点 points &#xff0c;其中 points[i] [xi, yi] 。回旋镖 是由点 (i, j, k) 表示的元组 &#xff0c;其中 i 和 j 之间的欧式距离和 i 和 k 之间的欧…

颜色对话框 QColorDialog

1. 颜色对话框 QColorDialog 1.1 基本函数 QColor getColor(const QColor &initial Qt::white, QWidget *parent nullptr, const QString &title QString(), QColorDialog::ColorDialogOptions options ColorDialogOptions())返回值&#xff1a;QColor&#xff0c;…

SpringBoot+SSM项目实战 苍穹外卖(11) Apache ECharts

继续上一节的内容&#xff0c;本节学习Apache ECharts&#xff0c;实现营业额统计、用户统计、订单统计和销量排名Top10功能。 数据统计效果图&#xff1a; 目录 Apache ECharts入门案例 营业额统计用户统计订单统计销量排名Top10 Apache ECharts Apache ECharts 是一款基于 …

使用 Clojure 进行 OpenCV 开发简介

从 OpenCV 2.4.4 开始&#xff0c;OpenCV 支持使用与 Android 开发几乎相同的接口进行桌面 Java 开发。 Clojure 是由 Java 虚拟机托管的一种现代 LISP 方言&#xff0c;它提供了与底层 JVM 的完全互操作性。这意味着我们甚至应该能够使用 Clojure REPL&#xff08;Read Eval …

代码随想录 Leetcode1. 两数之和

题目&#xff1a; 代码&#xff08;首刷看解析 2024年1月15日&#xff09;&#xff1a; class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {int another 0;unordered_map<int,int> hash;for(int i 0; i < nums.size();…

arcgis javascript api4.x以basetilelayer方式加载天地图web墨卡托(wkid:3857)坐标系

需求&#xff1a; arcgis javascript api4.x以basetilelayer方式加载天地图web墨卡托&#xff08;wkid&#xff1a;3857&#xff09;坐标系 效果图&#xff1a; 代码&#xff1a; 提示&#xff1a; 2个文件放同一个文件夹下 MyCustomTileLayer.js define([exports, "…

Grind75第10天 | 133.克隆图、994.腐烂的橘子、79.单词搜索

133.克隆图 题目链接&#xff1a;https://leetcode.com/problems/clone-graph 解法&#xff1a; 这个题是对无向图的遍历&#xff0c;可以用深度优先搜索和广度有限搜索。 下面这个图比较清楚的说明了两种方法的区别。 DFS&#xff1a;从A开始克隆&#xff0c;遍历两个邻居…

IntelliJ IDEA - 快速去除 mapper.xml 告警线和背景(三步走)

1、去掉 No data sources configure 警告 Settings&#xff08;Ctrl Alt S&#xff09; ⇒ Editor ⇒ Inspections ⇒ SQL ⇒ No data sources configure 2、去掉 SQL dialect is not configured 警告 Settings&#xff08;Ctrl Alt S&#xff09; ⇒ Editor ⇒ Inspecti…

C语言经典算法之冒泡排序算法

目录 前言 建议&#xff1a; 简介&#xff1a; 一、代码实现 二、时空复杂度 时间复杂度&#xff1a; 空间复杂度&#xff1a; 总结&#xff1a; 前言 建议&#xff1a; 1.学习算法最重要的是理解算法的每一步&#xff0c;而不是记住算法。 2.建议读者学习算法的时候…

决策树(公式推导+举例应用)

文章目录 引言决策树学习基本思路划分选择信息熵信息增益增益率&#xff08;C4.5&#xff09;基尼指数&#xff08;CART&#xff09; 剪枝处理预剪枝&#xff08;逐步构建决策树&#xff09;后剪枝&#xff08;先构建决策树再剪枝&#xff09; 连续值与缺失值处理连续值处理缺失…