一:什么是開關抖動?
當我們按下按鈕或撥動開關或微動開關時,兩個金屬部件會接觸以短路電源。但它們不會立即連接,而是金屬部件在實際穩(wěn)定連接之前連接和斷開幾次。
釋放按鈕時也會發(fā)生同樣的事情。這會導致誤觸發(fā)或多次觸發(fā),例如多次按下按鈕。這就像一個彈跳的球從高處落下,它一直在表面彈跳,直到它靜止。
換句話說,我們可以說開關彈跳是任何開關的非理想行為,它會生成單個輸入的多個轉換。當我們處理電源電路時,開關彈跳不是主要問題,但當我們處理邏輯或數(shù)字電路時,它會引起問題。因此,為了消除電路中的抖動,使用了開關去抖動電路。
二:電路及波形
首先,我們將演示沒有開關去抖動的電路
您還可以在按下按鈕時在示波器中看到波形。它顯示在按鈕切換期間發(fā)生了多少彈跳。
三:硬件去抖動
防止電路開關彈跳的常用方法有3種。
硬件去抖
RC 去抖
開關去抖IC
01
硬件電路去抖
在硬件去抖動技術中,我們使用 S-R 觸發(fā)器來防止電路發(fā)生開關抖動。這是所有方法中最好的去抖動方法。
該電路由兩個與非門(74HC00 IC)組成,形成一個 SR 觸發(fā)器。正如您在電路圖中看到的,只要撥動開關切換到 A 側,輸出邏輯就會變?yōu)椤案摺?。在這里,我們使用示波器來檢測彈跳。而且,正如您在下面給出的波形中看到的那樣,邏輯正在以輕微的曲線移動而不是彈跳。電路中使用的電阻是上拉電阻。 每當開關在觸點之間移動以產(chǎn)生反彈時,觸發(fā)器都會保持輸出,因為“0”是從與非門的輸出反饋的。
02
R-C 去抖
R-C 僅由其名稱定義,該電路使用 RC 網(wǎng)絡來防止開關彈跳。電路中的電容器濾除開關信號的瞬間變化。當開關處于打開狀態(tài)時,電容器兩端的電壓保持為零。最初,當開關打開時,電容器通過 R1 和 R2 電阻器充電。
當開關閉合時,電容器開始放電至零,因此反相施密特觸發(fā)器輸入端的電壓為零,因此輸出變?yōu)楦唠娖健?
在彈跳情況下,電容器停止 Vin 處的電壓,直到它達到 Vcc 或接地。
為了提高 RC 去抖動的速度,我們可以連接一個二極管,如下圖所示。因此,它減少了電容器的充電時間。
03
開關去抖IC
市場上有用于開關去抖動的 IC。一些去抖 IC 是 MAX6816、MC14490 和 LS118。
下面是使用MAX6818進行開關去抖的電路圖。
所以在這里,我們學習了按鈕如何產(chǎn)生開關反彈效應,以及如何通過使用硬件的方式來防止按鍵抖動。
四:軟件消抖
我們都知道,并且也是我們使用最多的場合是通過軟件實現(xiàn)按鍵消抖。
最簡單的方式是增加延遲以消除軟件去抖。添加延遲會強制控制器在特定時間段內停止,但在程序中添加延遲并不是一個好的選擇,因為它會暫停程序并增加處理時間。最好的方法是在代碼中使用中斷來進行軟件彈跳。
01
軟件延時
sbit KEY = P1^3; ///按鍵讀取函數(shù) uint8_t GetKey(void) { if(KEY == 1) { DelayMs(20); //延時消抖 if(KEY == 1) { return 1; } else { return 0; } } else { return 0; } }上面是最簡單的軟件延時方法,也可以通過多個按鍵組合增加相關軟件濾波的方式進行按鍵判斷,其實原理相似。
但是這種純延時的實現(xiàn)方式太過暴力,在延時的時候一直占用cpu的資源,如果在延時的時候,有其他外部中斷或者搶占事件,系統(tǒng)完全沒有響應的。
所以我們CPU需要一個獨立的定時裝置,來完成這個計時工作,而且需要在計時時間到達時再檢測一次按鍵的電平值。
02
中斷消抖
首先初始化管腳,打開管腳的外部中斷:
/*Configure GPIO pins : KEY_1_Pin KEY_2_Pin */ GPIO_InitStruct.Pin = KEY_1_Pin|KEY_2_Pin; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); /* EXTI interrupt init*/ HAL_NVIC_SetPriority(EXTI15_10_IRQn, 5, 0); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);初始化TIM1,打開其update中斷:
static void MX_TIM1_Init(void) { htim1.Instance = TIM1; htim1.Init.Prescaler = 7200 - 1; // 72000000 / 7200 = 10000 hz 0.01ms htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 200 - 1; // 200 * 0.01 = 20ms htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim1) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); }
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base) { if(htim_base->Instance==TIM1) { /* Peripheral clock enable */ __HAL_RCC_TIM1_CLK_ENABLE(); /* USER CODE BEGIN TIM1_MspInit 1 */ HAL_NVIC_SetPriority(TIM1_UP_IRQn,1,3); HAL_NVIC_EnableIRQ(TIM1_UP_IRQn); } }在stm32f1xx_hal_it.c中去注冊中斷回調函數(shù)(關鍵的步驟,需要在按鍵中斷處理函數(shù)中打開定時器,開始計時):
void EXTI15_10_IRQHandler(void) // 按鍵的中斷處理函數(shù) { HAL_TIM_Base_Start_IT(&htim1); // 開啟定時器1,開始計時 printf("key down "); __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_11); __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_12); }定時器的中斷處理函數(shù):
void TIM1_UP_IRQHandler(void) { HAL_TIM_IRQHandler(&htim1); //這個是所有定時器處理回調的入口,在這個函數(shù)里對應定時器多種中斷情況的中斷回調,需要找到update的回調函數(shù) printf("TIM IRQ "); } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) // 定時器update中斷處理回調函數(shù) { /* USER CODE BEGIN Callback 0 */ /* USER CODE END Callback 0 */ if (htim->Instance == TIM2) { HAL_IncTick(); } if (htim->Instance == TIM1) { // 在這里選擇tim1 printf("TIM1 updata "); HAL_TIM_Base_Stop_IT(&htim1); // 關閉tim1 及清除中斷 if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_11) ) //再次判斷管腳的電平 { printf("KEY1 be pressed!!! "); } if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_12) )//再次判斷管腳的電平 { printf("KEY2 be pressed!!! "); } } /* USER CODE BEGIN Callback 1 */ /* USER CODE END Callback 1 */ }
總結一下,實現(xiàn)用定時器中斷來完成按鍵延時去抖的關鍵步驟:
1. 初始化GPIO腳,初始化TIM ,算好時間,填入分頻值。
2. 打開GPIO中斷,在中斷處理函數(shù)中打開定時器,讓其計數(shù)。
3. 定時器溢出中斷函數(shù)中,再次判斷按鍵電平值。關閉定時器,清除pending。
審核編輯:劉清
-
示波器
+關注
關注
113文章
6164瀏覽量
184316 -
定時器中斷
+關注
關注
0文章
49瀏覽量
11157 -
按鍵消抖
+關注
關注
2文章
27瀏覽量
10437
原文標題:按鍵消抖常用的軟硬件方法
文章出處:【微信號:玩轉單片機與嵌入式,微信公眾號:玩轉單片機與嵌入式】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論