STM32-11-电容触摸按键

STM32-01-认识单片机
STM32-02-基础知识
STM32-03-HAL库
STM32-04-时钟树
STM32-05-SYSTEM文件夹
STM32-06-GPIO
STM32-07-外部中断
STM32-08-串口
STM32-09-IWDG和WWDG
STM32-10-定时器

STM32电容触摸按键

  • 电容触摸按键原理
    在这里插入图片描述
    在这里插入图片描述

    无手指触摸:上电时,电阻作用下,电容Cs进行充电,直到电容充满,这时候会有一个充电时间Tcs.

    有手指触摸:上电时,电阻作用下,电容CsCx进行充电,电容充满时间会变长,得到充电时间Tcx.
    在这里插入图片描述
    在这里插入图片描述

  • 检测电容触摸按键过程

    1. TPAD引脚设置为推挽输出,输出低电平,实现电容放电到地
    2. TPAD引脚设置为浮空输入(IO复位后的状态),电容开始充电
    3. 同时开启TPAD引脚的输入捕获功能,开始捕获高电平
    4. 等待充电过程中,上升沿触发(充电到Vth(上升沿的电压值))
    5. 计算充电时间(定时器捕获/比较寄存器获取)
  • 硬件结构图
    在这里插入图片描述

  • 代码实现

    • TPAD初始化函数

      uint8_t tpad_init(uint16_t psc)
      {
          uint16_t buf[10];
          uint16_t temp;
          uint16_t i,j;
          
          
          tpad_timx_cap_init(0XFFFF, psc - 1);
          
          //连续读取10次
          for(i = 0; i < 10; i++)
          {
              buf[i] = tpad_get_val();
              delay_init(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;
          
          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 > 0XFFFF / 2)
          {
              return 1;                   /* 初始化遇到超过TPAD_ARR_MAX_VAL/2的数值,不正常! */
          }
          
          return 0;
      }
      
    • 触摸按键输入捕获设置

      void tpad_timx_cap_init(uint16_t arr, uint16_t psc)
      {
          GPIO_InitTypeDef gpio_init_struct;
          TIM_IC_InitTypeDef timx_ic_cap_chy;
          
          __HAL_RCC_GPIOA_CLK_ENABLE();  //初始化GPIOA时钟
          __HAL_RCC_TIM5_CLK_ENABLE();   //初始化TIM5时钟
          
          gpio_init_struct.Pin = GPIO_PIN_1;       //PA1
          gpio_init_struct.Mode = GPIO_MODE_INPUT; //输入
          gpio_init_struct.Pull = GPIO_PULLDOWN;   //下拉
          gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM; //中速
          HAL_GPIO_Init(GPIOA, &gpio_init_struct);  //初始化
          
          g_timx_cap_chy_handle.Instance = TIM5;      //定时器基地址
          g_timx_cap_chy_handle.Init.Prescaler = psc; //分频系数
          g_timx_cap_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;  //向上计数
          g_timx_cap_chy_handle.Init.Period = arr;    //自动重装载值
          g_timx_cap_chy_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;  //时钟分频因子
          HAL_TIM_IC_Init(&g_timx_cap_chy_handle);
          
          timx_ic_cap_chy.ICPolarity = TIM_ICPOLARITY_RISING;     //上升沿捕获
          timx_ic_cap_chy.ICSelection = TIM_ICSELECTION_DIRECTTI; //映射到TI1
          timx_ic_cap_chy.ICPrescaler = TIM_ICPSC_DIV1;           //输入分频设置为不分频
          timx_ic_cap_chy.ICFilter = 0;                           //输入滤波设置为不滤波
          HAL_TIM_IC_ConfigChannel(&g_timx_cap_chy_handle, &timx_ic_cap_chy, TIM_CHANNEL_2);
          
          HAL_TIM_IC_Start(&g_timx_cap_chy_handle, TIM_CHANNEL_2);  //使能输入捕获和定时器
      }
      

      输入捕获映射到TI1通道,意味着PA1引脚的信号将被定时器的第一个输入捕获通道TI1处理。代码中指定了TIM_ICSELECTION_DIRECTTI,表示直接选择输入引脚作为捕获源,而不是通过其他中间信号。

      PA1引脚的信号映射到定时器通道1(TI1)的过程是通过硬件内部的多路复用器(multiplexer,简称MUX)实现的。

    • 获取捕获值

      uint16_t tpad_get_val(void)
      {
          tpad_reset();
          
          //等待捕获上升沿,捕获结束后标志位会置1
          while(__HAL_TIM_GET_FLAG(&g_timx_cap_chy_handle, TIM_CHANNEL_2) == 0)
          {
              if(g_timx_cap_chy_handle.Instance->CNT > 0xFFFF - 500)
              {
                  return g_timx_cap_chy_handle.Instance->CNT;
              }
          }
          
          return TIM5->CCR2;
      } 
      
    • 复位TPAD

      void tpad_reset(void)
      {
          GPIO_InitTypeDef gpio_init_struct;
          
          gpio_init_struct.Pin = GPIO_PIN_1;
          gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
          gpio_init_struct.Pull = GPIO_PULLUP;
          gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;
          HAL_GPIO_Init(GPIOA, &gpio_init_struct);
          
          HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);  //TPAD引脚输出0,放电
          
          delay_ms(5);
          
          g_timx_cap_chy_handle.Instance->SR = 0;   //清除标记
          g_timx_cap_chy_handle.Instance->CNT = 0;  //归零
          
          gpio_init_struct.Pin = GPIO_PIN_1;
          gpio_init_struct.Mode = GPIO_MODE_INPUT;
          gpio_init_struct.Pull = GPIO_NOPULL;
          gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;
          HAL_GPIO_Init(GPIOA, &gpio_init_struct);
      }
      
      1. 配置PA1为推挽输出模式,并且设置为上拉电阻。这样可以确保PA1在输出状态下可以稳定的输出高低电平信号。
      2. 设置PA1引脚为低电平,相当于对TPAD引脚进行放电操作。
      3. 延时5ms,确保放电操作完成。
      4. 清除定时器状态寄存器和计数器。
      5. 配置PA1引脚为输入模式,并且设置为无上下拉电阻,这样,TPAD引脚可以正常接收外部输入信号。

      在嵌入式系统中,尤其是在涉及触摸传感器或类似的硬件操作时,先将引脚设置为推挽输出模式再进行放电是一个常见的做法。这种方法可以确保引脚能够快速且有效地放电,从而为后续的操作(例如测量或重新配置引脚为输入模式)提供一个已知的初始状态。

      为什么要先设置为推挽输出模式再进行放电?

      1. 强制性放电
      • 推挽输出模式能够提供较强的驱动能力。通过将引脚设置为推挽输出模式并输出低电平,能够确保引脚上的电容或残留电荷能够迅速放电至0。这对于某些敏感的电路来说是必要的,确保电路在重新配置为输入模式之前没有残余电荷影响测量精度。
      1. 可靠的初始状态
      • 直接将引脚设置为低电平进行放电在某些情况下可能并不能保证完全的放电,特别是在引脚上有较大的寄生电容时。推挽模式可以提供更可靠的低电平输出,确保引脚电位完全放电至0。
      1. 硬件保护
      • 通过推挽输出模式放电,可以避免因高阻抗状态导致的浮动电平问题。高阻抗状态下,外界噪声可能会干扰引脚电平,从而影响后续的测量。
    • 扫描触摸按键

      uint8_t tpad_scan(uint8_t mode)
      {
          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 + 100))    /* 大于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;
      }
      
    • 读取的数据取最大值

      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;
      }
      
    • 主函数

      int main(void)
      {
          uint8_t t = 0;
      
          HAL_Init();                                 /* 初始化HAL库 */
          sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
          delay_init(72);                             /* 延时初始化 */
          usart_init(115200);                         /* 串口初始化为115200 */
          led_init();                                 /* 初始化LED */
          tpad_init(6);
      
      
          while (1)
          {
              if (tpad_scan(0))   /* 成功捕获到了一次上升沿(此函数执行时间至少15ms) */
              {
                  LED1_TOGGLE();  /* LED1翻转 */
              }
      
              t++;
              if (t == 10)
              {
                  t = 0;
                  LED0_TOGGLE();  /* LED0翻转 */
              }
      
              delay_ms(200);
          }
      }
      
  • 程序运行流程
    在这里插入图片描述

