FreeRTOS学习笔记—CM4内核中断在RTOS中的使用方法

最近要移植FreeRTOS操作系统,这也是个学习的过程,在此以笔记的形式记录下来。本篇文章主要是谈Cortex-M4内核中断的一些概念以及其在FreeRTOS中的使用方法,部分内容摘自《Cortex-M3权威指南(宋岩译)》,在此向作者表示感谢。

1 优先级值和逻辑优先级

首先要解释一下优先级值和逻辑优先级:在Cortex-M内核中,假如有8级优先级,我们说优先级值是0~7,但数值最大的优先级7却代表着最低的逻辑优先级。

接下来需要清楚的是,在Cortex-M4内核中,一个中断的优先级数值越低,逻辑优先级却越高。比如,中断优先级为2的中断可以抢占中断优先级为5的中断,但反过来就不行。换句话说,中断优先级2比中断优先级5的优先级更高。这是Cortex-内核最容易让人犯错之处,因为大多数的非Cortex-M内核微控制器的中断优先级表述是与之相反的。

FreeRTOS中所说的优先级均指逻辑优先级。

2 异常类型与优先级

异常类型
Cortex-M4内核支持众多的系统异常和外部中断。其中,编号1-15对应系统异常(没有编号为0的异常),大于等于16的则全是外部中断。除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。Cortex-M4内核异常如下。
FreeRTOS学习笔记—CM4内核中断在RTOS中的使用方法_第1张图片

优先级
在CM4中,优先级对于异常来说很关键的,它会决定一个异常是否能被掩蔽,以及在未掩蔽的情况下何时可以响应。优先级的数值越小,则优先级越高。CM4支持中断嵌套,使得高优先级异常会抢占(preempt)低优先级异常。有3个系统异常:复位,NMI 以及Hard Fault,它们有固定的优先级,并且它们的优先级号是负数,从而高于所有其它异常。所有其它异常的优先级则都是可编程的(但不能被编程为负数)。

中断优先级的设定是通过Interrupt Priority-Level Registers (0xE000E400-0xE000E4EF),即NVIC->IP[IRQn]寄存器,该寄存器共有240个。这些寄存器都是8位,原则上,其可支持3个固定的高优先级和多达256级的可编程优先级。但是,绝大多数CM4芯片都会精简设计,以致实际上支持的优先级数会更少,如8级、16 级、32 级等。它们在设计时会裁掉表达优先级的几个低端有效位,以减少优先级的级数,但不管使用多少位来表达优先级,都是以MSB对齐。举例来说,如果只使用了3个位来表达优先级,则NVIC->IP[IRQn]优先级配置寄存器的结构会如图 所示。STM32F4采用4位来表达优先级,即最高支持0-15的可编程优先级。

抢占优先级和子优先级
NVIC 中有一个Application interrupt and reset control register (AIRCR)寄存器(应用程序中断及复位控制寄存器),它里面有一个位段(10-8 Bits)名为“PRIGROUP优先级组”。该位段的值对每一个优先级可配置的异常都有影响—把其优先级分为2个位段:MSB 所在的位段对应Preempt priority(抢占优先级),而 LSB 所在的位段对应Sub-priority(子优先级),如图所示。

抢占优先级决定了抢占行为:当系统正在响应某异常L时,如果来了抢占优先级更高的异常H,则H可以抢占L。子优先级则处理“内务”:当抢占优先级相同的异常有不止一个悬起时,就最先响应子优先级最高的异常。

这种优先级分组做出了如下规定:子优先级至少是1个位。因此抢占优先级最多是7个位,这就造成了最多只有128级抢占的现象。但是CM4允许从比特7处分组,此时所有的位都表达子优先级,没有任何位表达抢占优先级,因而所有优先级可编程的异常之间就不会发生抢占—相当于在它们之中除能了CM4的中断嵌套机制。当然还有凌驾于法律之上的三位老大:复位,NMI和Hard Fault。它们无论何时出现,都立即无条件抢占所有优先级可编程的“平民异常”在计算抢占优先级和子优先级的有效位数时,必须先求出下列值:
(1)芯片实际使用了多少位来表达优先级
(2)优先级组是如何划分的。

