1.電容觸摸按鍵原理介紹
? 觸摸按鍵與傳統的機械按鍵相比,不僅美觀而且耐用、壽命長,它顛覆了傳統意義上的機械按鍵控制,只要輕輕觸摸,就可以實現按鍵開關的控制、量化調節甚至方向控制。觸摸按鍵已廣泛應用于手機、DVD、洗衣機等消費類電子產品中。本章我們就介紹一種簡單的觸摸按鍵:電容式觸摸按鍵。
我們PZ6806D開發板上的電容觸摸按鍵其實就是一小塊覆銅區域,也稱之為觸摸感應區。
? 通常我們會將四周的銅片與電路板地信號連通,觸摸感應區設計成方便手指觸摸大小,并將其連接在輸入捕獲通道上。??
觸摸感應區與四周的銅片區域就形成了一個電容,通過檢測電容充放
電時間即可判斷是否有觸摸。實現原理:
電容充放電公式:Vc=V0*(1-e^(-t/RC))
在上圖中,R是外接電阻,開關就是STM32管腳的內部開關。CS是觸摸感應區與電路板GND之間的雜散電容。當手指按到觸摸區時,等于增加了一個CX電容并到CS上,所以電源通過RC對電容的充電時間就會變長。
本實驗中,使用TIM5的通道2(PA1管腳)來檢測觸摸按鍵是否按下。具體步驟是:
1)在每次檢測前,我們需要先將電容Cs(或 Cs+Cx)放電,即配置PA1引腳為推挽輸出模式,輸出一個低電平,才能使電容放電。這等效于上圖中的開關閉合。
2)然后配置PA1 為浮空輸入模式,利用外部上拉電阻給電容 Cs(Cs+Cx)充電,同時開啟TIM5_CH2的輸入捕獲,配置極性為上升沿,當檢測到上升沿的時候,就認為電容充電完
成了,完成一次捕獲檢測。? 每次系統重啟時,我們執行一次捕獲檢測(可認為沒有觸摸),記錄此時捕獲到上升沿時,需要多少時間即TCS的值。
3)在后續的捕獲檢測中,即不斷重復上面的第1步和第2步,我們就可以通過與記錄的值進行對比,判斷是否發生觸摸。很明顯,如果沒有發生觸摸,每次捕獲發生的時間是基本上相等的,如果有觸摸,那么時間必然明顯延長。這樣,就知道了是否發生觸摸了
這就是電容觸摸工作的原理!搞清楚了就很簡單。
2.編寫電容觸摸按鍵控制程序
? 本實驗所要實現的功能是:通過TIM5的通道2(PA1)捕獲電容觸摸按鍵輸入信號的高電平脈寬,根據捕獲到的高電平時間長短,來判斷是否有按鍵按下,如果有按下,則翻轉D2指示燈的狀態以提示檢測到了一次按下。同時D1指示燈不斷閃爍表示系統正常運行。程序框架如下:
(1)初始化PA1管腳為TIM5通道2輸入捕獲功能,設置上升沿捕獲等
(2)讀取一次捕獲高電平的值
(3)電容觸摸按鍵初始化
(4)檢測電容觸摸按鍵是否按下
(5)編寫主函數
main.c
#include "system.h"
#include "led.h"
#include "SysTick.h"
#include "usart.h"
#include "input.h"
#include "touch_key.h"int main()
{u8 i=0;SysTick_Init(72);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中斷優先級分組LED_Init();USART1_Init(9600);Touch_Key_Init(6);//72M 6分頻后12Mwhile(1){if((Touch_Key_Scan(0)==1))//判斷觸摸是否有效{led2=led2;//有效則翻轉指示燈}i++;if(i%20 ==0){led1=!led1;//LED1閃,用來指示主程序循環是否運行}delay_ms(50);}
}
touch_key.c
#include "touch_key.h"
#include "SysTick.h"
#include "usart.h"#define Touch_ARR_MAX_Value 0xffff
u16 touch_default_value =0;void TIM5_CH2_Input_Init(u16 period,u16 prescaler)
{GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//結構體變量聲明TIM_ICInitTypeDef TIM_ICInitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//使能TIM5時鐘GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //GPIO_Init(GPIOA,&GPIO_InitStructure);TIM_TimeBaseInitStructure.TIM_Period=period; //裝入函數傳過來的自動裝載值TIM_TimeBaseInitStructure.TIM_Prescaler=prescaler; //裝入函數傳過來的分頻系數TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//1分頻(沒有分頻)TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //設置向上計數模式:從0開始計數到自動重載值后溢出產生中斷TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);//初始化TIM5各參數:自動重裝值、分頻系統、計數方式等TIM_ICInitStructure.TIM_Channel=TIM_Channel_2; //通道2TIM_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;//直接映射到TI1TIM_ICInit(TIM5,&TIM_ICInitStructure);TIM_Cmd(TIM5,ENABLE );//使能定時器}void Touch_Reset()
{GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1; 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);//將IO口輸出低電平以將觸摸按鍵的電容放電delay_ms(5);TIM_ClearFlag(TIM5,TIM_FLAG_Update|TIM_FLAG_CC2);//清除定時器的狀態標志TIM_SetCounter(TIM5,0); //設定定時器初值為0以重新計數GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//重新改為浮空模式以對電容充電GPIO_Init(GPIOA,&GPIO_InitStructure);}u16 Touch_Get_Value()
{Touch_Reset();while(TIM_GetFlagStatus(TIM5,TIM_FLAG_CC2)==0)//{if(TIM_GetCounter(TIM5)>Touch_ARR_MAX_Value-500){return TIM_GetCounter(TIM5);//如果沒有發生捕獲事件,計數器計到最大值-500后返回當前計數器的值}}return TIM_GetCapture2(TIM5);//如果發生了捕獲事件,則返回捕獲發生時的計數器值}u8 Touch_Key_Init(u8 psc)
{u8 i,j;u16 buf[10];u16 temp;TIM5_CH2_Input_Init(Touch_ARR_MAX_Value,psc);//定時器5通道2輸入捕獲初始化for (i=0; i<10;i++){buf[i]= Touch_Get_Value();//得到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;}}//去掉最小的兩個,去掉最大的兩個,余下的6個取平均值temp=0;for(i=2; i<8;i++){temp+=buf[i];}touch_default_value = temp/6;printf("touch_default_value=%d\r\n",touch_default_value);if(touch_default_value>touch_default_value/2)//這個判斷條件是通過實際調試測試決定的return 1;//如果這個值大于最大值的一半,也就對應前面的沒有發生捕獲的情況,那么認為初始化失敗//返回1 表明初始化失敗return 0; //返回0表示初始化成功,得到了沒有觸摸時的缺省充電時間}u16 Touch_Get_MaxVal(u8 n)//得到n次捕獲中的最大值
{u16 temp=0;u16 max=0;while(n--){temp=Touch_Get_Value();if(temp>max)max=temp;}return max;}//mode =0 單次掃描,mode =1,連續掃描
//返回Touch_Status,其值為1則表示觸摸有效
#define TOUCH_GATE_VAL 100
u8 Touch_Key_Scan(u8 mode)
{u8 Touch_Status;u8 sample =3;u16 MaxVal =0;static u8 keyen =0;if(mode)//mode如為1,則表示連續掃描,因此,每次調用Touch_Key_Scan時都將keyen=0,所以每次都能得到觸摸值{ //反之,因為keyen后續=3,所以需要三次之后,才能降為0,才能得到觸摸值sample =6;keyen=0;}MaxVal=Touch_Get_MaxVal(sample);//得到三次采樣中的最大值if(MaxVal>(touch_default_value+TOUCH_GATE_VAL)&&MaxVal<(10*touch_default_value)){if((keyen==0)&&(touch_default_value+TOUCH_GATE_VAL)){Touch_Status=1;}}printf("觸摸后捕獲高電平值為:%d\r\n",MaxVal);keyen=3;if(keyen) keyen--;return Touch_Status;}
? ?touch_key.h
#ifndef _touch_key_H
#define _touch_key_H#include "system.h"void TIM5_CH2_Input_Init(u16 period,u16 prescaler);
u8 Touch_Key_Init(u8 psc);
u8 Touch_Key_Scan(u8 mode);#endif
這個程序因為只能在PZ6806D開發板上才可以運行,而我手上的是PZ6806L,所以沒有實際燒錄測試,但原理我是完全理解的。
因此,如果在PZ6806L上接出一塊小的觸摸板,這實驗是可以做成功的,這沒有什么疑問。