依然採用輸入捕獲的原理來採集是否產生電容觸摸。
實驗目的:
我們將用 TIM5 的通道 2(PA1)來做輸入捕獲,並實現一個簡單的電容觸摸按鍵,通過該按鍵控制 DS1 的亮滅。
實驗原理
電容式觸摸按鍵。
如圖爲內部結構圖,我們是通過充放電時間(到達一個固定電壓值)來判定是否有觸摸電容按鍵,當開關打開時,電容CX爲觸摸按鍵的平面,裏面包含一定量的散雜(本身存在)電容,這時,電容處於放電狀態,當放電完畢,關閉開關,這時電容CX開始充電,充電時間遵從公式Vc=V0*(1-e^(-t/RC)),算出T,可以得到未觸摸時的充電時間值,可以作爲初始化的步驟。
如圖爲有觸摸(B)和無觸摸(A)到達固定電壓的時間值,只要我們通過分析實際測量的值,做一個門檻值,就可以準確判斷是否存在觸摸情況。
流程
說了實驗原理,想要實現我們的功能,少不了的是梳理實驗流程。
想要實時的實現我們的輸入捕獲,需要經歷一個不斷充放電的過程,檢測充放電時間來判定是否有按下電容觸摸按鍵,一直循環方可實現觸摸按鍵的掃描,實現led燈反轉。
解釋及補充
led能否反轉,關鍵在於充電的時間,我們在(mcu重啓)初始化時,就將未觸摸時的充電時間算出來,保存在一個十六位的變量中,與後面觸摸時的充電時間作比較判斷。
我們使用 PA1(TIM5_CH2)來檢測 TPAD 是否有觸摸,在每次檢測之前,我們先配置PA1 爲推輓輸出,將電容 Cx放電,然後配置 PA1 爲浮空輸入,利用外部上拉電阻給電容 Cx充電,同時開啓 TIM5_CH2 的輸入捕獲,檢測上升沿,當檢測到上升沿的時候,就認爲電容充電完成了,完成一次捕獲檢測。
硬件配置
精英版的用跳線帽短接
算法梳理
我們需要的:
(1):需要未觸摸時的充電時間,取多次求平均值,定義爲static變量
(2):需要時刻採集電容觸摸按鍵上的充電時間,多次採集,取最大值與未觸摸的做比較,
(3):支持連續觸摸(持續採集)還是非連續(按下去必須鬆開纔會進行下一次採集)
程序初始化流程
(1):GPIO,定時器時鐘開啓
(2):定時器初始化,初始化GPIO爲開漏輸出
(3):開啓定時器捕獲,不開啓中斷,採用循環檢測標誌位判斷是否成功採集捕獲脈衝
(4):編寫充電放電過程函數,從推輓輸出放電到浮空輸入
(5):循環檢測標誌位,獲取觸摸時的充電時間值
(6):採集數據的準確度處理,算法自行編寫,取出最大值,對採集到的合理值進行支持連按和非連按處理
(7):在主函數main.c裏循環檢測調用數據採集函數
電容觸摸按鍵頭文件tpad.h
#ifndef TPAD_H
#define TPAD_H
#include "sys.h"
extern void tpad_init(u16 arr,u16 prer);
extern void tapd_reset();
extern int value_get(void);
extern int scan_max(int );
extern int scan(int );
extern int tapd_initvalue();
#endif
電容觸摸按鍵源文件tapd.c
#include "tpad.h"
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "timepwm.h"
#define TPAD_GATE_VAL 30
short int basevalue;
void tpad_init(u16 arr,u16 prer)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_InitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
TIM_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_InitStructure.TIM_Period=arr;
TIM_InitStructure.TIM_Prescaler=prer;
TIM_TimeBaseInit(TIM5, &TIM_InitStructure);
TIM_ICInitStructure.TIM_Channel=TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter=0x00;
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICPrescaler= TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection= TIM_ICSelection_DirectTI;
TIM_ICInit(TIM5, &TIM_ICInitStructure);
TIM_ITConfig( TIM5,TIM_IT_CC1,ENABLE);
TIM_Cmd(TIM5,ENABLE );
}
void tpad_reset(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
delay_ms(5);
TIM_SetCounter(TIM5,0);
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
int value_get(void)
{
tpad_reset();
while(TIM_GetFlagStatus(TIM5, TIM_IT_CC2)==0)
{
if(TIM_GetCounter(TIM5)>(65536-500))
return TIM_GetCounter(TIM5);
}
return TIM_GetCapture2(TIM5);
}
int scan_max(int n)
{u16 temp=0;
u16 res=0;
while(n--)
{
temp=value_get();
if(temp>res)res=temp;
};
return res;
}
int tapd_initvalue(void)
{
int buf[10];
int temp;
int i;
int j;
int d;
tpad_init(0xffff,71);
for( d=0;d<10;d++)
{
buf[d]=value_get();
}
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];
basevalue=temp/6;
if(basevalue>0xffff/2)return 1;
return 0;
}
int scan(int a)
{
static unsigned char flag=0;
int b;
int e=0;
b=scan_max(a);
if(b<(TPAD_GATE_VAL+basevalue))
{
flag++;
}
if((flag>=3)&&(b>TPAD_GATE_VAL+basevalue))
{
e=1;
flag=0;
}
else
{
e=0;
}
return e;
}
電容觸摸按鍵主函數main.c
#include "delay.h"
#include "sys.h"
#include "tpad.h"
#include "led.h"
int main(void)
{
delay_init();
LED_Init();
tapd_initvalue();
while(1)
{
delay_ms(10);
if(scan(10))
{
LED1=!LED1;
}
}
}
連按與非連按
我們定義有效數據定義爲採集時間大於某一個設定值如:b>TPAD_GATE_VAL+basevalue
我們把採集到的有效數據視爲高電平,無效的視爲低電平。
**非連按:**初始化後到採集第一次數據成功經歷了電平從低到高,想要不連續,下一次的有效數據採集一定是低電平,才能進行下一次有效數據的採集,這樣就實現了非連按
連按:不必經過從低電平到高電平階段,在一定時間內,手爲鬆開電容觸摸按鍵,則自動視爲採集下一次有效數據開始
細節問題
關於門檻值的定義,一定要先採集自己板子上的實際值再開始數據判斷,每個開發板的實際值有差異,否則可能導致一直不成功反覆修改代碼