“电容触摸按键实验”实例解析

发布者:MindfulCreator最新更新时间:2022-04-20 来源: eefocus 手机看文章 扫描二维码
随时随地手机看文章

电容触摸按键实验

电容触摸按键的基本原理(原理图层面)

新电容的产生与作用

我们学过模电的同学知道:我们的手其实相当于一块可以存储感应电荷的金属板,当我们的手靠近屏幕时,我们的手与屏幕下的金属板Cx构成了一个平行电容板,这个电容与Cs杂散电容相并联,“并联电容C=C1+C2”。


充放电性能的变化

我们看见“VCC是一定的;电容C越大代表一定电压下,存储的电荷数量越多(Q=CU),反之,充电到U所花费的时间也更多;电阻R的值一定代表着对电荷的阻碍作用是固定的”。


充电到V=Vth,花费时间TA明显小于TB;我们真实情况下的矩形脉冲如下图所示:


脉冲如何被捕获

当电容从0充电到Vth时,就相当于一个上升沿脉冲。其实这个充放电过程很快,我们一般用的RC充放电电路是为了观察现象因此将时间常数做的很大,但是现实的话我们要快速识别必须将时间常数缩小到一定范围内才OK。


原理图层面的连接

STM_ADC其实只是这个引脚功能的其中一个,我们这里并不是用的引脚的ADC功能,以下为该引脚的复用:

我们这里使用的是PA1的TIM5_CH2功能,用于快速捕获TPAD的有效脉冲沿变化。


硬件配置的大致流程

image.png

注:没有按下的时候,充电时间为T1(default)。按下TPAD,电容变大,所以充电时间为T2。我们可以通过检测充放电时间,来判断是否按下。如果T2-T1大于某个值,就可以判断有按键按下。


电容触摸按键的基本原理(软件配置层面)

image.png

函数说明

TPAD_InitConfig()函数

void TPAD_InitConfig() // 读取默认电容充电时间  

{  

    u16 RecordChargeTime[10] = {0};  

    u8 i = 0, temp = 0;  

      

    uart_init(115200); // 初始化USART1  

    TIM_CAP_InitConfig(0xFFFF, 72-1); // 初始化TIM5_CH2的输入配置  

      

    for(; i<10; i++)  

    {  

        RecordChargeTime[i] = TPAD_GetValue(); // 连续计算10次默认充电时间  

    }  

for(i=0; i<9; i++)

{

if(RecordChargeTime[i] < RecordChargeTime[i+1]) // 冒泡排序(大->小)

{

temp = RecordChargeTime[i];

RecordChargeTime[i] = RecordChargeTime[i+1];

RecordChargeTime[i+1] = temp;

}

    for(i=1, temp=0; i<9; i++)  

    {  

        temp += RecordChargeTime[i]; // 去掉MAX与MIN求平均充电时间  

    }  

    TPAD_DefaultValue = temp/8; // 求出TPAD无动作时的默认充电时间  

    printf("Default:%dusrn",TPAD_DefaultValue); // 向串口打印未触摸时的电容充电时长  

}  

 


TPAD_Reset()函数

这里值得注意的是:PA1端口输入输出模式的变化以及TIM_ClearFlag()函数的使用。


PA1端口输入输出模式的变化:


我们会疑问“PA1端口为何要变化输入输出模式?”,在我们上述流程中提及过,我们要对电容充电所造成的上升沿重新计数就必须先将TPAD(PA1)端口置0进行充分的放电。那我们如何放电呢?

在图中,PA1与TPAD通过跳线帽连接在一起,然后为了读取TPAD中产生的上升沿,我们必须将PA1设置为TIM5_CH2的功能,此时PA1为浮空输入模式,但是我们读取完有效的脉冲沿之后,若继续读取必须先进行放电才可以。此时,我们配置PA1为推挽输出模式,放完电后再变成浮空输入模式。


TIM_ClearFlag()库函数的含义:


我们使用库函数的同学会有些疑问:TIM_ClearFlag()函数与 TIM_ClearITPendingBit()函数的区别在哪里?这里我来解说一下。


TIM_ClearFlag()函数是用于“清除标志位的”,比如:当检测到上升沿脉冲(有效的脉冲沿),相应的TIM_IT_CC2标志会出现,其实这些标志位和TIM_ClearITPendingBit ()函数中的中断标志位是一摸一样的,只不过由于未开启中断,事件一旦发生仅仅会使得标志位置1,不会触发中断。 其实如下的程序中我们就是想清除标志位,因此也可以使用TIM_ClearITPendingBit(TIM5, TIM_IT_CC2),这两个函数的功能就是去除相应的标志位,功能相同可以相互转换使用。


TIM_ClearFlag()与TIM_ClearITPendingBit()的函数体对比


TIM_ClearFlag()函数体


void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)  

