席萌0209 发表于 2016-8-20 16:27:03

第12章 FreeRTOS中断优先级配置(重要)




第12章    FreeRTOS中断优先级配置(重要)

    本章节为大家讲解FreeRTOS中断优先级配置,此章节非常重要,初学者经常在这里犯迷糊。对于初学者来说,本章节务必要整明白。
12.1 NVIC基础知识
12.2 使用FreeRTOS时如何配置外设NVIC
12.3 FreeRTOS配置选项中NVIC相关配置
12.4 不受FreeRTOS管理中的的深入讨论
12.5总结

12.1 NVIC基础知识

    NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。
    对于M3和M4内核的MCU,每个中断的优先级都是用寄存器中的8位来设置的。8位的话就可以设置2^8 =256级中断,实际中用不了这么多,所以芯片厂商根据自己生产的芯片做出了调整。比如ST的STM32F1xx和F4xx只使用了这个8位中的高四位,低四位取零,这样2^4=16,只能表示16级中断嵌套。
    对于这个NVIC,有个重要的知识点就是优先级分组,抢占优先级和子优先级,下面就以STM32为例进行介绍,STM32F1xx和F4xx都是只使用了这个8位寄存器的高四位。
图12.1 优先级分组
    从上面的表格可以看出,STM32支持5种优先级分组,系统上电复位后,默认使用的是优先级分组0,也就是没有抢占式优先级,只有子优先级,关于这个抢占优先级和这个子优先级有几点一定要说清楚。
    l具有高抢占式优先级的中断可以在具有低抢占式优先级的中断服务程序执行过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以抢占低抢占式优先级的中断的执行。
    l在抢占式优先级相同的情况下,有几个子优先级不同的中断同时到来,那么高子优先级的中断优先被响应。
    l在抢占式优先级相同的情况下,如果有低子优先级中断正在执行,高子优先级的中断要等待已被响应的低子优先级中断执行结束后才能得到响应,即子优先级不支持中断嵌套。
    lReset、NMI、Hard Fault 优先级为负数,高于普通中断优先级,且优先级不可配置。
    l对于初学者还有一个比较纠结的问题就是系统中断(比如:PendSV,SVC,SysTick)是不是一定比外部中断(比如SPI,USART)要高,答案:不是的,它们是在同一个NVIC下面设置的。

    掌握了这些基础知识基本就够用了。另外特别注意一点,配置抢占优先级和子优先级,他们合并成的4bit数字的数值越小,优先级越高,这一点千万不要搞错了,下面通过12.2小节举一个实例。

席萌0209 发表于 2016-8-20 16:30:22

12.2 使用FreeRTOS时如何配置外设NVIC


    强烈推荐用户将Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及STM32F429的NVIC优先级分组设置为4,即:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);这样中断优先级的管理将非常方便。这个也是官方强烈建议的。此函数在bsp_Init中第一个被调用:
/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
*             全局变量。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{   
   /* 优先级分组设置为4, 优先配置好NVIC */
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
   
   bsp_InitUart();    /* 初始化串口 */
   bsp_InitLed();   /* 初始LED指示灯端口 */
   bsp_InitKey();   /* 初始化按键 */
   
}
    (注意:一旦初始化好NVIC的优先级分组后,切不可以在应用中再次更改。)
    设置NVIC的优先级分组为4表示支持0-15级抢占优先级(注意,0-15级是16个级别,包含0级),不支持子优先级。反映在STM32标准库的配置上就是如下:
/*
*********************************************************************************************************
*    函 数 名: TIM_Config
*    功能说明: 配置定时器TIM2的中断
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
static void TIM_Config(void)
{
   NVIC_InitTypeDefNVIC_InitStructure;

   NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
/* 抢占优先级设置,优先级分组为4的情况下,抢占优先级可设置范围0-15 */
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
/* 子优先级设置,优先级分组为4的情况下,子优先级无效,取数值0即可 */
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;      
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init(&NVIC_InitStructure);
}
在这里继续强调下这一点,在NVIC分组为4的情况下,抢占优先级可配置范围是0-15,那么数值越小,抢占优先级的级别越高,即0代表最高优先级,15代表最低优先级。

席萌0209 发表于 2016-8-20 16:34:33

12.3 FreeRTOS配置选项中NVIC相关配置


    FreeRTOSConfig.h配置文件中设置到NVIC中断的有如下几个选项:
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
   /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
   #define configPRIO_BITS            __NVIC_PRIO_BITS
#else
   #define configPRIO_BITS            4      /* 15 priority levels */
#endif

/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY            0x0f

/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions.DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x01

