天天看點

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

一、建立工程

1. 打開 STM32CubeMX 軟體,點選“建立工程”

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

2. 選擇 MCU 和封裝

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

3. 配置時鐘

RCC 設定,選擇 HSE(外部高速時鐘) 為 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

選擇 Clock Configuration,配置系統時鐘 SYSCLK 為 72MHz

修改 HCLK 的值為 72 後,輸入回車,軟體會自動修改所有配置

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

4. 配置調試模式

非常重要的一步,否則會造成第一次燒錄程式後續無法識别調試器

SYS 設定,選擇 Debug 為 Serial Wire

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

二、EXTI外部中斷

2.1 參數配置

System Core

中選擇

GPIO

設定。

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

在右邊圖中找到按鍵對應引腳,選擇

GPIO_EXTIx

這裡的

x

是指挂載在中斷線幾上,如 GPIO_EXTI0 就是挂載在中斷線0上。

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項
  • 開啟下降沿觸發中斷:即在 按下按鍵時 電平由高變為低時觸發,則在

    GPIO mode

    中選擇

    External Interrupt Mode with Falling edge trigger detection

  • 開啟上升沿觸發中斷:即在 按下按鍵後松開時 電平由低變為高時觸發,則在

    GPIO mode

    中選擇

    External Interrupt Mode with Rising edge trigger detection

  • 開啟下降沿上升沿都觸發中斷:即在 按下時觸發,松開時再次觸發,則在

    GPIO mode

    中選擇

    External Interrupt Mode with Rising/Falling edge trigger detection

  • 如果硬體上已外部上拉或下拉,則在

    GPIO Pull-up/Pull-down

    中選擇

    No pull-up and no pull-down

    既不上拉也不下拉。
  • 如果硬體外部沒有上拉,則在

    GPIO Pull-up/Pull-down

    中選擇

    Pull-up

    内部上拉電阻。
    STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項
    配置

    NVIC

    中斷優先級分組規則

    Priority Group

    預設為4個比特位,一般情況下不改。

    勾選剛剛配置的外部中斷線0和13,并配置搶占優先級

    Preemption Priority

    和響應優先級

    Sub Priority

    • 搶占優先級,數字越小,優先級越高
    • 若搶占優先級相同,判斷子優先級,同樣,數字越小,優先級越高
      STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

2.2 生成代碼

輸入項目名和項目路徑

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

選擇應用的 IDE 開發環境 MDK-ARM V5

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

每個外設生成獨立的

’.c/.h’

檔案

不勾:所有初始化代碼都生成在 main.c

勾選:初始化代碼生成在對應的外設檔案。 如 GPIO 初始化代碼生成在 gpio.c 中。

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

點選 GENERATE CODE 生成代碼

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

2.3 修改中斷回調函數

打開

stm32f1xx_it.c

中斷服務函數檔案,找到 EXTI0 中斷的服務函數

EXTI0_IRQHandler()

中斷服務函數裡面就調用了 GPIO 外部中斷處理函數

HAL_GPIO_EXTI_IRQHandler()

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

打開

stm32f1xx_hal_gpio.c

檔案,找到外部中斷處理函數原型

HAL_GPIO_EXTI_IRQHandler()

,其主要作用就是判斷是幾号線中斷,清除中斷辨別位,然後調用中斷回調函數

HAL_GPIO_EXTI_Callback()

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項
這個函數不應該被改變,如果需要使用回調函數,請重新在使用者檔案中實作該函數。

HAL_GPIO_EXTI_Callback()

按照官方提示我們應該再次定義該函數,

__weak

是一個弱化辨別,帶有這個的函數就是一個弱化函數,就是你可以在其他地方寫一個名稱和參數都一模一樣的函數,編譯器就會忽略這一個函數,而去執行你寫的那個函數;而

UNUSED(GPIO_Pin)

,這就是一個防報錯的定義,當傳進來的GPIO端口号沒有做任何處理的時候,編譯器也不會報出警告。其實我們在開發的時候已經不需要去理會中斷服務函數了,隻需要找到這個中斷回調函數并将其重寫即可而這個回調函數還有一點非常便利的地方這裡沒有展現出來,就是當同時有多個中斷使能的時候,STM32CubeMX會自動地将幾個中斷的服務函數規整到一起并調用一個回調函數,也就是無論幾個中斷,我們隻需要重寫一個回調函并判斷傳進來的端口号即可。

接下來我們就在

stm32f1xx_it.c

這個檔案的最下面添加

HAL_GPIO_EXTI_Callback()

/* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin==KEY1_Pin)
	{
        // 自定義應用程式
        HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
	}
}
/* USER CODE END 1 */
           

2.3 HAL庫與标準庫代碼比較

STM32CubeMX 使用 HAL 庫生成的代碼:

#define KEY2_Pin GPIO_PIN_13
#define KEY2_GPIO_Port GPIOC
#define KEY2_EXTI_IRQn EXTI15_10_IRQn
#define KEY1_Pin GPIO_PIN_0
#define KEY1_GPIO_Port GPIOA
#define KEY1_EXTI_IRQn EXTI0_IRQn

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    /*Configure GPIO pin : KEY2_Pin */
    GPIO_InitStruct.Pin = KEY2_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(KEY2_GPIO_Port, &GPIO_InitStruct);

    /*Configure GPIO pin : KEY1_Pin */
    GPIO_InitStruct.Pin = KEY1_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStruct);

    /* EXTI interrupt init*/
    HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);

    HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}