{  

  /* Check the parameters */  

  assert_param(IS_TIM_ALL_PERIPH(TIMx));  

  assert_param(IS_TIM_IT(TIM_IT));  

  /* Clear the IT pending Bit */  

  TIMx->SR = (uint16_t)~TIM_IT;  // 操作SR寄存器

}  

 


TIM_ClearITPendingBit()的函数体


void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG)  

{    

  /* Check the parameters */  

  assert_param(IS_TIM_ALL_PERIPH(TIMx));  

  assert_param(IS_TIM_CLEAR_FLAG(TIM_FLAG));  

     

  /* Clear the flags */  

  TIMx->SR = (uint16_t)~TIM_FLAG;  // 操作SR寄存器

}  

 


我们从TIM_ClearFlag()与TIM_ClearITPendingBit()的函数体对比发现,完全一摸一样,可以互换使用。


//复位一次  

void TPAD_Reset(void)  

{  

    GPIO_InitTypeDef  GPIO_InitStructure;   

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    //使能PA端口时钟  

      

    //设置GPIOA.1为推挽使出  

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;                //PA1 端口配置  

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         //推挽输出  

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

    GPIO_Init(GPIOA, &GPIO_InitStructure);  

    GPIO_ResetBits(GPIOA,GPIO_Pin_1);                        //PA.1输出0,放电  

  

    delay_ms(5);  // 给予充足的时间进行放电

  

    TIM_SetCounter(TIM5,0);     //计数器的初始计数值置零

    TIM_ClearFlag(TIM5, TIM_IT_CC2);   // 清除TIM5_CH2的上升沿标志位

    //重新设置GPIOA.1为浮空输入  

    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;  //浮空输入  

    GPIO_Init(GPIOA, &GPIO_InitStructure);   

 


TPAD_GetValue()函数

u16 TPAD_GetValue() // 获取电容充电时间  

{  

    TPAD_Reset(); // 复位一次  

      

    while(TIM_GetFlagStatus(TIM5, TIM_IT_CC2) == RESET)//不断轮询等待捕获上升沿  

    {  

        if(TIM_GetCounter(TIM5)>MAX_ARR-500)  

        {  

            return TIM_GetCounter(TIM5);//超时了,直接返回CNT的值  

        }  

    };    

    return TIM_GetCapture2(TIM5);     

}  

 


这里有几行代码特别值得关注:


if(TIM_GetCounter(TIM5)>MAX_ARR-500)    

{    

    return TIM_GetCounter(TIM5);//超时了,直接返回CNT的值    

   


这里的提出了一个“检查错误”的概念,我们可以在检查是否有上升沿脉冲触发的同时检测“电容充电时间是否过长”,如果充电时间过长直接return值结束轮询(while(1)),此时我们认为有效脉冲沿持续时间为此时的计数器的值。


TPAD_GetMaxValue()函数

u16 TPAD_GetMaxValue(u8 SampleNumber) // 捕获4次取MAX  

{  

    u8 temp = TPAD_GetValue();  

      

    while(1)  

    {  

        temp = temp>TPAD_GetValue()?temp:TPAD_GetValue();  

        if(--SampleNumber) break;  

    }  

      

    return temp; // 求出TPAD无动作时的默认充电时间  

}  

 


TPAD_Scan()

u8 TPAD_Scan() // 扫描TPAD状态且不支持连续按

{  

    u16 TPAD_MaxValue = TPAD_GetMaxValue(3); // 连续采集3次  

    static u8 PressedFlag = 1;  

      

    uart_init(115200); // 初始化USART1  

      

    printf("Moment:%dusrn",TPAD_MaxValue); // 向串口打印当前充电时长  

      

    if((TPAD_MaxValue >= TPAD_Threshold + TPAD_DefaultValue)&&PressedFlag)  

    {  

        PressedFlag = 0;  

        return 1;  

    }  

    else if(TPAD_MaxValue < TPAD_Threshold + TPAD_DefaultValue)  

    {  

        PressedFlag = 1;  

        return 0;  

    }  

    else  

    {  

        return 0;  

    }  

}  

 


不支持连续按的精髓代码:


    if((TPAD_MaxValue >= TPAD_Threshold + TPAD_DefaultValue)&&PressedFlag)  

    {  

        PressedFlag = 0;  

        return 1;  

    }  

    else if(TPAD_MaxValue < TPAD_Threshold + TPAD_DefaultValue)  

    {  

        PressedFlag = 1;  

        return 0;  

    }  

    else  

    {  

        return 0;  

    }  

// 希望大家理解代码执行的逻辑,我在这里就不仔细讲解了。

 


我们要清楚的一点是:我们前面编写的所有函数都是为了编写TPAD_Scan()做准备,我们真正在main函数中使用的只有TPAD_Scan()函数。我们在编写函数时的好习惯是:注意哪些是辅助函数,哪些是要在main函数中使用得到的函数,函数的功能一定不要过于集中也不要过于分散。


程序示例

Main.c

#include "tpad.h"  

#include "led.h"  

#include "timer.h"  

#include "stm32f10x.h"  

#include "delay.h"  

#include "usart.h"  

  

int main()  

{  

    extern u16 TPAD_DefaultValue; // TPAD的默认充电时间  

    u8 TPAD_Status = 0, temp = 0;  

      

    delay_init(); // systick时钟初始化  

    LED_InitConfig(); // 初始化LED1&LED0  

    TPAD_InitConfig(); // 计算TPAD_DefaultValue  

    uart_init(115200); // 初始化USART1  

      

    while(1)  

    {  

        TPAD_Status = TPAD_Scan(); // 实时捕获TPAD的动作  

          

        if(TPAD_Status == 1)  

        {  

            LED1 = !LED1; // 触摸按键动作触发LED1状态翻转  

        }  

        temp++;  

        if(temp == 15)  

        {  

            LED0 = !LED0; // LED0作为状态灯使用  

        }  

  

    }  

}  

 


Led.c

#include "led.h"  

#include "stm32f10x.h"  

  

void LED_InitConfig()  

{  

    GPIO_InitTypeDef GPIO_InitStructure;  

      

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); // 使能LED1,LED0的时钟  

      

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

[1] [2]
引用地址:“电容触摸按键实验”实例解析

上一篇:STM32输入捕获实验示例详解
下一篇:OLED自定义显示图像实验(基础->进阶)

推荐阅读最新更新时间:2024-11-11 14:56

20.电容触摸按键实验
一。 电容触摸按键原理 1. RC 充放电电路原理 Cx电压从0开始充电,一直到V1。 如果达到同样的电压值,如果电容越大,那么达到的时间越长。 手指按下后,电容值为Cs+Cx,电容变大,充电时间变长,通过判断充电时间长短来判断TPAD是否被按下。 2. 电容触摸按键在PCB板上怎么画 首先,人体是具有一定电容的。当我们把PCB上的铜画成如下形式的时候,就完成了一个最基本的触摸感应按键。 上图左边,是一个基本的触摸按键,中间圆形绿色的为铜(我们可以称之为“按键”),在这些按键中会引出一根导线与MCU相连,MCU通过这些导线来检测是否有按键“按下”(检测的方法多种多样,这将在后面章节中谈到);外围的绿色也是铜,不
[单片机]
20.<font color='red'>电容</font><font color='red'>触摸按键</font>实验

推荐帖子

如何加载驱动!!!!!
请问各位,我编好了1个iic设备的驱动(.h和.c)后,怎么能把它加载到xworks中呢都有什么方法,请高人详解还有就是xxDRV()和xxDevCreate()一般用在什么地方来注册设备如何加载驱动!!!!!不懂,帮顶!需要看你是基于工程的,还是基于命令行方式的。VxWorks是通过网络来下载引导的:方法:1、基于目标板BSP建立一个可下载的工程2、把你驱动的原文件加入到工程中去,然后编译3、编译完成,上电你的目标板,利用网络下载、引导VxWorks
yangyali314 嵌入式系统
WiMAX技术综述
人类的科学总是不断进步的,技术总是为应运而生的。WiMAX作为一项新兴技术,能够在比Wi-Fi更广阔的地域范围内提供“最后一公里”宽带连接性,由此支持企业客户享受T1类服务以及居民用户拥有相当于线缆/DSL的访问能力。凭借其在任意地点的1~6英里覆盖范围,WiMAX将可以为高速数据应用提供更出色的移动性。此外,凭借这种覆盖范围和高吞吐率,WiMAX还能够提供为电信基础设施、企业园区和Wi-Fi热点提供回程。本文主要针对WiMAX技术产生的背景,技术特点,主要应用,存在问题以及发展趋
maker RF/无线
请问香版,st72324如何进行位操作,cosmicc如何操作,急
st72324是不是在指定的区域才能位操作cosmicc下有没有直接的操作指令请问香版,st72324如何进行位操作,cosmicc如何操作,急仔细查下,有与或操作吧,不过cosmic的帮助手册有讲位操作的例如var|=0x10;var&=~0x10;直接使用与或操作即可,编译器会选用位操作指令的能不能结构定义如PADDR.1PADDR.2这样能不能结构定义但不是你说的那样,你那
fjjwwb123456 stm32/stm8
无线信号的常识性知识
什么是信号,哪里有信号?通常我们在用手机的时候经常会说信号差,手机没信号了,生活中处处都有信号,手机信号是“无线电波”“无线电信号”,它是看不见摸不着的东西。今天我们就来说一说关于信号的知识。图1信号差,难以通话  通常可以用示波器、频谱分析仪等设备来检测无线电信号。可用频率、幅度、相位等参数来描述无线电信号。对于一般的维修人员来说,很少关注信号的相位。  1、信号的频率  频率,指单位时间内信号发生周期性变化的次数。频率的国际单位是赫兹(Hz)。
Jacktang RF/无线
micropython的EFM32移植
https://github.com/osresearch/micropython/tree/efm32/ports/efm32micropython的EFM32移植
dcexpert MicroPython开源版块
Multisum 12.0 和13.0破解安装,以及安装包
前几天买了SSD,一重装了系统,一些软件就不能用了,所以花时间又重新装了一些软件,发现有的软件安装还是挺繁琐的,对于之前完全没有接触过的新手,也想起之前自己在网络上找资源的艰辛,不多说,我就是来送福利的,另附Multisum12.0Multisum13.0Timson,如果您要查看本帖隐藏内容请回复的安装包和破解说明汉化教程。Multisum12.0和13.0破解安装,以及安装包找了很久,学习下载多多学习学习:pleased:刚好才装遇到此事,看来福利来了,
logitech66 模拟电子
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved