【STM32】进阶(一):抢占式优先级和响应式优先级(NVIC_PriorityGroupConfig)

【STM32】STM32单片机总目录

1、简介

STM32(Cortex-M3)中每个中断源都有两级优先级:抢占式优先级(pre-emption priority)和子优先级(subpriority),子优先级也叫响应式优先级。

1.1 抢占式优先级

望文知义,就是优先级高的可以抢占优先级的中断。

具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套在低抢占式优先级的中断中。

1.2 子优先级(响应式优先级)

望文知义,就是当两个抢占式优先级同时来时,先处理响应式优先级高的(谁优先级高先响应谁)。

当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。

总结下便是:抢占式优先级>响应优先级>中断表中的排位顺序(其中“>”理解为比较的方向)。

2、划分优先级

每个中断源都需要被指定这两种优先级,如果用一个字节(8位)表示中断源,将这8位划分成两部分,用前几位表示抢占式优先级,后几位表示响应式优先级,可以有有8种分配方式,如下:

1. 所有8位用于指定响应优先级
2. 最高1位用于指定抢占式优先级,最低7位用于指定响应优先级
3. 最高2位用于指定抢占式优先级,最低6位用于指定响应优先级
4. 最高3位用于指定抢占式优先级,最低5位用于指定响应优先级
5. 最高4位用于指定抢占式优先级,最低4位用于指定响应优先级
6. 最高5位用于指定抢占式优先级,最低3位用于指定响应优先级
7. 最高6位用于指定抢占式优先级,最低2位用于指定响应优先级
8. 最高7位用于指定抢占式优先级,最低1位用于指定响应优先级

以上便是优先级分组的概念,但是Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:

第0组:所有4位用于指定响应优先级
第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
第4组:所有4位用于指定抢占式优先级

3、设置优先级分组模式

在程序初始化阶段,使用固件库函数NVIC_PriorityGroupConfig先来对优先级分组模式。
NVIC_PriorityGroupConfig(u32 NVIC_PriorityGroup)函数的参数共有5种:

NVIC_PriorityGroup_0 => 选择第0组
NVIC_PriorityGroup_1 => 选择第1组
NVIC_PriorityGroup_2 => 选择第2组
NVIC_PriorityGroup_3 => 选择第3组
NVIC_PriorityGroup_4 => 选择第4组

源码定义,在STM32F10x_FWLib\inc\misc.h中

#define NVIC_PriorityGroup_0         ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
                                                            4 bits for subpriority */
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
                                                            3 bits for subpriority */
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
                                                            2 bits for subpriority */
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
                                                            1 bits for subpriority */
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
                                                            0 bits for subpriority */

NVIC_PriorityGroupConfig在源码STM32F10x_FWLib\src\misc.c中
主要设置这个寄存器SCB->AIRCR:SCB(系统控制块的内存映射结构)中的 AIRCR(应用程序中断/重置控制寄存器)

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_1,那么抢占式优先级便占一位,也就是说可以有2个级别,可以设置为0和1,而响应优先级则占3位,也就是说可以有2^3个选择,可以设置为0~7;总共来说就可以区别16种优先级,多余16种优先级时,再按照中断表中的排位顺序执行。

举个例子吧,假如现在有4个外部中断,还有一个EXTI9_5中断,那么如果选择优先级分组为第1组,那么抢占式优先级便只有两种,5个中断就至少有3个在抢占式优先级上是相同的优先级上,其他两个在令一优先级别。接着设置响应优先级可以有8种选择;假如现在同时有两个抢占式优先级别相同的中断发生,那么处理的顺序是谁的响应优先级高则谁优先进入中断,另外这点是需要注意的,如果此时进入这个中断之后又来了一个抢占式优先级相同但是响应优先级更高的中断,这时也是不会打断已有的中断的

4、设置中断

以设置串口中断为例,完整代码如下

void uart_init(u32 bound){
    
    
    GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	# a)使能USART1、GPIOA时钟 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	
  
  	# b)引脚初始化
	//USART1_TX   GPIOA.9 初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用为推免输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
	//USART1_RX	  GPIOA.10 初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

    # c)配置中断优先级Usart1 NVIC
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占式优先级为3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级(响应式优先级)为3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//配置
  
    # d)USART初始化设置
	USART_InitStructure.USART_BaudRate = bound;					//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	//数据长度8位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;		//停止位1位
	USART_InitStructure.USART_Parity = USART_Parity_No;			//无奇偶校验
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式(读写都使能)
	
	# e) 初始化串口、使能串口中断、使能串口
	USART_Init(USART1, &USART_InitStructure); 		//初始化串口1
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);	//开启串口中断
	USART_Cmd(USART1, ENABLE);                    	//使能串口1
}

5、中断处理函数

注意 USART_IT_IDLE 和 USART_IT_RXNE 区别:

当接收到1个字节,会产生 USART_IT_RXNE 中断
当接收到1帧数据,会产生 USART_IT_IDLE 中断
void USART1_IRQHandler(void)                
{
    
    
	u8 Res;
	u8 clear = 0;
	
	# a)
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   
	{
    
    
		Res =USART_ReceiveData(USART1);	//读取收到的数据
		TEST_RX_BUF[TEST_RX_STA++]=Res;	//将数据放入缓存
	} 
	else if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
	{
    
    
		clear = USART2->SR;
		clear = USART2->DR;
		clear = clear;
		TEST_RX_BUF[0] = TEST_RX_STA- 1;
		TEST_RX_STA= 1;
	} 											 
} 

猜你喜欢

转载自blog.csdn.net/u010168781/article/details/129378202