举个例子,如果只使用3个位来表达优先级 [7:5],并且优先级组的值是5(从比特5处分组),则得到4级抢占优先级,且在每个抢占优先级的内部有有2个子优先级,则其优先级的划分如图所示。
FreeRTOS学习笔记—CM4内核中断在RTOS中的使用方法_第2张图片

3 中断优先级在FreeRTOS中的使用

在移植FreeRTOS到ARM Cortex-M内核上时,大多数的问题点是由不正确的优先级设置引起的。FreeRTOS中优先级数值越大则优先级越高,而在Cortex-M中优先级数值越大代表的优先级反而越小。但FreeRTOS中的优先级更多的是对任务而言,Cortex-M中的优先级多是指外设中断。前者可认为是“虚拟的”,而后者是“实际发生的”,二者是存在很大差异的。本小结描述Cortex-M的中断优先级机制,并描述怎样结合RTOS内核使用。

总而言之,本小结主要是讲要想在系统某个外设中断函数中调用FreeRTOS API函数,其中断优先级需要满足哪些条件。

在接下来的描述中,正如第一小节所谈,要注意文中所说的“高优先级”或“低优先级”都是指逻辑优先级,而出现的具体数字则是指优先级值,在Cortex-M中,优先级值越大的中断则说明其逻辑优先级越低。

Cortex-M优先级
Cortex-M构架自身最多允许256级可编程优先级(优先级配置寄存器最多8位,所以优先级范围从0x00~0xFF),但是绝大多数微控制器制造商只是使用其中的一部分优先级。比如, STM32F407 Cortex-M4微控制器使用优先级配置寄存器的5个位,能提供32级优先级。

如果你的工程包含CMSIS库头文件,则头文件中的宏__NVIC_PRIO_BITS定义使用多少优先级寄存器的位(默认是4位)。
FreeRTOS学习笔记—CM4内核中断在RTOS中的使用方法_第3张图片

Cortex-M内核的中断优先级寄存器(NVIC->IP[IRQn])是以最高位(MSB)对齐的。比如,如果使用了3位来表达优先级,则这3个位位于中断优先级寄存器的bit5、bit6、bit7位。剩余的bit0~bit4可以设置成任何值,但为了兼容,最好将他们设置成1。

应用到FreeRTOS
RTOS中断嵌套方案利用以下两个宏定义了两组中断优先级的边界,从而将有效的中断优先级分成两组:

#define configMAX_SYSCALL_INTERRUPT_PRIORITY(FreeRTOSConfig.h中配置)
#define configKERNEL_INTERRUPT_PRIORITY(FreeRTOSConfig.h中配置)

FreeRTOS学习笔记—CM4内核中断在RTOS中的使用方法_第4张图片

一组可以通过RTOS临界区屏蔽,另一组不受RTOS影响,永远都是使能的。其中逻辑优先级高于configMAX_SYSCALL_INTERR UPT_PRIORITY(即优先级数值小于该宏定义)的中断不受RTOS影响。最优值取决于微控制器使用的优先级配置寄存器的位数。

以“FromISR”结尾的FreeRTOS函数是具有中断调用保护的(执行这些函数会进入临界区),但是就算是这些函数,也不可以被逻辑优先级高于configMAX_SYSCALL_INTERRUPT_PRIORIT的中断服务函数调用。因此,任何使用RTOS API函数的外设中断服务例程的中断优先级数值必须大于等于configMAX_SYSCALL_INTERRUPT_PRIORITY宏的值。这样才能保证中断的逻辑优先级等于或低于configMAX_SYSCALL_INTERRUPT_PRIORITY。

