導言
GPIO設置輸入模式后,一般會用輪詢的方式去查看GPIO的電平狀態。比如,最常用的案例是用于檢測按鈕的當前狀態(是按下還是沒按下)。中斷的使用一般用于計算脈沖的頻率與計算脈沖的數量。
項目地址:https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library05_gpio_input_interrupt
一、代碼(LL庫)
1.1、main.c
如上所示,函數EXTI_Configure()
完成PB4的上升沿觸發中斷的設置。
如上所示,一定要在GPIO設置完輸入模式后,才能設置中斷。
1.2、stm32f1xx_it.c
如上所示,編寫EXTI4中斷回調函數EXTI4_IRQHandler()的內容,在函數EXTI4_IRQHandler()里一定要清除中斷標志,否則下一次中斷將不會被執行。
為什么EXTI4的中斷函數的名字是EXTI4_IRQHandler()? 在啟動文件startup_stm32f103xb.s的中斷向量表里有定義,當發現EXTI4中斷時,調用函數EXTI4_IRQHandler()。如下所示:
回到EXTI4中斷回調函數EXTI4_IRQHandler(),既然EXTI4捕獲到中斷時,會調用函數EXTI4_IRQHandler()。但是,為什么EXTI4_IRQHandler()里需要用if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_4) != RESET)再一次確認是EXTI4的中斷標志位?
我認為主要有兩個原因:
- 規范中斷回調函數的一致性(都要再一次通過中斷標志位確認)
- 在 STM32 中,并非所有 EXTI 線路都有獨立的中斷處理函數。例如,EXTI5 到 EXTI9 共享一個處理函數 EXTI9_5_IRQHandler(),在這種情況下,必須通過檢查標志位來判斷具體是哪條線路觸發了中斷。所以,不管是EXTI1(有獨立的中斷回調函數EXTI41_IRQHandler)還是EXTI9都要在中斷回調函數里再一次檢查中斷標志位。
- 確保中斷源的準確性
- 盡管 EXTI4_IRQHandler() 通常只由 EXTI4 觸發,但在某些異常情況下(例如軟件配置錯誤、中斷控制器異常等),其他因素可能導致誤觸發。檢查 LL_EXTI_LINE_4 的標志位可以確認中斷確實是由 EXTI4 引發的,從而避免執行錯誤的處理邏輯。這種額外的驗證提高了代碼的健壯性。
- 盡管 EXTI4_IRQHandler() 通常只由 EXTI4 觸發,但在某些異常情況下(例如軟件配置錯誤、中斷控制器異常等),其他因素可能導致誤觸發。檢查 LL_EXTI_LINE_4 的標志位可以確認中斷確實是由 EXTI4 引發的,從而避免執行錯誤的處理邏輯。這種額外的驗證提高了代碼的健壯性。
二、寄存器的梳理
2.1、中斷向量表
《STM32F1參考手冊》的章節9.1.2-中斷與異常向量看到,EXTI中斷一共有如下:
如上所示,只有EXTI1~4有單獨的中斷地址,EXTI5~EXTI9共享一個中斷地址,EXTI10~EXTI15共享一個中斷地址。所以,弄明白中斷回調函數EXTI4_IRQHandler()
里為什么再一次使用代碼if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_4) != RESET)
去判斷中斷標志位了。從中斷向量表里看到,EXTI5~EXTI9確認共享一個中斷地址(函數指針)。
2.2、為什么PB4的中斷對應EXTI4?
如上所示,根據《STM32F1參考手冊》的章節9.2.5看到GPIO跟EXTI中斷線的對應關系,PB4對應EXTI4。
2.3、外部中斷配置寄存器x (AFIO_EXTICRx)
如上所示,《STM32F1參考手冊》的章節8.4.4,寄存器AFIO_EXTICR2的段EXTI4 = 0001時,相當于將PB4映射到EXTI4。
MODIFY_REG(AFIO->EXTICR[1], 0xF << 0UL, 0x01 << 0UL); // 配置EXTI4線路映射到PB4引腳
2.4、上升沿觸發選擇寄存器(EXTI_RTSR)
寄存器EXTI_PTSR的作用設置上升沿是否觸發EXTI線中斷。比如,位TR4 = 1相當于EXTI4啟動上升沿觸發中斷。
EXTI->RTSR |= 0x01UL << 4UL; // EXTI4開啟上升沿觸發中斷
EXTI->RTSR |= 0x01UL << 10UL; // EXTI10開啟上升沿觸發中斷EXTI->RTSR &= ~(0x01UL << 4UL); // EXTI4關閉上升沿觸發中斷
EXTI->RTSR &= ~(0x01UL << 10UL); // EXTI10關閉上升沿觸發中斷
2.5、下降沿觸發選擇寄存器(EXTI_FTSR)
寄存器EXTI_FTSR的作用設置下降沿是否觸發EXTI線中斷。比如,為TR4 = 1相當于EXTI4啟動下降沿觸發中斷。
EXTI->FTSR |= 0x01UL << 4UL; // 開啟EXTI4下降沿中斷
EXTI->FTSR &= ~(0x01UL << 4UL); // 關閉EXTI4下降沿中斷
2.6、掛起寄存器(EXTI_PR)
當寄存器EXTI_PR的位PR4置1時,代表觸發EXTI4中斷。 往里寫‘1’可以清除它。
if (EXTI->PR & (0x01UL << 4UL)) { // 判斷是不是EXTI4中斷EXTI->PR |= 0x01UL << 4UL; // 清除EXTI4的中斷標志
}if (EXTI->PR & (0x01UL << 10UL)) { 判斷是不是EXTI10中斷EXTI->PR |= 0x01 << 10UL; // 清除EXTI10的中斷標志
}
三、代碼(寄存器方式)
3.1、main.c
如上所示,使用寄存器方式真的簡潔。
3.2、stm32f1xx_it.c
如上所示,通過寄存器EXTI_PR的bit4判斷是否是EXTI4中斷。然后往bit4寫入‘1’就能清除中斷標志。
如上所示,在debug模式看到,當PB4從低電平->高電平時,進入中斷回調函數EXTI4_IRQHandler(),且寄存器EXTI_PR的bit4被置1。
四、細節補充
4.1、EXTI4支持同時檢測PB4與PA4嗎??
根據《STM32F1參考手冊》的章節9.2.5,根據PA0~PG0映射到EXTI0上,所以PA4~PG4是映射到EXTI4上。有一個疑問,STM32F1支持PA4、PB4一起映射到EXTI4上嗎??抱著這個疑問,我嘗試用CubeMX試試,看看ST官方工具允許不允許這樣做。
如上圖所示,CubeMX不支持PA4與PB4一起映射到EXTI4。PA4映射到EXTI4后,如果將PAB4映射到EXTI4的話,PA4就會自動失效。所以,每一個EXTIx只能映射某一個PAx。
4.2、STM32F103一共支持多少路EXTI中斷?
如上所示,參考《STM32F1參考手冊》的章節9.2.5,在STM32F103引腳資源足夠的情況下,最多支持16個GPIO口映射到EXTI外部中斷(EXTI0~EXTI15)。 另外EXTI16~EXTI19不能映射到普通GPIO,只能用于處理特定內部事件或外設的中斷/事件,比如EXTI18的USB喚醒事件。