STM32使用HAL庫實現按鍵的單擊、雙擊、長按
前言
編程開發環境:STM32CubeIDE編程
~~~~~~~~ 按鍵的單擊、雙擊、長按等在MCU編程中是比較常見且經常使用的事件,本文章基於STM32來實現,具體思路用在其餘MCU也是如此。ui
具體思路
- 初始化一個全局標記
- 按鍵中斷事件發生後置位標記
- while死循環中一直檢測這個標記,若是被置位那麼進行消抖,而後再次檢測鏈接KEY的IO是否處於按下狀態,如是則認爲本次按鍵有效
- 第一次按鍵事件有效後,啓動定時器定時300ms,在此定時期間內若是有二次按下那麼就是雙擊,若是沒有按下,等到300ms定時時間到後讀取IO電平,若是處於鬆開狀態那麼本次就是單擊事件,若是仍是按下狀態那麼就再次啓動700定時器,700ms事後再次讀取IO電平是否處於按下狀態,如是那麼就是長按。
工程配置
我是使用STM32CUBEMX配置生成的工程,配置按鍵:spa
STM32的定時器有向上、向下計數模式,向上計數就是從0計數到N而後觸發中斷,向下計數模式就是倒計時到0,因爲咱們須要二次啓動定時器來檢測長按,因此再次配置爲向下計數模式,分頻爲7199,計數3000就是定時300ms:
中斷配置:
code
代碼實現
/* * key_state位說明: * * bit[15]:按鍵事件完成標誌位 * bit[14]:按鍵中斷觸發標誌位 * bit[13]:長按事件觸發須要啓動第二次定時器標誌位 * bit[12~0]: 按鍵計數,=1:單擊 =2:雙擊 =3:長按 */ __IO uint16_t key_state = 0; int __io_putchar(int ch) { HAL_UART_Transmit(&huart1, (uint8_t*) &ch, 1, 100); return ch; } void TIM4_Stop(void) { HAL_TIM_Base_Stop_IT(&htim4); } void TIM4_Start(uint16_t timeout) { __HAL_TIM_SET_COUNTER(&htim4, timeout); HAL_TIM_Base_Start_IT(&htim4); } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == htim4.Instance) { TIM4_Stop(); if(key_state & 0X2000) // 若是是700ms的超時 { // 再次檢測按鍵若是是按下狀態,那麼就是長按事件,不然事件無效 if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET) { key_state = 0X8003; }else { key_state = 0X8000; } } else // 若是是300ms的超時 { // 300ms時間到再次檢測按鍵狀態若是仍處於按下狀態,那麼就啓動700ms的超時, // 700ms時間到後再次檢測按鍵若是是按下狀態,那麼就是長按事件了。 if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET) // 按鍵處於按下狀態 { key_state |= 0X2000; // 標記啓動了700ms的超時定時 TIM4_Start(7000); // 啓動700ms超時 } else // 按鍵處於鬆開狀態 { if((key_state & 0X1FFF) == 1) // 按鍵按下計數=1,是單擊事件 { key_state = 0X8001; }else if((key_state & 0X1FFF) >= 2) // 按鍵按下計數>=2,是雙擊事件 { key_state = 0X8002; }else // 按鍵按下計數爲其餘值,本次按鍵事件無效 { key_state = 0; } } } } } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { switch(GPIO_Pin) { case KEY_Pin: if((key_state & 0X8000) == 0) { key_state |= 0X4000; // 標記按鍵中斷觸發,在while死循環中進行消抖和的啓動定時器 } break; default:break; } } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); MX_TIM4_Init(); __HAL_TIM_CLEAR_IT(&htim4, TIM_IT_UPDATE); // STM32的定時器初始化完畢以後若是不清理中斷標記爲會有直接進入中斷的問題 while (1) { if(key_state & 0X4000) // 按鍵中斷觸發 { key_state &= ~0x4000; // 清除事件 HAL_Delay(60); // 消抖延時 // 消抖完畢以後再次檢測按鍵是否爲按下狀態,如是則本次按鍵有效,不然視爲無效並清除標識。 if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET) { if((key_state & 0X1FFF) == 0) // 首次按下時啓動300ms超時定時器 { TIM4_Start(3000); } key_state++; // 按鍵按下計數增長 } } if(key_state & 0X8000) // 按鍵事件發成了 { switch(key_state) { case 0X8001: printf("single\r\n"); break; case 0X8002: printf("double\r\n"); break; case 0X8003: printf("hold\r\n"); break; default:break; } key_state = 0; // 清除事件 } } }
實驗效果
按鍵單擊、雙擊、長按實驗效果以下:blog