STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用

一、新建工程

1. 打開 STM32CubeMX 軟件,點擊“新建工程”

2. 選擇 MCU 和封裝

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


選擇 Clock Configuration,配置系統時鐘 SYSCLK 為 72MHz
修改 HCLK 的值為 72 后,輸入回車,軟件會自動修改所有配置

4. 配置調試模式
非常重要的一步,否則會造成第一次燒錄程序后續無法識別調試器
SYS 設置,選擇 Debug 為 Serial Wire

二、EXTI外部中斷

2.1 參數配置

System Core 中選擇 GPIO 設置。


在右邊圖中找到按鍵對應引腳,選擇 GPIO_EXTIx
這里的 x 是指掛載在中斷線幾上,如 GPIO_EXTI0 就是掛載在中斷線0上。

  • 開啟下降沿觸發中斷:即在 按下按鍵時 電平由高變為低時觸發,則在 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 內部上拉電阻。

    配置 NVIC
    中斷優先級分組規則 Priority Group 默認為4個比特位,一般情況下不改。
    勾選剛剛配置的外部中斷線0和13,并配置搶占優先級 Preemption Priority 和響應優先級 Sub Priority
    • 搶占優先級,數字越小,優先級越高
    • 若搶占優先級相同,判斷子優先級,同樣,數字越小,優先級越高


2.2 生成代碼

輸入項目名和項目路徑


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

每個外設生成獨立的 ’.c/.h’ 文件
不勾:所有初始化代碼都生成在 main.c
勾選:初始化代碼生成在對應的外設文件。 如 GPIO 初始化代碼生成在 gpio.c 中。

點擊 GENERATE CODE 生成代碼

2.3 修改中斷回調函數

打開 stm32f1xx_it.c 中斷服務函數文件,找到 EXTI0 中斷的服務函數 EXTI0_IRQHandler()
中斷服務函數里面就調用了 GPIO 外部中斷處理函數 HAL_GPIO_EXTI_IRQHandler()


打開 stm32f1xx_hal_gpio.c 文件,找到外部中斷處理函數原型 HAL_GPIO_EXTI_IRQHandler(),其主要作用就是判斷是幾號線中斷,清除中斷標識位,然后調用中斷回調函數 HAL_GPIO_EXTI_Callback()

/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
這個函數不應該被改變,如果需要使用回調函數,請重新在用戶文件中實現該函數。

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 NUSER CODE END N 之間,否則下次使用 STM32CubeMX 重新生成代碼后,會被刪除。


? 由 Leung 寫于 2021 年 1 月 12 日

? 參考:STM32CubeMX系列教程2:外部中斷(EXIT)
    【STM32Cube_05】使用EXIT中斷檢測按鍵(NVIC配置+HAL庫中斷處理機制)
    《嵌入式-STM32開發指南》第二部分 基礎篇 - 第3章 按鍵(HAL庫)
    STM32CubeMX實戰教程(三)——外部中斷(中斷及HAL_Delay函數避坑)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內容