STM32電容按鍵

STM32電容按鍵實驗

一、電容按鍵實驗簡介

觸摸按鍵相對於傳統的機械按鍵有壽命長、佔用空間少、易於操作等諸多優點。大家看看如今的手機,觸摸屏、觸摸按鍵大行其道,而傳統的機械按鍵,正在逐步從手機上面消失。本章,我們將給大家介紹一種簡單的觸摸按鍵:電容式觸摸按鍵。我們將利用精英 STM32F103 上的觸摸按鍵(TPAD),來實現對 DS1 的亮滅控制。這裏 TPAD其實就是精英 STM32F103 上的一小塊覆銅區域,實現原理如圖 16.1.1 所示:
Alt
這裏我們使用的是檢測電容充放電時間的方法來判斷是否有觸摸, 圖中 R 是外接的電容充電電阻, Cs 是沒有觸摸按下時 TPAD 與 PCB 之間的雜散電容。 而 Cx 則是有手指按下的時候,手指與 TPAD 之間形成的電容。圖中的開關是電容放電開關(由實際使用時,由 STM32F1 的IO 代替)。
先用開關將 Cs(或 Cs+Cx)上的電放盡,然後斷開開關,讓 R 給 Cs(或 Cs+Cx)充電,當沒有手指觸摸的時候, Cs 的充電曲線如圖中的 A 曲線。 而當有手指觸摸的時候, 手指和 TPAD之間引入了新的電容 Cx,此時 Cs+Cx 的充電曲線如圖中的 B 曲線。 從上圖可以看出, A、 B兩種情況下, Vc 達到 Vth 的時間分別爲 Tcs 和 Tcs+Tcx。
其中, 除了 Cs 和 Cx 我們需要計算,其他都是已知的,根據電容充放電公式:Vc=V0*(1-e^(-t/RC))
其中 Vc 爲電容電壓, V0 爲充電電壓, R 爲充電電阻, C 爲電容容值, e 爲自然底數, t 爲充電時間。根據這個公式,我們就可以計算出 Cs 和 Cx。 利用這個公式,我們還可以把精英開發板作爲一個簡單的電容計,直接可以測電容容量了,有興趣的朋友可以搗鼓下。
其實我們只要能夠區分 Tcs 和 Tcs+Tcx,就已經可以實現觸摸檢測了,當充電時間在 Tcs 附近,就可以認爲沒有觸摸,而當充電時間大於 Tcs+Tx 時,就認爲有觸摸按下(Tx爲檢測閥值)。
本次實驗我們使用 PA1(TIM5_CH2)來檢測 TPAD 是否有觸摸,在每次檢測之前,我們先配置PA1 爲推輓輸出,將電容 Cs(或 Cs+Cx)放電,然後配置 PA1 爲浮空輸入,利用外部上拉電阻給電容 Cs(Cs+Cx)充電,同時開啓 TIM5_CH2 的輸入捕獲,檢測上升沿,當檢測到上升沿的時候,就認爲電容充電完成了,完成一次捕獲檢測。
在 MCU 每次復位重啓的時候,我們執行一次捕獲檢測(可以認爲沒觸摸),記錄此時的值,記爲 tpad_default_val,作爲判斷的依據。在後續的捕獲檢測,我們就通過與 tpad_default_val 的對比,來判斷是不是有觸摸發生。

二、硬件設計

本實驗用到的硬件資源有:
1) 指示燈 DS0 和 DS1
2) 定時器 TIM5
3) 觸摸按鍵 TPAD
我們需要通過 TIM5_CH2(PA1)採集 TPAD 的信號,所以本實驗需要用跳線帽短接多功能端口(P7)的 TPAD 和 ADC,以實現 TPAD 連接到 PA1。如圖 16.2.1所示:在這裏插入圖片描述

三、軟件設計

在 HARDWARE 文件夾下新建 TPAD 的文件夾。然後打開 USER 文件夾下的工程,新建一個 tpad.c 的文件和 tpad.h 的頭文件,保存在 TAPD 文件夾下,並將 TPAD 文件夾加入頭文件包含路徑。我們在 tpad.c 裏輸入如下代碼:

