文章目錄
- 前言
- 硬體分析
- 原理
- 源程式
- 主函式
- TpadInit
- GetTimeUntoched
- GetTimeCharge
- TpadScan
前言
本文主要介紹電容按鍵的原理與使用方法,主要使用的ARM資源為捕獲模塊,并不涉及新的模塊,所以本文內容不涉及新的HAL庫內容的介紹,
關于捕獲模塊部分,可以參考以下三篇博客:
- STM32F429第二十二篇之高級定時器——捕獲功能原理
- STM32F429第二十三篇之捕獲實驗詳解
- STM32CubeMX第六篇之捕獲實驗
本文主要參考資料:
- 煉訓良,楊森.STM32庫開發實戰指南——基于STM32F4.機械工業出版社
本實驗的源代碼如下所示:
https://github.com/zhenhaiyang/keil
本實驗的主要功能為:
- 通過電容按鍵改變LED1的狀態,每次點擊電容按鍵,LED的狀態置反,
- LED0按照1s的周期切換狀態,
硬體分析

本實驗的硬體如上圖所示:
- 將TPAD與STM_ADC短接,
- STM_ADC即為PA5,
所以,相當于將按鍵TPAD與PA5直接短接,
原理
電容器就是可以容納電荷的器件,在兩塊金屬板之間添加絕緣體就構成了最簡答的電容器,而在PCB中,可以設計為一塊帶有上拉電阻的銅塊,而其被接地的銅塊包圍住,這就構成了最簡單的電容接觸按鍵,當電路板的形狀固定時,電容接觸按鍵的容值也基本上是固定的,
此時,若將手指接觸到PCB板,電容接觸按鍵的容值就會改變,這是因為在金屬板和手指之間又新增了一個等效的電容Cs,如下圖所示:


綜上,電容接觸按鍵的原理就是:當手指接觸到按鍵的實收,按鍵的等效電容就會增大,因此,其充電時間增加,
所以,本實驗只需要檢測電容按鍵的充電時間,即可判斷手指是否接觸按鍵,
具體來說,可以大致分成以下幾個步驟:
- 將PA5與電容短接——硬體分析已經實作,
- 將PA5用作推挽輸出,且輸出低電平,使電容放電,
- 將PA5用作TIM2CH1的捕獲管腳,電容在上拉的作用下開始充電,PA5捕獲電容充電時間,
- 通過判斷充電時間長短,來確定按鍵是否被按下,
源程式
主函式
/**
******************************************************************************
* @file main.c
* @author zhy
* @version 1.0
* @date 2021-04-26
* @brief 電容按鍵使用:
* 1.通過按下電容按鍵,改變LED1的狀態
* 2.LED0按照1s的頻率改變其狀態
* 管腳分配:
* PA5:用于捕獲電容充電時間
* 使用資源:
* TIM2CH1
******************************************************************************
*/
#include "stdio.h"
#include "stm32f4xx_hal.h"
#include "tpad.h"
#include "capture.h"
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
int main()
{
/* 1.變數初始化 */
TpadStatus tpadStatus = TPAD_HIGH;
uint32_t timeInMs = HAL_GetTick();
uint32_t timeNewMS = 0;
/* 2.硬體初始化 */
HAL_Init();
SystemClock_Config();
UartInit();
LedInit();
CaptureInit();
TpadInit();
/* 3.while回圈 */
while (1)
{
timeNewMS = HAL_GetTick();
TpadScan(&tpadStatus);
if (tpadStatus == TPAD_RISING)
{
LED1 = !LED1;
}
if (timeNewMS - timeInMs >= 1000)
{
timeInMs = timeNewMS;
LED0 = !LED0;
}
}
}
主函式可以分成三個部分:
- 變數初始化
- 硬體初始化
- while回圈
在硬體初始化中,CaptureInit() 初始化TIM2CH1,其初始化的方法與之前博客介紹的基本相同,不再詳細介紹,TpadInit()為電容觸摸按鍵初始化,下文詳細分析,
在while回圈中,函式TpadScan()將按鍵的處理結果通過傳遞到變數tpadStatus中,若檢測到上升沿,則將LED1置反,需要注意的是,此處的上升沿是邏輯上的上升沿指的是:按鍵由無效狀態變成有效狀態,然后通過Tick時鐘判斷當前時間,從而實作LED0的1s周期變化,
TpadInit
/**
* @brief 觸摸按鍵初始化——將按鍵放電
* @note 無
* @param {*}無
* @retval 無
*/
void TpadInit(void)
{
/* 1.時鐘初始化 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* 2.GPIO初始化 */
GPIO_InitTypeDef initGpio;
initGpio.Pin = GPIO_PIN_5; //pin5
initGpio.Mode = GPIO_MODE_OUTPUT_PP; //推挽輸出
initGpio.Speed = GPIO_SPEED_FAST; //速度:快
HAL_GPIO_Init(GPIOA, &initGpio); //初始化PA5
/* 3.初始化 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); //PA5輸出低電平
/* 4.獲取為觸摸時的電容充電時間 */
GetTimeUntoched();
}
該函式十分簡單,首先,將PA5初始化為輸出低電平的推挽輸出,然后呼叫GetTimeUntoched()函式來求取未接觸按鍵時,按鍵的充電時間,
GetTimeUntoched
/**
* @brief 獲取初始時間
* @note 無
* @param {*}無
* @retval 無
*/
void GetTimeUntoched(void)
{
int16_t temp[10] = {0};
for (uint8_t i = 0; i < 10; i++)
{
GetTimeCharge((uint16_t *)(void *)temp + i);
}
int16_t min = 0;
int16_t max = 0;
int16_t sub = 0;
int16_t average = 0;
for (uint8_t i = 1; i < 10; i++)
{
sub = temp[i] - temp[0]; //獲取差值
min = min > sub ? sub : min; //差值的最小值
max = max < sub ? sub : max; //差值的最大值
average += sub; //差值的和
}
average -= min + max; //剔除最小與最大值
average >>= 3; //差值的平均
timeUntouched = average + temp[0]; //實際的平均值
printf("timeUntouched:%d\n", timeUntouched); //輸出未按下按鍵時的充電時間
}
通過呼叫函式GetTimeCharge()來獲取10次充電時間,默認認為在函式初始化的時候,手指沒有觸碰按鍵,然后通過10次求平均的方式,來獲得比較準確的按鍵的充電時間,將其存盤在全域變數timeUntouched中,且通過串口輸出,
GetTimeCharge
/**
* @brief 獲取捕獲時間
* @note 無
* @param {uint32_t} *time 捕獲時間
* @retval 無
*/
void GetTimeCharge(uint16_t *time)
{
/* PA5:推挽輸出 */
GPIOA->MODER &= ~GPIO_MODER_MODE5;
GPIOA->MODER |= GPIO_MODER_MODE5_0;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_5;
/* 輸出低電平 */
PAout.bit5 = 0;
delay_us(50);
/* PA5:復用為TIM2CH1 */
GPIOA->MODER &= ~GPIO_MODER_MODE5;
GPIOA->MODER |= GPIO_MODER_MODE5_1;
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL5;
GPIOA->AFR[0] |= GPIO_AFRL_AFRL5_0; //0b0001:AF1
TIM2->SR = 0x0000; //清空中斷標志
TIM2->CR1 |= TIM_CR1_CEN; //打開計數器
while (!(TIM2->SR & TIM_SR_CC1IF)) //等待充電完成
;
TIM2->SR ^= TIM_SR_CC1IF; //清除中斷標記
TIM2->CNT = 0; //清空計數器
TIM2->CR1 &= ~TIM_CR1_CEN; //關閉計數器
*time = TIM2->CCR1; //獲取捕獲值
printf("time:%d\n", *time); //發送時間值
}
該函式用于獲取電容的充電時間,
- 將PA5配置為推挽輸出且輸出低電平,延時一段時間,讓電容充分放電,
- 將PA5配置為復用功能,復用做TIM2CH1的上升沿檢測管腳,打開計數器開始計時,
- 通過while回圈等待上升沿觸發,然后關閉計時器,清零計數器等操作后,將捕獲的電容充電時間通過引數輸出,且用串口列印,
TpadScan
/**
* @brief 判斷按鍵是否有按下
* @note 無
* @param {TpadStatus} *tpadStatus 按鍵狀態
* @retval 無
*/
void TpadScan(TpadStatus *tpadStatus)
{
static uint8_t timeHigh = 0;
uint16_t timeCharge = 0;
GetTimeCharge(&timeCharge);
if (timeCharge > timeUntouched * 5 >> 2) //若是平均未觸碰充電時間的5/4,則判斷是按鍵按下
{
timeHigh = 3;
switch (*tpadStatus)
{
case TPAD_LOW:
*tpadStatus = TPAD_RISING;
break;
case TPAD_RISING:
*tpadStatus = TPAD_HIGH;
break;
default:
break;
}
}
else //若檢測到時間不足
{
*tpadStatus = TPAD_HIGH;
if (timeHigh > 0)
{
timeHigh--;
}
else
{
*tpadStatus = TPAD_LOW;
}
}
}
首先,判斷充電時間是否為原有時間的5/4,若復合條件,則認為手指接觸了按鍵,否則,則認為手指離開了按鍵,
若條件由不符合變為復合,則認為這是一個上升沿,否則,其處于高電平或者低電平,
變數timeHigh相當于為濾波器,至少3次連續的條件不符合才認為其為真,這是為了防止按鍵抖動,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/280973.html
標籤:其他
上一篇:2021-04-27