Cortex中断默认情况下有一个数值为0的优先级。大多数情况下0代表最高级优先级。因此,绝对不可以在优先级为0的中断服务例程中调用RTOS API函数。

上文中已经描述,那些在外设中断服务例程中调用RTOSAPI函数的中断逻辑优先级必须低于或等于configMAX_SYSCALL_INTE RRUPT_PRIORITY(低逻辑优先级意味着高优先级数值)。

可以在FreeRTOSConfig.h中设置宏的值,这两个宏需要根据Cortex-M内核自身的情况进行设置,要以最高有效位对齐。比如某微控制器使用中断优先级寄存器中的3位,设置configKERNEL_INTERRUPT_PRIORITY的值为5,则代码为:

#define configKERNEL_INTERRUPT_PRIORITY (5<<(8-3) )

临界区

RTOS内核使用Cortex-M内核的BASEPRI寄存器来实现临界区(注:BASEPRI为优先级屏蔽寄存器,优先级数值大于或等于该寄存器的中断都会被屏蔽,优先级数值越大,逻辑优先级越低,但是为零时不屏蔽任何中断)。这允许RTOS内核可以只屏蔽一部分中断,因此可以提供一个灵活的中断嵌套模式。

那些需要在中断调用时保护的API函数,FreeRTOS使用寄存器BASEPRI实现中断保护临界区。当进入临界区时,将寄存器BASEPRI的值设置成configMAX_SYSCALL_INTERRUPT_PRIORITY,当退出临界区时,将寄存器BASEPRI的值设置成0。很多Bug反馈都提到,当退出临界区时不应该将寄存器设置成0,应该恢复它之前的状态(之前的状态不一定是0)。但是Cortex-M NVIC决不会允许一个低优先级中断,去打断当前正在执行的高优先级中断,不管BASEPRI寄存器中是什么值。与进入临界区前先保存BASEPRI的值,退出临界区再恢复的方法相比,退出临界区时将BASEPRI寄存器设置成0的方法可以获得更快的执行速度。

RTOS内核通过写configMAX_SYSCALL_INTERRUPT_PRIORITY的值到BASEPRI寄存器的方法创建临界区。中断优先级0(具有最高的逻辑优先级)不能被BASEPRI寄存器屏蔽,因此,configMAX_SYSCALL_INTERRUPT_PRIORITY绝不可以设置成0。

4 总结

(1) FreeRTOS通过两个宏将系统外设中断分为两类:第一类是外设中断不受RTOS影响(不会被RTOS延迟),但此外设中断响应函数中不可调用RTOS的API;第二类是外设中断受RTOS影响(可能会被RTOS延迟),但此外设中断响应函数中可调用RTOS的API。

(2)configMAX_SYSCALL_INTERRUPT_PRIORITY(默认为5)定义了受RTOS影响的外设中断的最高逻辑优先级,configKERNEL_INTERRUPT_PRIORITY(默认为0xF)定义了受RTOS影响的外设中断的最低逻辑优先级。

当configMAX_SYSCALL<=外设中断优先级数值<= configKERNEL时,该外设中断属于第二类。

当外设中断优先级数值<= configMAX_SYSCALL时,该外设中断属于第一类。

(3)由于中断优先级寄存器(NVIC->IP[IRQn])是以最高位(MSB)对齐,在设置的时候要进行移位操作。

若设置configMAX_SYSCALL_INTERRUPT_PRIORITY为5,则宏定义应该写为:

#define configMAX_SYSCALL_INTERRUPT_PRIORITY (5 << (8 - __NVIC_PRIO_BITS) )

若设置configKERNEL_INTERRUPT_PRIORITY为0xF,则宏定义应该写为:

#define configKERNEL_INTERRUPT_PRIORITY (0xF << (8 - __NVIC_PRIO_BITS) )

你可能感兴趣的:(Cortex,RTOS)