實現電容觸摸按鍵控制LED(基於STM32F103ZET6)

依然採用輸入捕獲的原理來採集是否產生電容觸摸。

實驗目的:

我們將用 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
我們把採集到的有效數據視爲高電平,無效的視爲低電平。
**非連按:**初始化後到採集第一次數據成功經歷了電平從低到高,想要不連續,下一次的有效數據採集一定是低電平,才能進行下一次有效數據的採集,這樣就實現了非連按
連按:不必經過從低電平到高電平階段,在一定時間內,手爲鬆開電容觸摸按鍵,則自動視爲採集下一次有效數據開始

細節問題

關於門檻值的定義,一定要先採集自己板子上的實際值再開始數據判斷,每個開發板的實際值有差異,否則可能導致一直不成功反覆修改代碼

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