声明:资料来源(战舰STM32F103ZET6开发板资源包)

  1. Cortex-M3权威指南(中文).pdf
  2. STM32F10xxx参考手册_V10(中文版).pdf
  3. STM32F103 战舰开发指南V1.3.pdf
  4. STM32F103ZET6(中文版).pdf
  5. 战舰V4 硬件参考手册_V1.0.pdf

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

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

相关文章

IC开发——VCS基本用法

1. 简介 VCS是编译型verilog仿真器&#xff0c;处理verilog的源码过程如下&#xff1a; VCS先将verilog/systemverilog文件转化为C文件&#xff0c;在linux下编译链接生成可执行文件&#xff0c;在linux下运行simv即可得到仿真结果。 VCS使用步骤&#xff0c;先编译verilog源…

claude3国内API接口对接

众所周知&#xff0c;由于地理位置原因&#xff0c;Claude3不对国内开放&#xff0c;而国内的镜像网站使用又贵的离谱&#xff01; 因此&#xff0c;团队萌生了一个想法&#xff1a;为什么不创建一个一站式的平台&#xff0c;让用户能够通过单一的接口与多个模型交流呢&#x…

sed命令详解

简介 sed&#xff08;Stream EDitor&#xff09;&#xff0c;是一种流文件编辑器&#xff0c;其特点为&#xff1a; 1、非交互式。处理文件时&#xff0c;把当前处理的行存储在临时缓冲区&#xff08;称为“模式空间”&#xff08;Pattern Space&#xff09;&#xff09;中&am…