#define TPAD_ARR_MAX_VAL 0XFFFF //最大的 ARR 值
vu16 tpad_default_val=0;//空載的時候(沒有手按下),計數器需要的時間
//初始化觸摸按鍵
//獲得空載的時候觸摸按鍵的取值.
//systick:系統時鐘頻率
//返回值:0,初始化成功;1,初始化失敗
u8 TPAD_Init(u8 systick)
{
u16 buf[10];
u16 temp;
u8 j,i;
TIM5_CH2_Cap_Init(TPAD_ARR_MAX_VAL,systick-1);//以 1Mhz 的頻率計數
for(i=0;i<10;i++)//連續讀取 10 次
{
buf[i]=TPAD_Get_Val(); 
delay_ms(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];//取中間的 8 個數據進行平均
tpad_default_val=temp/6;
printf("tpad_default_val:%d\r\n",tpad_default_val);
if(tpad_default_val>TPAD_ARR_MAX_VAL/2)return 1;//初始化數值不正常!
return 0;
}
//復位一次
//釋放電容電量,並清除定時器的計數值
void TPAD_Reset(void)
{
GPIOA->CRL&=0XFFFFFF0F; //PA1 輸入
GPIOA->CRL|=0X00000030; 
GPIOA->ODR&=~(1<<1); 
delay_ms(5);
TIM5->SR=0; 
TIM5->CNT=0; //推輓輸出
//輸出 0,放電//清除標記
//歸零GPIOA->CRL&=0XFFFFFF0F; //PA1 輸入GPIOA->CRL|=0X00000040; //浮空輸入}
//得到定時器捕獲值
//如果超時,則直接返回定時器的計數值.
//返回值:捕獲值/計數值(超時的情況下返回)
u16 TPAD_Get_Val(void)
{
TPAD_Reset();
while((TIM5->SR&0X04)==0)//等待捕獲上升沿
{
if(TIM5->CNT>TPAD_ARR_MAX_VAL-500)return TIM5->CNT;//超時了 
};
return TIM5->CCR2;
}
//讀取 n 次,取最大值
//n:連續獲取的次數
//返回值: n 次讀數裏面讀到的最大讀數值
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,可以開始檢測;>0,還不能開始檢測
u8 res=0;
u8 sample=3; 
u16 rval;
if(mode)
{
sample=6; 
keyen=0; //默認採樣次數爲 3 次//支持連按的時候,設置採樣次數爲 6 次
//支持連按}
rval=TPAD_Get_MaxVal(sample);
if(rval>(tpad_default_val+TPAD_GATE_VAL))
//大於 tpad_default_val+TPAD_GATE_VAL,有效
{
if(keyen==0)res=1; 
//printf("r:%d\r\n",rval);
keyen=3; //keyen==0,有效//至少要再過 3 次之後才能按鍵有效}
if(keyen)keyen--;
return res;
} 

//定時器 5 通道 2 輸入捕獲配置
//arr:自動重裝值
//psc:時鐘預分頻數
void TIM5_CH2_Cap_Init(u16 arr,u16 psc)
{
//此部分需手動修改 IO 口設置
RCC->APB1ENR|=1<<3; //TIM5 時鐘使能
RCC->APB2ENR|=1<<2; //使能 PORTA 時鐘
GPIOA->CRL&=0XFFFFFF0F; //PA1 輸入
GPIOA->CRL|=0X00000040; //浮空輸入
TIM5->ARR=arr; //設定計數器自動重裝值//剛好 1ms
TIM5->PSC=psc; //預分頻器,1M 的計數頻率
TIM5->CCMR1|=1<<8; //CC2S=01 選擇輸入端 IC2 映射到 TI2 上
TIM5->CCMR1|=0<<12; //IC2F=0011 配置輸入濾波器 8 個定時器時鐘週期濾波
TIM5->CCMR1|=0<<10; //IC2PS=00 配置輸入分頻,不分頻
TIM5->CCER|=0<<5; //CC2P=0 上升沿捕獲
TIM5->CCER|=1<<4; //CC2E=1 允許捕獲計數器的值到捕獲寄存器中
TIM5->CR1|=0x01; //使能定時器 5
} 

此部分代碼包含 6 個函數,我們將介紹其中 4 個比較重要的函數:TIM5_CH2_Cap_Init、TPAD_Get_Val、 TPAD_Init 和 TPAD_Scan。
首先介紹TIM5_CH2_Cap_Init函數,該函數和上一章的輸入捕獲函數基本一樣,不同的是,這裏我們設置的是 CH2 通道,並開啓了輸入濾波器。通過該函數的設置,我們將可以捕獲 PA1上的上升沿。我們再來看看 TPAD_Get_Val 函數,該函數用於得到定時器的一次捕獲值。該函數先調用TPAD_Reset,將電容放電,同時設置 TIM5_CNT 寄存器爲 0,然後死循環等待發生上升沿捕獲(或計數出),將捕獲到的值(或溢出值)作爲返回值返回。
接着我們介紹 TPAD_Init 函數,該函數用於初始化輸入捕獲,並獲取默認的 TPAD 值。該函數有一個參數,用來傳遞分頻係數,其實是爲了配置TIM5_CH2_Cap_Init 的分頻係數,該值設置的越小,觸摸越靈敏,不過也越容易受干擾。在該函數中連續 10 次讀取 TPAD 值,將這些值升序排列後取中間 6 個值再做平均(這樣做的目的是儘量減少誤差),並賦值給tpad_default_val,用於後續觸摸判斷的標準。
最後,我們來看看 TPAD_Scan 函數,該函數用於掃描 TPAD 是否有觸摸, 該函數的參數mode,用於設置是否支持連續觸發。返回值如果是 0,說明沒有觸摸,如果是 1,則說明有觸摸。該函數同樣包含了一個靜態變量,用於檢測控制,類似第八章的 KEY_Scan 函數。所以該函數同樣是不可重入的。在函數中,我們通過連續讀取 3 次(不支持連續按的時候)TPAD 的值,取這他們的最大值,和 tpad_default_val+TPAD_GATE_VAL 比較,如果大於則說明有觸摸,如果小於,則說明無觸摸。其中 tpad_default_val 是我們在調用 TPAD_Init 函數的時候得到的值,而 TPAD_GATE_VAL 則是我們設定的一個門限值(這個大家可以通過實驗數據得出,根據實際情況選擇適合的值就好了,越小越靈敏),這裏我們設置爲 100。該函數,我們還做了一些其他的條件限制,讓觸摸按鍵有更好的效果,這個就請大家看代碼自行參悟了。
我們將 tpad.c 文件保存,然後加入到 HARDWARE 組下。接下來,在 tpad.h 文件裏,我們輸入如下代碼:

#ifndef __TPAD_H
#define __TPAD_H
#include "sys.h"
#include "timer.h"
//空載的時候(沒有手按下),計數器需要的時間
//這個值應該在每次開機的時候被初始化一次
extern vu16 tpad_default_val;
void TPAD_Reset(void);
u16 TPAD_Get_Val(void);
u16 TPAD_Get_MaxVal(u8 n);
u8 TPAD_Init(u8 systick);
u8 TPAD_Scan(u8 mode);
void TIM5_CH2_Cap_Init(u16 arr,u16 psc);
#endif 

主函數的代碼主要如下:

int main(void)
{
u8 t=0;
Stm32_Clock_Init(9); 
uart_init(72,115200); 
delay_init(72); 
LED_Init(); 
TPAD_Init(6); 
while(1)
{//系統時鐘設置
//串口初始化爲 115200
//延時初始化
//初始化與 LED 連接的硬件接口
//初始化觸摸按鍵if(TPAD_Scan(0)) //成功捕獲到了一次上升沿(此函數執行時間至少 15ms)
{
LED1=!LED1;//LED1 取反
}
t++;
if(t==15)
{
t=0;
LED0=!LED0;//LED0 取反,提示程序正在運行
}
delay_ms(10);
}
} 


main 函數比較簡單, TPAD_Init(6)函數執行之後,就開始觸摸按鍵的掃描,當有觸摸的時候,對 DS1 取反,而 DS0 則有規律的間隔取反,提示程序正在運行。 注意在修改 main 函數之後,還需要在 test.c 裏面添加 tpad.h 頭文件,否則會報錯哦。
這裏還要提醒一下大家,不要把 uart_init(72,115200);去掉,因爲在 TPAD_Init 函數裏我們有用到 printf,如果你去掉了 uart_init,就會導致 printf 無法執行,從而死機。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章