STM32F103ZET6的系統中斷有10個,外部中斷有60個
1、中斷的概念
????????中斷是為使單片機具有對外部或內部隨機發生的事件實時處理而設置的,中斷功能的存在,很大程度上提高了單片機處理外部或內部事件的能力
? ? ? ? eg::你打開火, 燒上一壺水。然后去洗衣服,在洗衣服的過程中,突然聽到水壺發出水開的報警聲,這時,你停止洗衣服動作,立即去關掉火,然后將開水灌入暖水瓶中,灌完開水后,你又回去繼續洗衣服。這個過程中實際上就發生了一次中斷
????????對于單片機來講,中斷是指CPU在處理某一事件A時,發生了另一事件B,請求CPU迅速去處理(中斷發生);CPU暫時停止當前的工作(中斷響應),轉去處理事件B(中斷服務);待CPU將事件B處理完畢后,再回到原來事件A被中斷的地方繼續處理事件A(中斷返回),這一過程稱為中斷
2、中斷的作用和意義
作用:
? ? ? ? (1)實時控制
????????????????在確定時間內對相應事件作出響應,如:溫度監控
? ? ? ? (2)故障處理
????????????????檢測到故障,需要第一時間處理,如:電梯門夾人了
? ? ? ? (3)數據傳輸
????????????????不確定數據何時會來,如:串口數據接收
意義:高效處理緊急查詢,不會一直占用CPU資源
3、STM32 GPIO外部中斷簡圖
如下圖所示:
4、NVIC?
NVIC全稱Nested vectored interrupt controller,嵌套向量中斷控制器,屬于內核M3/4/7
NVIC支持:256個中斷(16內核 + 240外部),支持:256個優先級,允許裁剪!
NVIC相關的寄存器如下:
typedef struct{
? __IOM uint32_t ISER[8U]; ? ? ? ? ? ? ? /*??中斷使能寄存器? */
? ? ? ? uint32_t RESERVED0[24U];
? __IOM uint32_t ICER[8U]; ? ? ? ? ? ? ? /*??中斷除能寄存器? ?*/
? ? ? ? uint32_t RSERVED1[24U];
? __IOM uint32_t ISPR[8U]; ? ? ? ? ? ? ? /*? 中斷使能掛起控制寄存器? */
? ? ? ? uint32_t RESERVED2[24U];
? __IOM uint32_t ICPR[8U]; ? ? ? ? ? ? ? /*? 中斷解掛控制寄存器? */
? ? ? ? uint32_t RESERVED3[24U];
? __IOM uint32_t IABR[8U]; ? ? ? ? ? ? ? /*? 中斷激活標志位寄存器? */
? ? ? ? uint32_t RESERVED4[56U];
? __IOM uint8_t ?IP[240U]; ? ? ? ? ? ? ? /*??中斷優先級寄存器??*/
? ? ? ? uint32_t RESERVED5[644U];
? __OM ?uint32_t STIR; ? ? ? ? ? ? ? ? ? /*? 軟件觸發中斷寄存器? */
} ?NVIC_Type;?
4.1 中斷優先級
????????STM32 中的中斷優先級可以分為:搶占式優先級和響應優先級。響應優先級也稱子優先級 , 每個中斷源都需要被指定這兩種優先級?
(1)搶占優先級:搶占優先級高的中斷可以打斷正在執行的搶占優先級低的中斷
(2)響應優先級:搶占優先級相同,響應優先級高的先執行,但是不能互相打斷
(3)當兩個或者多個中斷的搶占式優先級和響應優先級相同時,那么就遵循自然優先級(硬件優先級)
數值越小,優先級越高(搶占、響應和自然優先級都是如此)
4.2 NVIC的使用
(1)設置中斷分組:
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_x);? ? ? ? //分組x,x:0~4
不用設置,默認有,在stm32f1xx_hal.c的文件中
(2)設置中斷優先級
HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
①形參1:中斷號
????????例如:HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);
????????為什么例子中的形參1與聲明不同?
????????因為IRQn_Type是定義的枚舉,EXTI0_IRQn在枚舉中
②形參2:搶占優先級,可以選擇范圍:0到15
③形參3:響應優先級,可以選擇范圍:0到15
(3)使能中斷
HAL_NVIC_EnableIRQ(IRQn_Type IRQn);? ? ? ? //操作ISERx寄存器
5、EXTI
? ? ? ? EXTI是外部中斷或事件控制器,包含20個產生事件/中斷請求的邊沿檢測器,即總共:20條EXTI線(F1)
中斷和事件的區別:
? ? ? ? 中斷:要進入NVIC,有相應的中斷服務函數,需要CPU處理
? ? ? ? 事件:不進入NVIC,僅用于內部硬件自動控制,如:TIM、DMA、ADC
EXTI的主要特征:每條EXTI線都可以單獨配置:選擇類型(中斷或者事件)、觸發方式(上升沿,下降沿或者雙邊沿觸發)、支持軟件觸發、開啟/屏蔽、有掛起狀態位
5.1 工作原理
從上圖可以看到有兩條主線,一條是由輸入線到NVIC中斷控制器,一條是由輸入線到脈沖發生器。這就恰恰是EXTI的兩大部分功能:產生中斷和產生事件
(1)產生中斷的線路
? ? ? ? 標號①:邊沿檢測電路,包括邊沿檢測電路、上升沿觸發選擇寄存器(EXTI_RTSR)和下降沿觸發選擇寄存器(EXTI_FTSR)。以輸入線作為信號輸入端,如果檢測到有邊沿跳變就輸出有效信號‘1’到標號②部分電路,否則輸出無效信號‘0’
EXTI_RTSR | EXTI_FTSR | 邊沿檢測電路 |
0 | 0 | 關閉 |
1 | 0 | 有效信號是上升沿 |
0 | 1 | 有效信號是下降沿 |
1 | 1 | 雙邊沿 |
? ? ? ? 標號②:或門電路,只要輸入端有信號‘1’,就會輸出‘1’到標號③電路和標號④電路。它的兩個信號輸入端分別是軟件中斷事件寄存器(EXTI_SWIER)和邊沿檢測電路的輸入信號
? ? ? ? 標號③:與門電路,輸入都為‘1’才輸出‘1’。它的兩個信號輸入端分別是中斷屏蔽寄存器(EXTI_IMR)和標號②電路輸出信號,如果EXTI_IMR設置為0時,不管從標號②電路輸出的信號特性如何,最終標號③電路輸出的信號都是0;假如EXTI_IMR設置為1時,最終標號③電路輸出的信號才由標號②電路輸出信號決定。這樣子就可以簡單控制EXTI_IMR來實現中斷的目的,標號④電路輸出‘1’就會把請求掛起寄存器(EXTI_PR)對應位置1
(2)產生事件的線路
產生事件線路是從標號2之后與中斷線路有所不用,之前的線路都是共用的
????????標號④:與門電路。輸入端來自標號②電路以及事件屏蔽寄存器(EXTI_EMR)。如果EXTI_EMR寄存器設置為0,那不管標號②電路輸出的信號是‘0’還是‘1’,最終標號④輸出的是‘0’; 如果EXTI_EMR寄存器設置為1,最終標號④電路輸出信號就由標號②電路輸出的信號決定。通過控制EXTI_EMR來實現是否產生事件
????????標號④電路輸出有效信號‘1’就會使脈沖發生器電路產生一個脈沖,而無效信號就不會使其產生脈沖信號。脈沖信號產生可以給其他外設電路使用,例如定時器,模擬數字轉換器等?
(3)產生中斷線路和產生事件線路的目的
????????產生中斷線路目的使把輸入信號輸入到NVIC,進一步運行中斷服務函數,實現功能
????????產生事件線路目的是傳輸一個脈沖信號給其他外設使用,屬于硬件級功能
5.2 EXTI的HAL庫設置步驟(外部中斷)
(1)使能GPIO時鐘:__HAL_RCC_GPIOx_CLK_ENABLE();(x:A~E)
(2)配置GPIO/AFIO/EXTI:HAL_GPIO_Init(GPIO_TypeDef ?*GPIOx, GPIO_InitTypeDef *GPIO_Init);
(3)設置中斷分組:HAL_NVIC_SetPriorityGrouping
(4)設置中斷優先級:HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
(5)使能中斷:HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
例子:exit初始化
void exit_init(void){
? ? GPIO_InitTypeDef g_exti_struct;
? ? //一、外部中斷--HAL庫配置
? ? //1、使能時鐘
? ? __HAL_RCC_GPIOA_CLK_ENABLE();
? ? __HAL_RCC_GPIOE_CLK_ENABLE();
? ? //2、配置GPIO/AFIO/EXTI
? ? ? ? //初始化KEY_UP
? ? g_exti_struct.Pin=GPIO_PIN_0;
? ? g_exti_struct.Mode=GPIO_MODE_IT_RISING; ?//上升沿觸發
? ? g_exti_struct.Pull=GPIO_PULLDOWN; ?//下拉
? ? g_exti_struct.Speed=GPIO_SPEED_FREQ_HIGH;
? ? HAL_GPIO_Init(GPIOA,&g_exti_struct);
? ? ? ? //初始化KEY0
? ? g_exti_struct.Pin=GPIO_PIN_4;
? ? g_exti_struct.Mode=GPIO_MODE_IT_FALLING; ?//下降沿觸發
? ? g_exti_struct.Pull=GPIO_PULLUP; ?//上拉
? ? HAL_GPIO_Init(GPIOE,&g_exti_struct);
? ? ? ? //初始化KEY1
? ? g_exti_struct.Pin=GPIO_PIN_3;
? ? g_exti_struct.Mode=GPIO_MODE_IT_FALLING; ?//下降沿觸發
? ? g_exti_struct.Pull=GPIO_PULLUP; ?//上拉
? ? HAL_GPIO_Init(GPIOE,&g_exti_struct);
? ? //3、設置優先級分組--不用設置,默認(main.c-->HAL_Init()下)有
? ? //4、設置優先級
? ? HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); ?//搶占優先級:2,響應優先級:0
? ? HAL_NVIC_SetPriority(EXTI4_IRQn,2,0); ?//搶占優先級:2,響應優先級:0
? ? HAL_NVIC_SetPriority(EXTI3_IRQn,2,0); ?//搶占優先級:2,響應優先級:0
? ? //5、使能中斷
? ? HAL_NVIC_EnableIRQ(EXTI0_IRQn);
? ? HAL_NVIC_EnableIRQ(EXTI4_IRQn);
? ? HAL_NVIC_EnableIRQ(EXTI3_IRQn);
}
5.3 中斷服務處理機制
(1)中斷服務函數:在啟動文件中已經定義過了
(2)要調用中斷公共處理函數
(3)中斷回調函數:在這里編寫中斷處理的邏輯代碼
例子:與5.2的例子是一體的
//二、中斷服務處理機制
//1、中斷服務函數:在啟動文件中已經定義過了
? ? //KEY_UP
void EXTI0_IRQHandler(){
? ? //2、要調用中斷公共處理函數
? ? HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); ?//中斷公共處理函數
? ? __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); ?//清中斷
}
? ? //KEY0
void EXTI4_IRQHandler(){
? ? HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4); ?//中斷公共處理函數
? ? __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4); ?//清中斷
}
? ? //KEY1
void EXTI3_IRQHandler(){
? ? HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); ?//中斷公共處理函數
? ? __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); ?//清中斷
}
//3、中斷回調函數:在這里編寫中斷處理的邏輯代碼
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){ ?//弱函數
? ? //消抖
? ? delay_ms(10);
? ? //判斷
? ? if(GPIO_Pin==GPIO_PIN_0){
? ? ? ? //KEY_UP ?控制LED0翻轉
? ? ? ? HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
? ? }else if(GPIO_Pin==GPIO_PIN_4){
? ? ? ? //KEY0 ?控制 LED1翻轉
? ? ? ? HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
? ? }else if(GPIO_Pin==GPIO_PIN_3){
? ? ? ? //KEY1 ?控制KEEP翻轉
? ? ? ? HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
? ? }
}
EXTI0_IRQHandler定義在啟動文件中,來源于startup_stm32f103xe.s第84行的DCD ? ? EXTI0_IRQHandler ? ? ? ? ? ; EXTI Line 0
HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)?;? ? ? ? //為中斷通用入口函數
作用:通過入口參數GPIO_Pin判斷中斷來自哪個IO口
#define __HAL_GPIO_EXTI_CLEAR_IT(__EXTI_LINE__) (EXTI->PR = (__EXTI_LINE__))
__HAL_GPIO_EXTI_CLEAR_IT定義在stm32f1xx_hal_gpio.h文件中,清中斷
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)????????//用__weak修飾的函數是弱函數,用戶可以重新定義
{
? /* 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
? ?*/
}? ? ? ??
HAL_GPIO_EXTI_Callback定義在stm32f1xx_hal_gpio.c文件中