目录
前言
在使用os前,我们都知道任务调度都是依靠一个Systick的中断(os的内核中断之一)进行任务切换的,既然是中断那么此时优先级是怎么样的配置的配置成优先级最高还是最低还是随意,以及配置原理是怎样的。
os内核中断优先级大小
首先,关于内核的中断优先级配置,我们可以从手册中发现,内核中断的优先级应当配置为最低优先级。
os内核中断配置宏
打开官方文档(FreeRTOSConfig.h),可以看到关于中断配置的宏有三个:
/* Interrupt nesting behaviour configuration. */
#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor]
#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application]
#define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application]
下面我们来看下这三个宏应该如何配置以及对应的含义。先看下面这句话:
configMAX_API_CALL_INTERRUPT_PRIORITY is a new name for configMAX_SYSCALL_INTERRUPT_PRIORITY that is used by newer ports only. The two are equivalent.很明显的理解我们需要配置的就是这两个了:
#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor]
#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application]
1、第一个configKERNEL_INTERRUPT_PRIORITY 这个意思是配置os的内核中断优先级,简单理解也就是Systick的优先级(另一个是PendSv优先级)。这个配置方式很简单明了,就配置成最低优先级,因为官方文档中说:configKERNEL_INTERRUPT_PRIORITY should be set to the lowest priority.
2、第二个configMAX_SYSCALL_INTERRUPT_PRIORITY 这个意思是配置os内核可管理的中断优先级以及在此类中断优先级以内的中断是否可以调用os的相关API函数使用的。下面看下这个图:
这里我们定义数值越小优先级越低,定义内核中断的优先级0即为最低,定义Max_SYSTEMCALL那个宏为4,然后理解就是以下:
- 那么对于优先级0-4的情况下,此时如果在中断服务程序里需要调用os的“FromISR”结尾的函数是允许的也是可以嵌套的。
- 对于大于4的优先级,os无权干预,这里的干预(如何理解,后续说明)可以暂时理解成可以快速响应的一些中断,因为对于0-4优先级,os可以退出其响应时间,造成此中断不是实时响应。这类中断服务函数里面是绝对不可以调用os的相关“FromISR”函数的。
- 中断服务函数本身是硬件层面上的,只所以os也需要关注就是因为你在中断里面调用了相关os的功能,将会影响到其任务调度相关,所以如果你中断里面从来都不会想用这类函数,大可不必过度关心这些东西,因为os只会在没有中断后才会执行的。至于中断后的堆栈怎么处理,这个也没必要考虑,当正在运行一个任务时,如果产生了一个中断,此时会直接进入中断服务函数里面,此时中断服务函数用的堆栈就是当前任务的堆栈,当中断结束后很自然就回来了,这里面压根没有os的参与其实os压根就不知道来了一个中断。
os中断配置原理
我们知道需要配置后的参数后,下一步面临如何配置以及原理。我们使用的平台是STM32F103的所以首先明确以下几点:
- Cortex-M3中断优先级分组
- Cortex-M3使用的中断优先级配置为子节的高4位。
优先级分组
关于优先级分组官方就建议配置成4位全为抢占优先级,对应的也就是中断优先级分组4.只所以这样也很好理解,一个中断可以抢占另一个相当于高任务可以抢占低任务,不然在os里面高优先级任务不可以优先运行,还有什么意义,所以这里直接配置成:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
另外一个,为何只用到高四位,ST就是这样定义的,无法反驳。
配置原理
说到现在终于说到配置,我们都知道内核中断配置成最低,在分组4的情况下一般配置为15,那么这里就直接是15吗?到底是不是15去看下官方例程里面的FreeRTOSConfig.h
/* This is the raw value as per the Cortex-M3 NVIC. Values can be 255
(lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY 255
/* !!!! 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 191 /* equivalent to 0xb0, or priority 11. */
看到没,这里是255并非15,如果你配置成15基本完蛋,也就是最高优先级,后果是啥咱还没测试,影响最大的也就是中断。所以下面,咱们来看下原理,为何是255而不是15了:
每个外部中断都有一个对应的优先级寄存器,每个寄存器占用 8 位,对于Cortex-M3只用了高四位,当分组为4时高四位数值也就是对应的优先级,知道这个了下面看下configKERNEL_INTERRUPT_PRIORITY是如何使用的:
port.c
...
#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
...
#define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) )
...
...
/* Make PendSV and SysTick the lowest priority interrupts. */
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
看到了吧,32存贮方式为小字端所以左移16位和24位置设置两个内核中断,至于PendSV中断是什么,简单来说就是利用它实现任务切换地组件之一,可以好好看看。然后由于我们只用高4位,第四位不使用需要保持默认值也就是1。这样就很好理解下面这两个宏的定义理解了。
/* This is the raw value as per the Cortex-M3 NVIC. Values can be 255
(lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY 255
/* !!!! 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 191 /* equivalent to 0xb0, or priority 11. */
总结
实际看时可能还会看到不少宏如:
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15
#define configPRIO_BITS 4
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0f
这类等等的吧,很老的FreeRTOS我没用过,不知道以前官方是不是这样定义的,其实这些宏都是为了更好的帮助我们设置在不同中断优先级分组的情况下快速设置以上两个宏使用的,知道了对应的原理也就明白了他们定义的那些东西的含义了。
还有一个很重要的东西,就是下面这句话:
A special note for ARM Cortex-M3 and ARM Cortex-M4 users: Please read the page dedicated to interrupt priority settings on ARM Cortex-M devices. As a minimum, remember that ARM Cortex-M3 cores use numerically low priority numbers to represent HIGH priority interrupts, which can seem counter-intuitive and is easy to forget! If you wish to assign an interrupt a low priority do NOT assign it a priority of 0 (or other low numeric value) as this can result in the interrupt actually having the highest priority in the system - and therefore potentially make your system crash if this priority is above configMAX_SYSCALL_INTERRUPT_PRIORITY.
The lowest priority on a ARM Cortex-M3 core is in fact 255 - however different ARM Cortex-M3 vendors implement a different number of priority bits and supply library functions that expect priorities to be specified in different ways. For example, on the STM32 the lowest priority you can specify in an ST driver library call is in fact 15 - and the highest priority you can specify is 0.
大致意思就是, Cortex-M3和Cortex-M4中断优先级和其数值是相反的,就是0是最高优先级,而15确是最低的优先级。在定义configMAX_SYSCALL_INTERRUPT_PRIORITY后记得小于这个值不能调用os相关API,只有大于才可以。