/* Interrupt priorities used by the kernel port layer itself.These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY      ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY   ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
u#define configPRIO_BITS             4
      此宏定义用于配置STM32的8位优先级设置寄存器实际使用的位数。STM32F103,STM32F407和STM32F429都是使用的4位。另外注意一点,这里使用了一个条件编译,用户可以选择将条件编译删掉,直接定义一个#defineconfigPRIO_BITS4即可。使用条件编译的好处就是方便与系统统一。这个__NVIC_PRIO_BITS在STM32F103标准库的头文件stm32f10x.h中以及STM32F407/439的标准库的头文件stm32f4xx.h中分别有定义。如果用户在FreeRTOSConfig.h文件里面包含了这个标准库的头文件,那么就会执行条件编译选项:
         #define configPRIO_BITS                  __NVIC_PRIO_BITS

    l __NVIC_PRIO_BITS
       关于这个__NVIC_PRIO_BITS有必要跟大家深入的说明下,我们要刨根问底。在CMSIS软件包的M3内核头文件core_cm3.h和M4内核的头文件core_cm4.h文件里面已经进行了定义:
                  #ifndef __NVIC_PRIO_BITS
                      #define __NVIC_PRIO_BITS          4U
                   #endif
      也就是说,如果用户没有定义的话,这里的定义就会起作用。而STM32F103标准库的头文件stm32f10x.h和STM32F407/439的标准库的头文件stm32f4xx.h有定义了,而且相应的头文件core_cm3.h和core_cm4.h是放在了宏定义#define __NVIC_PRIO_BITS4的后面,如此一来,头文件core_cm3.h和core_cm4.h的定义就会被屏蔽掉。STM32F103标准库的头文件stm32f10x.h和STM32F407/439的标准库的头文件stm32f4xx.h里面的定义是有效的。

u#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY                     0x0f
      此宏定义是用来配置FreeRTOS用到的SysTick中断和PendSV中断的优先级。在NVIC分组设置为4的情况下,此宏定义的范围就是0-15,即专门配置抢占优先级。这里配置为了0x0f,即SysTick和PendSV都是配置为了最低优先级,实际项目中也建议大家配置最低优先级即可。

    l SVC中断
      在FreeRTOS的移植文件ports.c中有用到SVC中断的0号系统服务,即SVC 0。此中断在FreeRTOS中仅执行一次,用于启动第一个要执行的任务。另外,由于FreeRTOS没有配置SVC的中断优先级,默认没有配置的情况下,SVC中断的优先级就是最高的0。如果用户在不清楚自己配置的PendSV和SysTick中断是否跟实际情况一致时,可以进行硬件调试。比如MDK,我们可以在硬件调试的状态下,先点击全速运行,然后查看如下调试组件:


      打开后可以看到如下状态,其中SysTick和PendSV中断的优先级240就是0x0f左移4位的结果。这里为什么要左移四位呢,前面我们已经多次强调了,STM32的优先级设置仅使用高4位。而SVC的优先级就是0,可以理解为0左移4位还是0。

u#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x01
      此宏定义比较重要,定义了受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。设置NVIC的优先级分组为4的情况下。配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY为0x01表示用户可以在抢占式优先级为1到15的中断里面调用FreeRTOS的API函数,抢占式优先级为0的中断里面是不允许调用的。不受FreeRTOS管理的中断有什么深层的含义吗?且看12.4小节继续为大家讲解。


u#define configKERNEL_INTERRUPT_PRIORITY
      宏定义configLIBRARY_LOWEST_INTERRUPT_PRIORITY的数值经过4bit偏移后得到一个8bit的优先级数值,即宏定义configKERNEL_INTERRUPT_PRIORITY的数值。这个8bit的数值才可以实际赋值给相应中断的优先级寄存器。
      也许初学者有疑问了,为什么前面NVIC配置的时候不是8bit的方式进行配置?这是因为ST的库函数NVIC_Init()已经为我们做好了。这里的宏定义数值是供PendSV和SysTick中断进行优先级配置的。比如:我们这里配置宏定义configLIBRARY_LOWEST_INTERRUPT_PRIORITY是0x0f,经过4bit偏移后就是0xf0,即SysTick和PendSV的中断优先级就是240。


u#define configMAX_SYSCALL_INTERRUPT_PRIORITY
      宏定义configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY的数值经过4bit偏移后得到一个8bit的优先级数值,即宏定义configMAX_SYSCALL_INTERRUPT_PRIORITY的数值。这个数值是赋值给寄存器basepri使用的,8bit的数值才可以实际赋值给相应中断的优先级寄存器。
      这里的宏定义数值赋给寄存器basepri后就可以实现全局的开关中断操作了。比如:我们这里配置宏定义configLIBRARY_LOWEST_INTERRUPT_PRIORITY是0x01,经过4bit偏移后就是0x10,即16。调用了FreeRTOS的关中断后,所有优先级数值大于等于16的中断都会被关闭。优先级数值小于16的中断不会被关闭,对寄存器basepri寄存器赋值0,那么被关闭的中断会被打开。

席萌0209 发表于 2016-8-20 16:38:41

12.4 不受FreeRTOS管理中断的深入讨论


    讲解不受FreeRTOS管理的中断之前要说一个小知识点----中断延迟。中断延迟时间是衡量RTOS实时操作系统的一项重要指标,那什么又是中断延迟呢?从中断触发到执行中断服务程序的第一条指令这段时间就是中断延迟时间。
    FreeRTOS内核源码中有多处开关全局中断的地方,这些开关全局中断会加大中断延迟时间。比如在源码的某个地方关闭了全局中断,但是此时有外部中断触发,这个中断的服务程序就需要等到再次开启全局中断后才可以得到执行。开关中断之间的时间越长,中断延迟时间就越大,这样极其影响系统的实时性。如果这是一个紧急的中断事件,得不到及时执行的话,后果是可想而知的。
    针对这种情况,FreeRTOS就专门做了一种新的开关中断实现机制。关闭中断时仅关闭受FreeRTOS管理的中断,不受FreeRTOS管理的中断不关闭,这些不受管理的中断都是高优先级的中断,用户可以在这些中断里面加入需要实时响应的程序。FreeRTOS能够实现这种功能的奥秘就在于FreeRTOS开关中断使用的是寄存器basepri,而像uCOS这种使用的是primask,详情请看下面整理的表格:       对寄存器basepri我们举一个例子,帮助大家理解,比我们配置寄存器basepri的数值为16,所有优先级数值大于等于16的中断都会被关闭,优先级数值小于16的中断不会被关闭。对寄存器basepri寄存器赋值0,那么被关闭的中断会被打开。这个就是FreeRTOS开关中断的实现方案。

席萌0209 发表于 2016-8-20 16:39:07

12.5 总结

    本章节为大家讲解FreeRTOS中断优先级配置,内容相比前面章节要复杂些,不知道大家是否已经理解透彻了?如果没有理解透彻,务必多读几遍,直到完全弄明白。

agkody 发表于 2016-11-30 00:17:10

回复的人几乎没有啊,谢谢楼主

hell0w0rd 发表于 2017-7-6 17:30:00

首先谢谢楼主辛苦写的教程,然后有点小疑问想请教下,教程中的这一段(如下文),红色部分是不是应该写成 #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x01。这只是我的猜测,不知道对不对。
还有一点,如下4个宏定义是不是可以归为两组,互相代替,同时只使用一组就可以了,还请指教。
初接触FreeRTOS,好多都不懂,看到楼主写的教程很详细,解决了我很多的疑惑,再次谢谢楼主
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0f
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 0x01

#define configKERNEL_INTERRUPT_PRIORITY
#define configMAX_SYSCALL_INTERRUPT_PRIORITY


#define configMAX_SYSCALL_INTERRUPT_PRIORITY
      宏定义configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY的数值经过4bit偏移后得到一个8bit的优先级数值,即宏定义configMAX_SYSCALL_INTERRUPT_PRIORITY的数值。这个数值是赋值给寄存器basepri使用的,8bit的数值才可以实际赋值给相应中断的优先级寄存器。
      这里的宏定义数值赋给寄存器basepri后就可以实现全局的开关中断操作了。比如:我们这里配置宏定义configLIBRARY_LOWEST_INTERRUPT_PRIORITY是0x01,经过4bit偏移后就是0x10,即16。调用了FreeRTOS的关中断后,所有优先级数值大于等于16的中断都会被关闭。优先级数值小于16的中断不会被关闭,对寄存器basepri寄存器赋值0,那么被关闭的中断会被打开。

eric2013 发表于 2017-7-7 01:34:45

回 hell0w0rd 的帖子

hell0w0rd:首先谢谢楼主辛苦写的教程,然后有点小疑问想请教下,教程中的这一段(如下文),红色部分是不是应该写成 #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x01。这只是我的猜测,不知道对不对。
还有一点,如下4个宏定义是不是可以归为两组,互相 .. (2017-07-06 17:30) images/back.gif

1. 对的,教程中写的有误。
2. 可以这么理解。

hell0w0rd 发表于 2017-7-10 11:18:16

回 eric2013 的帖子

eric2013:

1. 对的,教程中写的有误。
2. 可以这么理解。
images/back.gif

谢谢楼主,解惑

bian 发表于 2019-3-8 06:32:18

这么好的贴,给你点赞

wangdunyu 发表于 2023-12-8 20:29:26

席萌0209 发表于 2016-8-20 16:34
12.3   FreeRTOS配置选项中NVIC相关配置



感谢楼主,介绍得很详细
页: [1]
查看完整版本: 第12章 FreeRTOS中断优先级配置(重要)