目錄
一、中斷系統
二、STM32中斷
三、NVIC(嵌套中斷向量控制器)基本結構
四、NVIC優先級分組
五、EXTI外部中斷
5.1 外部中斷基本知識
5.2 外部中斷(EXTI)基本結構
?編輯
5.2.1開發步驟:?
5.3 AFIO復用IO口
5.4 EXTI內部框圖
六、AFIO庫函數
七、EXTI庫函數?
八、NVIC庫函數
九、中斷函數
以上函數具體使用請參考”第十章、實驗“?
十、實驗
10.1對射式紅外傳感器計次
10.2旋轉編碼器計次
十一、中斷編程建議
一、中斷系統
中斷:在主程序運行過程中,出現了特定的中斷觸發條件(中斷源),使得CPU暫停當前正在運行的程序,轉而去處理中斷程序,處理完成后又返回原來被暫停的位置繼續運行
中斷優先級:當有多個中斷源同時申請中斷時,CPU會根據中斷源的輕重緩急進行裁決,優先響應更加緊急的中斷源
中斷嵌套:當一個中斷程序正在運行時,又有新的更高優先級的中斷源申請中斷,CPU再次暫停當前中斷程序,轉而去處理新的中斷程序,處理完成后依次進行返回
二、STM32中斷
?68個可屏蔽中斷通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多個外設
使用NVIC統一管理中斷,每個中斷通道都擁有16個可編程的優先等級,可對優先級進行分組,進一步設置搶占優先級和響應優先級
三、NVIC(嵌套中斷向量控制器)基本結構
作用:統一分配中斷優先級和管理中斷
四、NVIC優先級分組
NVIC的中斷優先級由優先級寄存器的4位(0~15)決定,這4位可以進行切分,分為高n位的搶占優先級和低4-n位的響應優先級
搶占優先級高的可以中斷嵌套,響應優先級高的可以優先排隊,搶占優先級和響應優先級均相同的按中斷號排隊
五、EXTI外部中斷
5.1 外部中斷基本知識
①EXTI(Extern Interrupt)外部中斷
②EXTI可以監測指定GPIO口的電平信號,當其指定的GPIO口產生電平變化時,EXTI將立即向NVIC發出中斷申請,經過NVIC裁決后即可中斷CPU主程序,使CPU執行EXTI對應的中斷程序
③支持的觸發方式:上升沿/下降沿/雙邊沿/軟件觸發
④支持的GPIO口:所有GPIO口,但相同的Pin不能同時觸發中斷
⑤通道數:16個GPIO_Pin,外加PVD輸出、RTC鬧鐘、USB喚醒、以太網喚醒
⑥觸發響應方式:中斷響應/事件響應中斷響應:引腳電平變化觸發中斷
事件響應:不觸發中斷,觸發其他外設操作
5.2 外部中斷(EXTI)基本結構
5.2.1開發步驟:?
①配置RCC,把涉及到的外設時鐘都打開
②配置GPIO,選擇端口為輸入模式
③配置AFIO,選擇用的某一路GPIO,連接到后面的EXTI
④配置EXTI,選擇邊沿觸發方式、觸發響應方式(中斷響應/事件響應)
⑤配置NVIC,給中斷選擇合適的優先級
5.3 AFIO復用IO口
AFIO主要用于引腳復用功能的選擇和重定義
在STM32中,AFIO主要完成兩個任務:復用功能引腳重映射、中斷引腳選擇
5.4 EXTI內部框圖
六、AFIO庫函數
void GPIO_AFIODeInit(void);//復位AFIO
//配置AFIO事件輸出功能
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);/*下面兩個重要*/
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);//引腳重映射,第一個參數:重映射的方式;第二個參數:新的狀態
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);//配置AFIO的數據選擇器,本節中斷用到的函數
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);//以太網相關
AFIO中斷選擇函數詳解
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);?
第一個參數:GPIO_PortSourceGPIOx where x can be (A..G)
第二個參數:GPIO_PinSourcex where x can be (0..15)
七、EXTI庫函數?
void EXTI_DeInit(void);//清除所有EXTI配置,復位
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);//結構體方式初始化EXTI
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);//給結構體變量賦默認值
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);//軟件觸發外部中斷/*主函數查看和清除標志位*/
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);//獲取指定標志位是否被置1
void EXTI_ClearFlag(uint32_t EXTI_Line);//對置1的標志位清除/*中斷函數查看和清除標志位*/
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
八、NVIC庫函數
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);//中斷分組的方式,整個芯片只能用一種,最好放在主函數中
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);//結構體方式初始化NVIC/*下面兩個不常用*/
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);//設置中斷向量表
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);//系統低功耗配置
九、中斷函數
每個通道對應一個中斷函數,在啟動文件中可查看
開發技巧:
在中斷函數中,先進行中斷標志位的判斷,確保是我們想要的中斷源觸發的函數;然后再使用清除標志位函數清除,否則會一直執行中斷函數,在中斷函數里卡死。
以上函數具體使用請參考”第十章、實驗“?
十、實驗
10.1對射式紅外傳感器計次
main.c
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "CountSensor.h"int main(void) {OLED_Init();CountSensor_Init();OLED_ShowString(1,1 ,"Count:");while (1){OLED_ShowNum(1,7,CountSensor_Get(),5);} }
CountSensor.c
#include "stm32f10x.h" // Device headeruint16_t CountSensor_Count;void CountSensor_Init(void) {/*一、開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//開啟APB2時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//開啟AFIO時鐘/*二、GPIO配置*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉輸入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);/*三、AFIO配置*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);/*四、EXTI第14個線路配置為中斷模式,下降沿觸發,開啟中斷*/EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line14;//EXTI線EXTI_InitStructure.EXTI_LineCmd = ENABLE;//開啟或關閉EXTIEXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中斷模式或者事件模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//指定觸發信號的有效邊沿EXTI_Init(&EXTI_InitStructure);/*五、配置NVIC*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//優先級分組NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//選擇中斷通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能或失能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//搶占優先級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//響應優先級NVIC_Init(&NVIC_InitStructure); }uint16_t CountSensor_Get(void) {return CountSensor_Count; }void EXTI15_10_IRQHandler(void) {if(EXTI_GetITStatus(EXTI_Line14) == SET) //判斷標志位,是否是EXTI_Line14觸發的中斷{CountSensor_Count++; EXTI_ClearITPendingBit(EXTI_Line14);//清除標志位}}
CountSensor.h
#ifndef __COUNTSENSOR_H #define __COUNTSENSOR_Hvoid CountSensor_Init(void); uint16_t CountSensor_Get(void);#endif
10.2旋轉編碼器計次
?mian.c
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Encoder.h"int16_t Num;int main(void) {OLED_Init();Encoder_Init();OLED_ShowString(1,1,"Num:");while (1){Num += Encoder_Get();OLED_ShowSignedNum(1,5,Num,5); } }
Encoder.c
#include "stm32f10x.h" // Device headerint16_t Encoder_Count;//無符號變量/*初始化操作*/ void Encoder_Init(void) {/*一、開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//開啟APB2時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//開啟AFIO時鐘/*二、GPIO配置*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉輸入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);/*三、AFIO配置*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);/*四、EXTI第0/1個線路配置為中斷模式,下降沿觸發,開啟中斷*/EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_Init(&EXTI_InitStructure);/*五、配置NVIC*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//優先級分組NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//搶占優先級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//響應優先級NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//搶占優先級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//響應優先級NVIC_Init(&NVIC_InitStructure); }int16_t Encoder_Get(void) {int16_t Temp;Temp = Encoder_Count;Encoder_Count = 0;return Temp; }/*正轉中斷函數*/ void EXTI1_IRQHandler(void) {if(EXTI_GetITStatus(EXTI_Line1) == SET) //B相下降沿(中斷){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == 0) //A相低電平{Encoder_Count ++;}EXTI_ClearITPendingBit(EXTI_Line1);} }/*反轉中斷函數*/ void EXTI0_IRQHandler(void) {if(EXTI_GetITStatus(EXTI_Line0) == SET) //A相下降沿(中斷){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0) //B相低電平{Encoder_Count --;}EXTI_ClearITPendingBit(EXTI_Line0);} }
Encoder.h
#ifndef __ENCODER_H #define __ENCODER_Hvoid Encoder_Init(void); int16_t Encoder_Get(void);#endif
十一、中斷編程建議
①在中斷函數中,不要執行耗時過長的代碼,中斷函數要簡短快速,不要執行Delay函數,防止主程序受到嚴重阻塞
②不要在主函數和中斷函數中調用相同的函數或者操作同一個硬件