/**
  * @brief This function handles EXTI line0 interrupt.
  */
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 */
}

/**
  * @brief This function handles EXTI line[15:10] interrupts.
  */
void EXTI15_10_IRQHandler(void)
{
    /* USER CODE BEGIN EXTI15_10_IRQn 0 */

    /* USER CODE END EXTI15_10_IRQn 0 */
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
    /* USER CODE BEGIN EXTI15_10_IRQn 1 */

    /* USER CODE END EXTI15_10_IRQn 1 */
}

/**
  * @brief  This function handles EXTI interrupt request.
  * @param  GPIO_Pin: Specifies the pins connected EXTI line
  * @retval None
  */
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
    /* EXTI line interrupt detected */
    if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
    {
        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
        HAL_GPIO_EXTI_Callback(GPIO_Pin);
    }
}
           

使用 STM32 标準庫的代碼:

#define KEY1_INT_GPIO_PORT         GPIOA
#define KEY1_INT_GPIO_CLK          (RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO)
#define KEY1_INT_GPIO_PIN          GPIO_Pin_0
#define KEY1_INT_EXTI_PORTSOURCE   GPIO_PortSourceGPIOA
#define KEY1_INT_EXTI_PINSOURCE    GPIO_PinSource0
#define KEY1_INT_EXTI_LINE         EXTI_Line0
#define KEY1_INT_EXTI_IRQ          EXTI0_IRQn

#define KEY1_IRQHandler            EXTI0_IRQHandler


#define KEY2_INT_GPIO_PORT         GPIOC
#define KEY2_INT_GPIO_CLK          (RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO)
#define KEY2_INT_GPIO_PIN          GPIO_Pin_13
#define KEY2_INT_EXTI_PORTSOURCE   GPIO_PortSourceGPIOC
#define KEY2_INT_EXTI_PINSOURCE    GPIO_PinSource13
#define KEY2_INT_EXTI_LINE         EXTI_Line13
#define KEY2_INT_EXTI_IRQ          EXTI15_10_IRQn

#define KEY2_IRQHandler            EXTI15_10_IRQHandler

 /**
  * @brief  配置嵌套向量中斷控制器NVIC
  * @param  無
  * @retval 無
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 配置NVIC為優先級組1 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* 配置中斷源:按鍵1 */
  NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
  /* 配置搶占優先級 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 配置子優先級 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中斷通道 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
  /* 配置中斷源:按鍵2,其他使用上面相關配置 */  
  NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
  NVIC_Init(&NVIC_InitStructure);
}

 /**
  * @brief  配置 IO為EXTI中斷口,并設定中斷優先級
  * @param  無
  * @retval 無
  */
void EXTI_Key_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; 
	EXTI_InitTypeDef EXTI_InitStructure;

	/*開啟按鍵GPIO口的時鐘*/
	RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK,ENABLE);
												
	/* 配置 NVIC 中斷*/
	NVIC_Configuration();
	
    /*--------------------------KEY1配置-----------------------------*/
	/* 選擇按鍵用到的GPIO */	
    GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
    /* 配置為浮空輸入 */	
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);

	/* 選擇EXTI的信号源 */
    GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, KEY1_INT_EXTI_PINSOURCE); 
    EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;
	
    /* EXTI為中斷模式 */
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    /* 上升沿中斷 */
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    /* 使能中斷 */	
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
	
    /*--------------------------KEY2配置-----------------------------*/
    /* 選擇按鍵用到的GPIO */	
    GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;
    /* 配置為浮空輸入 */	
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);

    /* 選擇EXTI的信号源 */
    GPIO_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, KEY2_INT_EXTI_PINSOURCE); 
    EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;
	
    /* EXTI為中斷模式 */
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    /* 下降沿中斷 */
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    /* 使能中斷 */	
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
}

void KEY1_IRQHandler(void)
{
    //確定是否産生了EXTI Line中斷
	if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) 
	{
		// LED1 取反		
		LED1_TOGGLE;
        //清除中斷标志位
		EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);     
	}  
}

void KEY2_IRQHandler(void)
{
    //確定是否産生了EXTI Line中斷
	if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET) 
	{
		// LED2 取反		
		LED2_TOGGLE;
        //清除中斷标志位
		EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);     
	}  
}
           

__HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE();

對應

RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE);

HAL_GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStruct);

對應

GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);

HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);HAL_NVIC_EnableIRQ(EXTI0_IRQn);

對應

NVIC_Init(&NVIC_InitStructure);EXTI_Init(&EXTI_InitStructure);

__HAL_GPIO_EXTI_GET_IT(GPIO_Pin)

對應

EXTI_GetITStatus(KEY1_INT_EXTI_LINE)

__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin)

對應

EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE)

三、注意事項

使用者代碼要加在

USER CODE BEGIN N

USER CODE END N

之間,否則下次使用 STM32CubeMX 重新生成代碼後,會被删除。

STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用一、建立工程二、EXTI外部中斷三、注意事項

• 由 Leung 寫于 2021 年 1 月 12 日

• 參考:STM32CubeMX系列教程2:外部中斷(EXIT)

    【STM32Cube_05】使用EXIT中斷檢測按鍵(NVIC配置+HAL庫中斷處理機制)

    《嵌入式-STM32開發指南》第二部分 基礎篇 - 第3章 按鍵(HAL庫)

    STM32CubeMX實戰教程(三)——外部中斷(中斷及HAL_Delay函數避坑)

繼續閱讀