【JavaEE精炼宝库】多线程(3)线程安全 | synchronized

目录 一、线程安全 1.1 经典线程不安全案例&#xff1a; 1.2 线程安全的概念&#xff1a; 1.3 线程不安全的原因&#xff1a; 1.3.1 案例刨析: 1.3.2 线程不安全的名词解释&#xff1a; 1.3.3 Java 内存模型 (JMM)&#xff1a; 1.3.4 解决线程不安全问题&#xff1a; 二…

成都爱尔眼科巫雷院长教你在家“自查”白内障

检查以下自己&#xff08;或者父母、亲属&#xff09;是否有发生以下情况&#xff1a; 视物模糊视物模糊是白内障的主要症状。2、眼前暗影白内障早期&#xff0c;有的患者眼前会出现阴影&#xff0c;这是因为晶状体发生浑浊。晶状体混浊在眼前固定位置&#xff0c;患者会“看到…

重生之 SpringBoot3 入门保姆级学习(02、打包部署)

重生之 SpringBoot3 入门保姆级学习&#xff08;02、打包部署&#xff09; 1.6 打包插件1.7 测试 jar 包1.8 application.properties 的相关配置 1.6 打包插件 官网链接 https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-starte…

Android15 Beta更新速览

Android15 Beta更新速览 前台服务变更 前台服务使应用保持活动状态&#xff0c;以便它们可以执行关键且对用户可见的操作&#xff0c;通常以牺牲电池寿命为代价。在 Android 15 Beta 2 中&#xff0c;dataSync 和 mediaProcessing 前台服务类型现在具有约 6 小时的超时时间&a…

【Flowable 7】学习笔记 01 - 初始化数据库表创建流程(源码)

文章目录 前言版本说明配置1、引擎配置初始化2、SQL 执行创建表2.0、创建表概览&#xff08;创建表数目&#xff1a;38&#xff09;2.1、基础组件表创建&#xff08;以 common 组件为例&#xff09;2.2、changelog 组件表创建&#xff08;基于 liquibase&#xff09;2.3、Engin…

swiftui基础组件Image加载图片,以及记载gif动图示例

想要在swiftui中展示图片&#xff0c;可以使用Image这个组件&#xff0c;这个组件可以加载本地图片和网络图片&#xff0c;也可以调整图片大小等设置。先大概看一下Image的方法有哪些可以用。 常用的Image属性 1.调整图像尺寸&#xff1a; 使用 resizable() 方法使图像可调整…

ComfyUI工作流网站

