关于中断
中断,就是程序执行到某个位置,突然收到了中断请求,此时程序暂停,去临时执行中断任务,执行完毕后再回到原位继续执行程序。
在STM32中,可以开启很多种中断,有系统内置的中断,比如:SysTick定时器中断。有设备中断,比如串口中断。有外部中断,比如某个引脚电平变化引起的中断。所有的中断都必须配置并开启后才会有效。
那么,加入某一时刻同时有好几个中断触发了,此时程序去执行哪个中断任务呢,因此需要给中断设置优先级,级别高的先执行,级别低的后执行。
关于优先级
中断优先级是通过NVIC来配置的,分为2步:
1.中断分组。
2.设置抢占优先级和响应优先级。
举个简单的例子,假设有5支集团军,每个集团军都有自己独特的指挥系统。以C军为例,C军指挥部有司令、政委、参谋长、作战部长。战斗编制有军长、师长、旅长、团长。那么指挥部的这4个级别就是抢占优先级,战斗编制的4个级别就是响应优先级。设置优先级,就是要分别设置抢占优先级和响应优先级。比如:(司令+军长)下达的命令,就要比(政委+团长)这个命令级别高,假如(政委+团长)这个命令先下达,并且已经执行了,此时(司令+军长)这个命令下达了,可以打断(政委+团长)命令抢先执行。相同抢占级的情况下,(司令+军长)>(司令+师长)。也就是说,先看抢占级,抢占级相同的情况下,才看响应级。
我们把5个集团军的指挥系统统一整理一下看看,
A军:没有指挥部,全部是战斗编制,按照军长、师长、旅长、团长...排列下去,一共有16个级别;
B军:指挥部设2级,司令、政委;战斗编制设8级,军长、师长、旅长、团战...。
C军:指挥部设4级,司令、政委、参谋长、作战部长;战斗编制设4级,军长、师长、旅长、团长。
D军:指挥部设8级,司令、政委、参谋长、作战部长...;战斗编制设2级,旅长、团长。
E军:指挥部设16级,司令、政委、参谋长、作战部长...;战斗编制没有。
中断分组的含义:用哪个集体军的指挥系统(所有中断用哪种方式来分级管理)
确定了指挥系统(分组),也就确定了指挥部有几级(抢占优先级),战斗编制有几级(响应优先级)。
我们再对照STM32的分组来看看:STM32共有5个分组
NVIC_PriorityGroup_0:A集团军(0级抢占,16级响应)
NVIC_PriorityGroup_1:B集团军(2级抢占,8级响应)
NVIC_PriorityGroup_2:C集团军(4级抢占,4级响应)
NVIC_PriorityGroup_3:D集团军(8级抢占,2级响应)
NVIC_PriorityGroup_4:E集团军(16级抢占,0级响应)
于是,我们配置NVIC中断优先级的步骤是:
1.先确定中断分组。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //先确定分组
2.再分配抢占优先级和响应优先级。
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级(共4级,可选0~3)
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;//响应优先级(共4级,可选0~3)
假如你的项目里只有1个中断,那么优先级的数字随便设(在级数范围内),反正没人会跟他抢。
实际应用中的注意点
1.实际我们的一个项目中,不会出现太多中断,为了简单方便,我们只用NVIC_PriorityGroup_4就好了,这样只需要关注抢占优先级,响应优先级根本就没有。
2.如果没有用到中断,也就不需要配置中断分组和优先级。但是一般来说我们一个项目中至少都会用到一个中断,为了避免忘记分组,无论是否需要,最好在main函数的最前面就开始分组,以后开启哪个中断,就只配置优先级就可以了。
3.经常我们用SysTick定时器来计时,开启了SysTick中断,因为计时准确性是比较关键的,因此一般会把SysTick定时器中断设定为0级(最高),防止计时出现偏差。
关于SysTick定时器的配置可以看这里,利用SysTick延时,
示例(UART4开启接收中断)
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//中断分组
uart4_init(115200);
while(1)
{
//do something
}
}
void uart4_init(u32 bound)
{
GPIO_InitTypeDef GPIOC_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//GPIO时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);//串口4时钟使能
GPIOC_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出(作为TX)
GPIOC_InitStruct.GPIO_Pin = GPIO_Pin_10; //PC10
GPIOC_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOC, &GPIOC_InitStruct);//GPIO初始化配置
GPIOC_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入(作为RX)
GPIOC_InitStruct.GPIO_Pin = GPIO_Pin_11; //PC11
GPIOC_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOC, &GPIOC_InitStruct);//GPIO初始化配置
USART_InitStruct.USART_BaudRate = bound; //默认115200
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//发送、接收模式
USART_InitStruct.USART_Parity = USART_Parity_No;//不使用奇偶校验
USART_InitStruct.USART_StopBits = USART_StopBits_1;//停止位1位
USART_InitStruct.USART_WordLength = USART_WordLength_8b;//8位字长
USART_Init(UART4, &USART_InitStruct);//串口4初始化配置
//配置中断优先级
NVIC_InitStruct.NVIC_IRQChannel = UART4_IRQn;//串口4中断通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能该通道
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;//响应优先级
NVIC_Init(&NVIC_InitStruct);//中断初始化配置
USART_ITConfig(UART4,USART_IT_RXNE, ENABLE);//开启串口4的中断
USART_Cmd(UART4, ENABLE);//使能串口4
}