前置技能
輸入按鍵-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
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!