[STM32CubeMX]學習筆記2:GPIO按鍵外部中斷控制LED狀態

 

MCU:STM32F103ZET6

IDE:  MDK-ARM V5 +STM32CubeMX5.0.0

 

本次實現的功能是通過按鍵的外部中斷,來改變對應LED的狀態,需要將按鍵的I/O端口映射到外部中斷線上。

需要初始化的按鍵I/O口分別對應PA0\PE2\PE3\PE4,LEDI/O口分別爲PC0\PC1\PC2\PC3\PC4。

一. 在 Pinout&Configuration---System Core中:

  • 1. 首先設置時鐘RCC的HSE(外部高速時鐘)爲晶振模式:Crystal/ceramic Resonator

  • 2. 設置系統SYS的Debug爲Serial Wire:

  • 3. 設置GPIO中的LED管腳。

根據MCU的硬件原理圖可以看出LED均是低電平觸發,且對應的端口爲PC0-PC7,因此在使能是應開啓GPIOC端口。

在MCU管腳圖中找到PC0-PC4管腳,右鍵將管腳設置爲GPIO_Output輸出模式。

此時在System Core的GPIO中,會出現對應端口,點擊端口將GPIO output level設置爲low,將User Label定義爲LED。

  • 4. 設置按鍵映射到外部中斷線上。

選擇PA0的端口爲GPIO_EXTI0,PE2\PE3\PE4的端口依次爲GPIO_EXTI2\3\4。

         

此時在System Core的GPIO中,會出現對應I/O端口,按鍵部分的硬件原理圖:

KEY_UP應爲上升沿觸發,並外接下拉電阻,點擊端口將GPIO mode設置爲External Interrupt Mode with Rising edge trigger detection,將GPIO Pull-up/Pull-down配置爲Pull-down,將User Label定義爲KEY_UP。

K1\K2\K3應爲下降沿觸發,點擊端口將GPIO mode設置爲External Interrupt Mode with Falling edge trigger detection,將User Label定義爲KEY_LEFT\KEY_DOWN\KEY_RIGHT。

接着在GPIO-Configuration中使能四個GPIO的中斷:

*在將端口映射到EXTI線上後,SYS會出現以下警告:表示紅色部分的模式不可使用,且因爲PA0本身對應的是系統喚醒功能,因此System Wake-UP的功能也無法被選中。

二.在 Clock Configuration中:

配置時鐘爲72 Mhz。

三.在 Project Manager---Project中:

  • 1.設置項目的名稱以及保存的位置,選擇Toolchain/IDE爲MDK-ARM V5,

Tips:最好把Linker Settings中的Minimum Heap Size設置爲0x600。

  • 2.在Code Generator選項中如下勾選:

最後點Generate Code生成代碼,並選擇“Open Project”:

四.代碼分析與改寫:

在生成的stm32f1xx_it.c中是系統中斷的相關函數,找到四個外部中斷的中斷服務函數:

void EXTI0_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI0_IRQn 0 */

  /* USER CODE END EXTI0_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
  /* USER CODE BEGIN EXTI0_IRQn 1 */

  /* USER CODE END EXTI0_IRQn 1 */
}

進入“HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0)”函數後發現定義如下:

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

服務函數中第一步對中斷口清零,隨後進入回調函數“HAL_GPIO_EXTI_Callback”:

__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */
}

回調函數是_weak符號弱定義,(庫中定義的弱符號可以被用戶定義的強符號覆蓋,從而使程序可以使用自定義版本的庫函數)但是回調函數中沒有定義任何操作,因此將我們要實現的功能編寫在回調函數中。

*關於強弱符號的詳細定義見:強符號 弱符號

在main.c中添加下列程序:

/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin==KEY_UP_Pin)
	{
		for(long i = 1;i<72000;i++){}; //消除抖動
		if(HAL_GPIO_ReadPin(KEY_UP_GPIO_Port,KEY_UP_Pin)==1)
		{
			HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
		}
	}
	else if(GPIO_Pin==KEY_DOWN_Pin)
	{
		for(long i = 1;i<72000;i++){};
		if(HAL_GPIO_ReadPin(KEY_DOWN_GPIO_Port,KEY_DOWN_Pin)==0)
		{
		  HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
		}
	}
	else if(GPIO_Pin==KEY_LEFT_Pin)
	{
		for(long i = 1;i<72000;i++){};
		if(HAL_GPIO_ReadPin(KEY_LEFT_GPIO_Port,KEY_LEFT_Pin)==0)
		{
		HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);
		}
	}
	else if(GPIO_Pin==KEY_RIGHT_Pin)
	{
		for(long i = 1;i<72000;i++){};
		if(HAL_GPIO_ReadPin(KEY_RIGHT_GPIO_Port,KEY_RIGHT_Pin)==0)
		{
		HAL_GPIO_TogglePin(LED4_GPIO_Port,LED4_Pin);
		}
	}
	__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
}
/* USER CODE END 4 */

在編程中一般不在中斷函數中添加延時函數,這裏有個BUG:當PA0按下時,只要進入HAL_delay函數就無法跳出循環,解決未果,因此用上面的for循環代替了延時函數,實現消抖的功能)。

以KEY_UP按下爲例:當KEY_UP按鍵按下後,

“GPIO_Pin==KEY_UP_Pin”

進入for循環(相當於小型的延時函數),循環一段時間後判斷KEY_UP管腳是否仍未高電平:

“HAL_GPIO_ReadPin(KEY_UP_GPIO_Port,KEY_UP_Pin)==1”

這裏因爲UP管腳是高電平觸發,其餘管腳是低電平觸發,因此判斷條件不同。

若此時KEY_UP仍爲高電平,說明不是抖動引起,則改變對應LED的狀態:

“HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin)”

最終在退出函數時要清除中斷標誌位,避免對下次按鍵的判斷造成影響。

至此,編譯後無錯並下載到開發板中,就完成了通過STM32CubeMX調用HAL庫實現按鍵控制LED的效果程序。

 

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