笔记来源于STM32F103VET6,野火指南者,中文参考手册,HAL库开发手册和b站的野火指南者中级篇视频。观看过好多次中级篇了,但往往理解得不够全面,现记下小笔记,用来回顾。属于个人笔记,不用于商业。
EXTI:外部中断/事件控制器,管理了控制器的 20 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
中断分为:系统异常(内核水平)、外部中断(内核之外)。
系统异常保存在core_m3.h、misc.h。
外部中断保存在stm32f10x.h。
对于第一点,输入线,EXTI控制器有19个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个GPIO,也可以是一些外设的事件。输入线一般是存在电平变化的信号。
对于第二点,边沿检测电路可以选择上升沿触发选择寄存器(EXTI_RTSR)或下降沿触发选择寄存器(EXTI_FTSR)。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号1给第三点电路,否则输出无效信号0。而EXTI_RTSR和EXTI_FTSR两个寄存器可以控制需要检测哪些类型的电平跳变过程,可以是只有上身沿触发、只有下降沿触发或者上身沿和下降沿都触发。
例如按键中断,上升沿触发选择寄存器记录的是按键按下时,下降沿触发选择寄存器记录的是按键按下后反弹回时。
对于第三点,或门电路,输入来自第二点和软件中断事件寄存器(EXTI_SWIER),任意一路输入给1就放行。EXTI_SWIER允许我们通过程序控制就可以启动中断/事件线。
对于第四点,与门电路,输入来自第三点和中断屏蔽寄存器(EXTI_IMR),所有输入都给1才放行。
如果EXTI_IMR设置为0时,那不管第三点电路的输出信号是1还是0,最终第四点的电路输出信号都为0。
如果EXTI_IMR设置为1时,最终第四点的电路输出信号才由第三点电路的输出信号决定。
这样我们可以简单的控制EXTI_IMR来实现是否产生中断的目的。第四点的电路输出信号会被保存到挂起寄存器(EXTI_PR)内,如果确定第四点的电路输出信号为1,就会把EXTI_PR对应位置1。
对于第五点,将EXTI_PR寄存器内容输出到NVIC中断控制器内,NVIC属于内核cm3的,内核就会在cm3文件中找中断服务函数ESR,实现ESR的功能。
对于第六点,与门电路,输入来自第三点和事件屏蔽寄存器(EXTI_EMR),所有输入都给1才放行。
如果EXTI_EMR设置为0时,那不管第三点电路的输出信号是1还是0,最终第六点的电路输出信号都为0。
如果EXTI_EMR设置为1时,最终第六点的电路输出信号才由第三点电路的输出信号决定。
这样我们可以简单的控制EXTI_EMR来实现是否产生事件的目的。
对于第七点,脉冲发生器电路,输出来自第六点。若第六点输出为1,则会产生1个脉冲。
对于第八点,产生脉冲信号。这个脉冲信号可以给其他外设电路使用,比如定时器TIM、模拟数字转换器ADC等等。
产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。 另外,EXTI 是在 APB2 总线上的。
中断编程的顺序:
1)使能中断请求
2)配置中断优先级分组
3)配置NVIC寄存器,初始化NVIC_InitTypeDef
4)编写中断服务函数
对于第一步,使能中断请求。先使能NVIC,在使能外设相应配置寄存器的中断使能位。
对于第二步,根据misc.h,有void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
现附上简单的按键中断例子程序:
static void EXTI_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_Init(&NVIC_InitStruct);
}
void EXTI_Key_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
//配置中断优先级
EXTI_NVIC_Config();
/*--------------------------------KEY1配置--------------------------------*/
//配置初始化GPIO-PA0
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//初始化EXTI
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStruct.EXTI_Line = EXTI_Line0;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
/*------------------------------------------------------------------------*/
/*--------------------------------KEY2配置--------------------------------*/
//配置初始化GPIO-PC13
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOC,&GPIO_InitStruct);
//初始化EXTI
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource13);
EXTI_InitStruct.EXTI_Line = EXTI_Line13;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
/*------------------------------------------------------------------------*/
}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
LED_G_TOGGLE; //LED状态翻转
}
EXTI_ClearITPendingBit(EXTI_Line0); //pemding是挂起。这个函数是将中断标志位清除掉
}
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line13) != RESET)
{
LED_R_TOGGLE; //LED状态翻转
}
EXTI_ClearITPendingBit(EXTI_Line13); //pemding是挂起。这个函数是将中断标志位清除掉
}
对NVIC_InitTypeDef 进行分析
typedef struct
{
uint8_t NVIC_IRQChannel; //IRQ通道。这个参数可以是IRQn_Type的值。(完整的STM32设备IRQ通道列表,请参阅stm32f10x.h文件)
uint8_t NVIC_IRQChannelPreemptionPriority; //抢占优先级。该参数可以是一个值,在0到15之间,如表NVIC_Priority_Table所述
uint8_t NVIC_IRQChannelSubPriority; //子优先级。 该参数可以是一个值,在0到15之间,如表NVIC_Priority_Table所述
FunctionalState NVIC_IRQChannelCmd; //IRQ通道使能,可选ENABLE或DISABLE
} NVIC_InitTypeDef;
IRQ通道如下
typedef enum IRQn
{
/****** Cortex-M3 Processor Exceptions Numbers ***************************************************/
NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */
MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */
BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */
UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */
SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */
DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */
PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */
SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */
/****** STM32 specific Interrupt Numbers *********************************************************/
WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */
PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */
TAMPER_IRQn = 2, /*!< Tamper Interrupt */
RTC_IRQn = 3, /*!< RTC global Interrupt */
FLASH_IRQn = 4, /*!< FLASH global Interrupt */
RCC_IRQn = 5, /*!< RCC global Interrupt */
EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */
EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */
EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */
EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */
EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */
DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 global Interrupt */
DMA1_Channel2_IRQn = 12, /*!< DMA1 Channel 2 global Interrupt */
DMA1_Channel3_IRQn = 13, /*!< DMA1 Channel 3 global Interrupt */
DMA1_Channel4_IRQn = 14, /*!< DMA1 Channel 4 global Interrupt */
DMA1_Channel5_IRQn = 15, /*!< DMA1 Channel 5 global Interrupt */
DMA1_Channel6_IRQn = 16, /*!< DMA1 Channel 6 global Interrupt */
DMA1_Channel7_IRQn = 17, /*!< DMA1 Channel 7 global Interrupt */
ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */
USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */
USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */
CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */
CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */
EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */
TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */
TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */
TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */
TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */
TIM2_IRQn = 28, /*!< TIM2 global Interrupt */
TIM3_IRQn = 29, /*!< TIM3 global Interrupt */
TIM4_IRQn = 30, /*!< TIM4 global Interrupt */
I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */
I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */
I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */
I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */
SPI1_IRQn = 35, /*!< SPI1 global Interrupt */
SPI2_IRQn = 36, /*!< SPI2 global Interrupt */
USART1_IRQn = 37, /*!< USART1 global Interrupt */
USART2_IRQn = 38, /*!< USART2 global Interrupt */
USART3_IRQn = 39, /*!< USART3 global Interrupt */
EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */
RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */
USBWakeUp_IRQn = 42, /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */
TIM8_BRK_IRQn = 43, /*!< TIM8 Break Interrupt */
TIM8_UP_IRQn = 44, /*!< TIM8 Update Interrupt */
TIM8_TRG_COM_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt */
TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */
ADC3_IRQn = 47, /*!< ADC3 global Interrupt */
FSMC_IRQn = 48, /*!< FSMC global Interrupt */
SDIO_IRQn = 49, /*!< SDIO global Interrupt */
TIM5_IRQn = 50, /*!< TIM5 global Interrupt */
SPI3_IRQn = 51, /*!< SPI3 global Interrupt */
UART4_IRQn = 52, /*!< UART4 global Interrupt */
UART5_IRQn = 53, /*!< UART5 global Interrupt */
TIM6_IRQn = 54, /*!< TIM6 global Interrupt */
TIM7_IRQn = 55, /*!< TIM7 global Interrupt */
DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */
DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */
DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */
DMA2_Channel4_5_IRQn = 59 /*!< DMA2 Channel 4 and Channel 5 global Interrupt */
} IRQn_Type;
抢占优先级和子优先级可以参考如下表
NVIC_PriorityGroup | NVIC_IRQChannelPreemptionPriority | NVIC_IRQChannelSubPriority | Description |
NVIC_PriorityGroup_0 | 0 | 0-15 | 主优先级占0位,子优先级占4位 |
NVIC_PriorityGroup_1 | 0-1 | 0-7 | 主优先级占1位,子优先级占3位 |
NVIC_PriorityGroup_2 | 0-3 | 0-3 | 主优先级占2位,子优先级占2位 |
NVIC_PriorityGroup_3 | 0-7 | 0-1 | 主优先级占3位,子优先级占1位 |
NVIC_PriorityGroup_4 | 0-15 | 0 | 主优先级占4位,子优先级占0位 |
举个例子,NVIC的初始化
static void EXTI_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
再对EXTI_InitTypeDef进行分析
typedef struct
{
uint32_t EXTI_Line; //中断/事件线,这个参数可以在EXTI_Lines中查看
EXTIMode_TypeDef EXTI_Mode; //中断/事件线模式,这个参数可以在EXTIMode_TypeDef中查看
EXTITrigger_TypeDef EXTI_Trigger; //触发状态,这个参数可以在EXTIMode_TypeDef中查看
FunctionalState EXTI_LineCmd; //中断/事件线使能,可以是ENABLE或DISABLE
}EXTI_InitTypeDef;
中断/事件线如下
#define EXTI_Line0 ((uint32_t)0x00001) /*!< External interrupt line 0 */
#define EXTI_Line1 ((uint32_t)0x00002) /*!< External interrupt line 1 */
#define EXTI_Line2 ((uint32_t)0x00004) /*!< External interrupt line 2 */
#define EXTI_Line3 ((uint32_t)0x00008) /*!< External interrupt line 3 */
#define EXTI_Line4 ((uint32_t)0x00010) /*!< External interrupt line 4 */
#define EXTI_Line5 ((uint32_t)0x00020) /*!< External interrupt line 5 */
#define EXTI_Line6 ((uint32_t)0x00040) /*!< External interrupt line 6 */
#define EXTI_Line7 ((uint32_t)0x00080) /*!< External interrupt line 7 */
#define EXTI_Line8 ((uint32_t)0x00100) /*!< External interrupt line 8 */
#define EXTI_Line9 ((uint32_t)0x00200) /*!< External interrupt line 9 */
#define EXTI_Line10 ((uint32_t)0x00400) /*!< External interrupt line 10 */
#define EXTI_Line11 ((uint32_t)0x00800) /*!< External interrupt line 11 */
#define EXTI_Line12 ((uint32_t)0x01000) /*!< External interrupt line 12 */
#define EXTI_Line13 ((uint32_t)0x02000) /*!< External interrupt line 13 */
#define EXTI_Line14 ((uint32_t)0x04000) /*!< External interrupt line 14 */
#define EXTI_Line15 ((uint32_t)0x08000) /*!< External interrupt line 15 */
#define EXTI_Line16 ((uint32_t)0x10000) /*!< External interrupt line 16 Connected to the PVD Output */
#define EXTI_Line17 ((uint32_t)0x20000) /*!< External interrupt line 17 Connected to the RTC Alarm event */
#define EXTI_Line18 ((uint32_t)0x40000) /*!< External interrupt line 18 Connected to the USB Device/USB OTG FS
Wakeup from suspend event */
#define EXTI_Line19 ((uint32_t)0x80000) /*!< External interrupt line 19 Connected to the Ethernet Wakeup event */
中断/事件线模式如下
typedef enum
{
EXTI_Mode_Interrupt = 0x00, //中断--针对外设
EXTI_Mode_Event = 0x04 //事件--针对内核
}EXTIMode_TypeDef;
触发状态如下
typedef enum
{
EXTI_Trigger_Rising = 0x08, //上升沿触发
EXTI_Trigger_Falling = 0x0C, //下降沿触发
EXTI_Trigger_Rising_Falling = 0x10 //上升沿和下降沿都触发
}EXTITrigger_TypeDef;
举个例子,EXTI的初始化
void EXTI_Config(void)
{
EXTI_InitTypeDef EXTI_InitStruct;
//配置中断优先级
EXTI_NVIC_Config();
//初始化EXTI-PA0
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStruct.EXTI_Line = EXTI_Line0;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
}
外部中断的一般配置步骤:
1.使能SYSCFG时钟
RCC_APB2PeriphClockCmd(Rcc_APB2Periph_SYSCFG,ENABLE);
2.初始化IO口为输入
GPIO_Init();
3.设置IO口与中断线的映射关系
viod SYSCFG_EXTILineConfig();
4.初始化线上中断,设置触发条件等
EXTI_Init();
5.配置中断分组(NVIC),并使能中断
NVIC_Init();
6.编写中断服务函数
EXTIx_IRQHandle();
7.清除中断标志位
EXTI_ClearITPendingBit();