参考资料:
《开发指南》P120,STM32 NVIC中断优先级管理
《参考手册》P130,中断和事件
《开发指南》P189,外部中断实验
STM32F103上有16个内核中断和60可外部中断,具有16级可编程的中断优先级(中断优先级由“抢占优先级”和“响应优先级”来控制)
STM32F103的每一个中断都会被设置一个抢占优先级和一个响应优先级。
高优先级的抢占优先级可以打断正在进行的低抢占优先级中断
例如:设A的抢占优先级是0,B的抢占优先级是1(数字越小,抢占优先级越高)。此时如果B正在执行中断内容,某时刻A突然发生中断,那么系统会先暂停B的中断内容,从而先执行A的中断内容,A的中断内容执行完后,再回到B的中断中。
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
例如:设A,B的抢占优先级都是0,A的响应优先级为0,B的响应优先级为1。那么如果此时A,B中断同时发生,那么系统会先进入响应优先级高的中断,也就是说会先进入A的中断,后进入B的中断。
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级d的中断。
例如:A,B的抢占优先级一样,而A的响应优先级是0,B的响应优先级是1。当B正在执行中断的时候,某时刻A的中断发生,此时系统会继续执行B的中断,不会进入A的中断。
如果两个中断的抢占优先级和响应优先级都一样,则看哪个中断先发生就先执行。
首先,对STM32F103中断分成0~4组。同时对每个中断设置一个抢占优先级和一个响应优先级。
如图:
分为0组时,没有抢占优先级,有4级的响应优先级
分为1组时,有1级的抢占优先级,有3级的响应优先级
分为2组时,有2级的抢占优先级,有2级的响应优先级
分为3组时,有3级的抢占优先级,有1级的响应优先级
分为4组是,有4级的抢占优先级,有0级的响应优先级
◉举例:设置中断优先级组为2(意味着有2级的抢占优先级,有2级的响应优先级);设置中断3的抢占优先级为2,响应优先级为1;设置中断6的抢占优先级为3,响应优先级为0;设置中断7的抢占优先级为2,响应优先级为0
此时这3个中断的优先级顺序为:中断7>中断3>中断6
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
/* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
函数参数是"分为几组"
参数的有效输入为:
NVIC_PriorityGroup_0
分为0组; NVIC_PriorityGroup_1
分为1组;
NVIC_PriorityGroup_1
分为2组; NVIC_PriorityGroup_3
分为3组; NVIC_PriorityGroup_1
分为4组
STM32通过一系列的寄存器来配置每一个中断的抢占优先级和响应优先级,相关寄存器如下:
这些寄存器的作用如下:
有240个8位寄存器,每个中断使用一个寄存器来确定优先级,STM32F103使用到IP【0】~IP【59】。
每个IP寄存器的高4位用来设置抢占和响应优先级,低4位没有用到 。
32位寄存器,每个位控制一个中断的使能。STM32F103有60个屏蔽中断,所以只使用了其中的ISER【0】和ISER【1】
ISER【0】的位0~位31分别对应中断0-31,ISER【1】的位0-27 对应中断32-59。
32位寄存器,每个位控制一个中断的失能。STM32F103有60个屏蔽中断,所以只使用了其中的ICER【0】和ICER【1】
ICER【0】的位0~位31分别对应中断0-31,ICER【1】的位0-27 对应中断32-59。
ISPR【8】寄存器
作用:用来挂起中断
ICPR【8】寄存器
作用: 用来解挂中断
IABR【8】寄存器
作用:通过它可以知道当前正在执行的中断是哪一个,如果对应位为1,说明该中断正在执行
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
参数为一个结构体,该结构体的定义如下:
其中的设置响应优先级和设置抢占优先级就是IP【240】寄存器的作用;其中的使能/失能就是ISER【8】和ICER【8】的作用
中断优先级的设置步骤:
STM32F103每一个IO口都可以作为外部中断输入
中断控制器支持19个外部中断/事件请求
线0~15:IO口的输入中断
线16:连接到PVD输出
线17:连接到RTC闹钟事件
线18:连接到USB唤醒事件
STM32F103有112个IO口,但只有16条中断线,它们之间的对应关系如下:
如图,GPIOx.0映射到EXTI0;GPIOx.1映射到EXTI1…GPIOx.15映射到EXTI15
中断线只有16个,每一条中断线在同一时间只能被一个IO口所映射。也就是说GPIOx.0中同时只能存在一个IO是输入中断。
对于每一个中断线,都可以 设置相应的触发方式(上升沿触发,下降沿触发,边沿触发)以及使能
关于中断服务函数
有16个中断线,但只有7个中断向量,也就是说只有7个中断服务函数
以按键的外部中断为例.
跟“按键初始化函数类似”
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能GPIOA和GPIOE的时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_3;//KEY0-KEY1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置为上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE3,4
//初始化 按键 WK_UP
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置为下拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA0
使用到的库函数为void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
该实例中两个参数应为:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
使用到的库函数为void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
第一个参数是“哪一个端口”(例如GPIOE);第二个参数是端口号(例如GPIOE4)
该实例中两个参数为:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);在GPIO.c文件中
这样配置完成后,GPIOE4就已经映射到中断线4上了!!!!!!
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); 在exti.c文件中
实例中:
EXTI_InitTypeDef EXIT_InitStrue;
EXIT_InitStrue.EXTI_Line=EXTI_Line4; 初始化的中断线
EXIT_InitStrue.EXTI_LineCmd=ENABLE; ENABLE or DISABLE
EXIT_InitStrue.EXTI_Mode=EXTI_Mode_Interrupt; 中断触发或者事件触发,该模式是中断触发
EXIT_InitStrue.EXTI_Trigger=EXTI_Trigger_Falling; 触发中断的模式,该中断是下降沿触发
EXTI_Init(&EXIT_InitStrue);
这一步完成了中断线4的初始化!!!!!!
中断优先级分组:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 设置中断分组2; 2位抢占,2位响应; 在misc.c文件中
中断线4抢占,响应优先级配置:
NVIC_InitTypeDef NVIC_InitStrue;
NVIC_InitStrue.NVIC_IRQChannel=EXTI4_IRQn; 选择中断线4
NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE; 使能
NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=2; 抢占优先级
NVIC_InitStrue.NVIC_IRQChannelSubPriority=2; 响应优先级
NVIC_Init(&NVIC_InitStrue);
void EXTI4_IRQHandler(void) 在startup_stm32f10x.hd.s文件中
{
}
调用函数:void EXTI_ClearITPendingBit(uint32_t EXTI_Line); 在exti.h文件中
在实例中:
void EXTI4_IRQHandler(void)
{
delay_ms(10);
if(KEY0==0){
LED0=!LED0;
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line4); 清除中断线4标志位
}
至此,以按键KEY_0为外部中断的相关配置就此完成。