電容觸摸按鍵原理


前置技能

   
  輸入按鍵-GPIO輸入

  通用定時器原理
RC充放電電路原理

RC充放電電路

如圖:
    電阻R和電容C串聯
    當開關斷開時,電阻R,電容0兩端電壓都是0,無電流
    當開關閉合時(瞬間),電阻R兩端V1(上端)和0(下端)有電壓差,產生電流
    此時電子通過電阻R積累在電容正極(上端),直到電容C電壓為V1,充電完成
    此時R兩端電壓均為V1,無壓差,不再有電流經過

電容C的電壓從0-V1,充電過程中,有充電時間t和電容C的電壓Vt之間的關系(右圖)
看以看出隨充電時間越來越長,充電的效率(斜率)越來越低

分析:
    當按鍵按下瞬間,電阻R兩端電壓V1和0,此時瞬間電流為I = V1 / R
    當電容C充電一段時間后,假設電容C此時的電壓為Vc,那么此時電阻C兩端的電壓為V1和Vc,此時的瞬間電流為I = (V1-Vc) / R
    所以隨着充電時間的增加,電容C的電壓增加,電阻R兩端壓差減小,電流降低,電子積累速度減慢,充電效率降低

RC電路充放電公式

Vt = V0 + (V1 - V0) * [1 - exp(-t / RC)]

V0:電容的初始電壓值
V1:電容最終可以沖/放到的電壓值
Vt:電容t時刻的電壓值

若電容C從0V開始充電,那么V0 = 0,則公式簡化為:
Vt =V1 * [1 - exp(-t / RC)]

公式中V1, R均為常數,要達到相同的充電電壓Vt,那么充電時間t和電容值C,成正比

RC電路電容與電阻關系

結論:
    同樣條件下,電容C和充電時間t成正比
    達到相同的電壓,電容越大,所需要的充電時間越長


電容觸摸按鍵原理

電容觸摸按鍵原理

R:外接電容充放電電阻
Cs:TPAD與PCB之間存在雜散電容
Cx:當手指按下時,手指和TPAD之間的電容

圖A:
    手指未按下時的電路,TPAD與PCB之間存在電容Cs

圖B:
    當手指按下時,手指與TPAD之間存在電容Cx,此時相當於Cs與Cx並聯,電容總值=Cs+Cx

根據上邊”RC充放電電路原理”可知:

    當R一定時,達到相同的電壓,電容越大,需要的充電時間越長
    所以,我們假設未觸摸時充電時間為T1,觸摸時充電時間為T2(T2 > T1)

檢測電容觸摸按鍵過程

1,電容放電到0
     TPAD引腳設置為推挽輸出,放電
     放電到0V后,將計數器值設置為0,充電計時使用

2,電容充電
     TPAD引腳設置為浮空輸入(IO復位后的狀態)

3,充電完成(Vx)進入輸入捕獲
     TPAD引腳開啟輸入捕獲
     因為放完電的時候C的電壓為 0,所以設置上升沿捕獲

4,是否按下-計算充電時間,對比是否按下
     當未按下時,記錄充電完成的時間T1(計數器頻路*數值)
     檢測當次充電時間T2,與T1對比,如果超過預設時間T3,說明按鍵按下


電容觸摸按鍵的硬件連接

電容觸摸按鍵的硬件連接

PA1引腳說明:

PA1引腳說明

如圖:
     電容觸摸按鍵TPAD(黃色部分),TPAD引腳與PA1項鏈
     使用TIM5_CH2進行輸入捕獲

代碼實現

tpad.h聲明功能函數

#ifndef __TPAD_H
#define __TPAD_H
#include "sys.h"

// 未按下電容觸摸按鍵時的充電時間
extern vu16 tpad_default_val;     

// 復位TPAD:
// 設置推挽輸出放電到0;再設置浮空輸入充電,計數器CNT=0
void  TPAD_Reset(void);

// 獲取一次捕獲事件得到充電時間:
// 復位TPAD,等待捕獲上升沿,得到計數器值,計算充電時間
u16   TPAD_Get_Val(void);

// n次調用TPAD_Get_Val取最大值
u16   TPAD_Get_MaxVal(u8 n);

// 初始化TPAD:
// 系統啟動后初始化輸入捕獲,10次調用TPAD_Get_Val()
// 取中間n次平均值,作為未按下時的充電時間tpad_default_val
u8     TPAD_Init(u8 psc);

// 掃描TPAD:
// 調用TPAD_Get_MaxVal獲取多次充電中最大充電時間
// 與tpad_default_val對比,若超過tpad_default_val+TPAD_GATE_VAL則為觸摸
u8     TPAD_Scan(u8 mode);

// 輸入捕獲通道初始化
void  TIM5_CH2_Cap_Init(u16 arr,u16 psc);

#endif

  

tpad.c-tpad.h聲明功能函數的實現

#include "tpad.h"
#include "delay.h"
#include "usart.h"

#define TPAD_ARR_MAX_VAL 0XFFFF    // ARR最大值
vu16 tpad_default_val=0;           // 沒有按下是的充電時間