https://openart.ai/home https://comfyworkflows.com/ https://civitai.com/

5G工厂长啥样

5G工厂是一种新型的工业互联网基础设施&#xff0c;利用5G为代表的新一代信息通信技术集成&#xff0c;打造新型工业互联网基础设施。在5G工厂中&#xff0c;自动化和智能化设备广泛使用&#xff0c;高度互联的工厂网络得以实现&#xff0c;远程监控和管理成为可能&#xff0c;…

220V转5V电源芯片,为您的微控制器、继电器和WiFi/蓝牙模块提供动力

220V转5V电源芯片&#xff0c;为您的微控制器、继电器和WiFi/蓝牙模块提供动力标题&#xff1a; 在我们生活的数字化世界中&#xff0c;电源芯片的重要性不容忽视。今天&#xff0c;我们要向您介绍一款适用于各种应用的电源芯片&#xff1a;220V转5V电源芯片。该芯片可从交流电…

Qt 项目(CMake)支持多国语言(2024/05)

目录 1.在工程手动创建languages文件夹2.修改CMakeLists.txt3.在qml上随便添加一下文字内容4.执行CMake 参考:Qt 项目(CMake)设置国际化支持 1.在工程手动创建languages文件夹 2.修改CMakeLists.txt set(TS_FILES"${CMAKE_SOURCE_DIR}/languages/aidi_zh_CN.ts"&qu…

享受当下,还是留待未来?一项fMRI与眼动追踪技术的联合研究

摘要 时间贴现(temporal discount)是指个体对奖励的估计会随着时间流逝而下降的心理现象。具体而言&#xff0c;当获得奖励的时间以日期(日期条件&#xff1b;例如&#xff0c;2023年6月8日)而不是延迟(延迟条件&#xff1b;例如&#xff0c;30天)呈现时&#xff0c;贴现率较低…

(二刷)代码随想录第15天|层序遍历 226.翻转二叉树 101.对称二叉树2

层序遍历 10 102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 综合代码&#xff1a; class Solution{public List<List<Integer>> resList new ArrayList<List<Integer>>();public List<List<…

springboot项目部署到linux服务器

springboot后端 修改前 修改后 重新生成war包 war上传到linux的tomcat的webapps下 其他环境配置和macOS大差不差 Tomcat安装使用与部署Web项目的三种方法_tomcat部署web项目-CSDN博客

如何高效测试防火墙的NAT64与ALG应用协议转换能力

在本文开始介绍如何去验证防火墙&#xff08;DUT&#xff09;支持NAT64 ALG应用协议转换能力之前&#xff0c;我们先要简单了解2个比较重要的知识点&#xff0c;即&#xff0c;NAT64和ALG这两个家伙到底是什么&#xff1f; 网络世界中的“翻译官” - NAT64技术 简而言之&…

紫光展锐前沿探索 | 满足未来6G多差异化应用场景的技术体系思考

在6G架构/系统设计中&#xff0c;紫光展锐提出了未来6G空口“一体多翼”的技术体系概念&#xff0c;即“Big-Lite Multi-RAT”。本文将详细对该技术体系展开介绍。 “一体多翼”技术体系通过 “体”&#xff08;Big RAT&#xff09;和“翼”&#xff08;Lite RAT&#xff09;的…

Visual Studio中调试信息格式参数:/Z7、/Zi、/ZI参数

一般的调试信息都保存在pdb文件中。 Z7参数表示这些调试信息保存到OBJ目标文件中&#xff0c;这样的好处是不需要单独分发PDB文件给下游。Zi就是把所有的调试信息都保存在pdb文件中&#xff0c;以缩小发布文件的大小。ZI和Zi类似&#xff0c;但是增加了热重载的能力&#xff1…

电火灶是燃气灶吗?这“火”又是怎么回事?

电火灶并非传统的燃气灶&#xff0c;它们在能源使用和工作原理上有着显著的区别。电火灶&#xff0c;又名电焰灶或电燃灶&#xff0c;属于新能源厨房灶具行列&#xff0c;它使用清洁的电能作为唯一能源&#xff0c;而不是依赖天然气或液化气等燃料。 具体来说&#xff0c;电火灶…