// 初始化觸摸按鍵
// 獲取空載時觸摸按鍵取值
// 返回值: 0:初始化成功 1:初始化失敗
u8 TPAD_Init(u8 psc)
{
    u16 buf[10];
    u16 temp;
    u8 j,i;

    // 初始化定時器5通道2輸入捕獲
    TIM5_CH2_Cap_Init(TPAD_ARR_MAX_VAL, psc-1);//以1Mhz的頻率計算

    // 連續讀取10次TPAD_Get_Val(),間隔10ms
    for(i=0;i<10;i++)
    {
        buf[i]=TPAD_Get_Val();// 10次定時器值裝入數組
        delay_ms(10);
    }

    // 將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;
            }
        }
    }

    // 取第2-8次充電時間取平均值,串口打印輸出
    temp=0;
    for(i=2;i<8;i++)temp+=buf[i];
    tpad_default_val=temp/6;
    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;
}

// TPAD復位
void TPAD_Reset(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA時鍾

    // 設置PA1為推挽輸出
    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);                    // PA1輸出0,放電

    delay_ms(5);     //延遲5ms 等待放電結束,電容電壓此時為0V

    TIM_SetCounter(TIM5, 0);        //定時器5計數器設置為0
    TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中斷標志

    //設置PA1為浮空輸入-開始充電
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

}

// 獲得定時器捕獲值-上升沿捕獲
u16 TPAD_Get_Val(void)
{
    // TPAD復位:PA1推挽輸出放電到0,再設置PA1浮空輸入充電,定時器=1,清除中斷標志
    TPAD_Reset();

    // 等待捕獲上升沿
    while(TIM_GetFlagStatus(TIM5, TIM_IT_CC2) == RESET)
    {
        // 超時,返回CNT值
        if(TIM_GetCounter(TIM5)>TPAD_ARR_MAX_VAL-500)
            return TIM_GetCounter(TIM5);
    };

    return TIM_GetCapture2(TIM5); // 返回通道2的捕獲值
}

// 連續n次讀取TPAD_Get_Val,返回最大值
u16 TPAD_Get_MaxVal(u8 n)
{
    u16 temp=0;    // 本次值
    u16 res=0;     // 最大值
    while(n--)
    {
        temp=TPAD_Get_Val();   // 獲取一次充電計數
        if(temp>res)res=temp;  // 記錄最大值
    };
    return res;
}

// 掃描觸摸按鍵
// mode:0,不支持連續,1,支持聯系
// 返回值:0,沒有按下;1,有按下
#define TPAD_GATE_VAL 100    //門限值:大於tpad_default_val+TPAD_GATE_VAL視為按下
u8 TPAD_Scan(u8 mode)
{
    static u8 keyen=0;    // 是否可檢測狀態位 0:可以開始檢驗 1: 還不能檢驗
    u8 res=0;             // 返回是否按下 1:按下 0:未按下
    u8 sample=3;          // 默認采樣3次
    u16 rval;             // 捕獲到上升沿的最大值

    // 如果支持連續觸發
    if(mode)
    {
        sample=6;         // 連續觸發時,采樣為6此
        keyen=0;          //支持連續
    }

    //取采樣次數的最大值
    rval=TPAD_Get_MaxVal(sample);

    //對比是否按下了
    if(rval>(tpad_default_val+TPAD_GATE_VAL))
    {
        if(keyen==0)res=1;      // keyen==0,有效,返回1
        //printf("r:%d\r\n",rval);
        keyen=3;                // 至少再掃描3次后按鍵才能生效
    }
    if(keyen)keyen--;

    return res;
}

// 定時器5通道2輸入捕獲初始化
void TIM5_CH2_Cap_Init(u16 arr, u16 psc)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_ICInitTypeDef  TIM5_ICInitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);    // 使能定時器5時鍾
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   // 使能GPIOA時鍾

    // 初始化GPIOA-浮空輸入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;               // PA1
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       // 50Mhz
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;     // 浮空輸入
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    //定時器5初始化
    TIM_TimeBaseStructure.TIM_Period = arr;                 // 重裝載值
    TIM_TimeBaseStructure.TIM_Prescaler =psc;               // 預分頻器
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 設置時鍾分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;// 向上計數
    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);

    //初始化通道2
    TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2; // CC1S=01 設置IC2映射到TI5
    TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿捕獲
    TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 配置輸入分頻-不分頻
    TIM5_ICInitStructure.TIM_ICFilter = 0x03;// IC2F=0011 配置輸入濾波器-8個定時器時鍾周期濾波
    TIM_ICInit(TIM5, &TIM5_ICInitStructure);// 初始化定時器5 IC2

    TIM_Cmd(TIM5, ENABLE ); // 使能定時器5
}

  

main.c 當捕獲到電容觸摸按鍵按下(捕獲上升沿),LED1反轉

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "tpad.h"

 int main(void)
 {
    u8 t=0;                      // 計數器
    delay_init();                // 延時函數初始化
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 中斷優先級分組配置
    uart_init(115200);           // 串口初始化 115200
    LED_Init();                  // LED初始化
    TPAD_Init(6);                // TPAD初始化

    while(1)
    {
        if(TPAD_Scan(0))        // 捕獲到上升沿(此函數執行至少15ms)
        {
            LED1=!LED1;          // LED1取反
        }

        t++;
        if(t==15)
        {
            t=0;
            LED0=!LED0;          // LED0閃爍,標志程序正在運行
        }

        delay_ms(10);            // 延時10ms
    }
 }

 

實驗結果

LED0每間隔一段時間閃爍,標志程序正在運行中
當手指按下電容觸摸按鍵時,LED1取反
---------------------
作者:BraveWangDev
來源:CSDN
原文:https://blog.csdn.net/ABAP_Brave/article/details/52937091
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM