NXP

在ARM CORTEX-M内核上运行FreeRTOS

2019-07-12 13:22发布

英文原文:http://www.freertos.org/RTOS-Cortex-M3-M4.html

注意

文章中关于中断嵌套的信息仅适用于Cortex-M3,Cortex-M4,Cortex-M4F和Cortex-M7,文章不适用于CORTEX-M0和CORTEX-M0+内核,因为CORTEX-M0和CORTEX-M0+内核没有BASEPRI寄存器。

简介

数以千计的使用了FreeRTOS的应用运行在ARM CORTEX-M内核的CPU上,然而令人惊讶的是,对于FreeRTOS+ARM CORTEX-M CPU这个组合的技术支持却少的可怜。错误的中断优先级设置是主要原因。尽管ARM CORTEX-M内核使用的中断模型非常强大,但是对那些用惯了使用符合常理的中断优先级的工程师来说,这样的中断模型依然显示有些傻逼和违背常理。这篇文章致力于描述ARM CORTEX-M内核的中断优先级机制,以及在使用FreeRTOS的时候该怎样配置中断优先级。
记住,虽然ARM CORTEX-M3内核提供的优先级机制看起来比较复杂,但是FreeRTOS官方的每一个应用案例都提供了正确的配置以供参考。另外FreeRTOS从V7.5.0版本开始引进了configASSERT()调用用来捕获ARM CORTEX-M中断控制器的错误配置。请确保开发过程中configASSERT配置项是打开的。

可用的有效优先级

CORTEX_M内核的硬件描述

由于并不是所有的ARM CORTEX-M微控制器都提供相同数量的中断优先级,所以对于一款使用了ARM CORTEX-M内核的微控制器,首先需要知道的是制造商总共实现了多少数量有效的中断优先级。ARM CORTEX-M架构本身最多允许256个不同的优先级(0~0xFF共256个),但是大多是情况下,如非必要,微控制器并不会实现这么多数量的中断优先级。比如,TI Stellaris系列的CORTEX-M3和CORTEX-M4微控制器使用了三个比特共能提供八个不同的优先级,NXP的LPC17xx系列的CORTEX-M3微控制器使用了五个比特共能提32个不同的优先级。如果你的项目使用了CMSIS库,请检查CMSIS头文件中的__NVIC_PRIO_BITS宏定义查看微控制器使用了几个比特来实现不同的优先级。

与FreeRTOS的关联

FreeRTOS的中断嵌套机制将有效的中断优先级分为两组:可被FreeRTOS临界区屏蔽的中断和不可被FreeRTOS临界区屏蔽的中断。FreeRTOSConfig.h中的configMAX_SYSCALL_INTERRUPT_PRIORITY宏定义了这两个组的边界,宏的最佳取值取决于微控制器的优先级由多少比特实现。

抢占优先级与子优先级

CORTEX_M内核的硬件描述

8比特宽度的优先级寄存器被分为两个部分:抢占优先级和子优先级,每部分占的比特宽度可配置。抢占优先级定义了中断是否可以打断另一个正在执行的中断,子优先级决定了当同一时刻相同抢占优先级的两个中断同时发生的时候哪个中断先运行。

与FreeRTOS的关联

推荐将所有的有效优先级比特位分配给抢占优先级,子优先级不分配比特位。任何其他的配置将使configMAX_SYSCALL_INTERRUPT_PRIORITY的设置和分配给外设中断的优先级复杂化。很多系统默认便是这种期望的配置,然而STM32驱动库确实一个例外。如果你使用STM32微控制器并且使用了STM32驱动库,请在FreeRTOS开始运行之前确保所有的有效优先级比特位分配给抢占优先级,你可以通过调用函数NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4)来实现这一目的。

优先级的设定值和逻辑优先级的逆向关系

CORTEX_M内核的硬件描述

关于ARM CORTEX-M内核接下来需要知道的事情是,优先级取值越低代表的逻辑优先级越高。比如,一个取值为2的中断的优先级高于一个取值为5的中断的优先级,换句话说,中断优先级2大于中断优先级5,中断优先级为2的中断可以打断正在执行的中断优先级为5的中断,但中断优先级为5的中断不可以打断正在执行的中断优先级为2的中断。这是ARM Cortex-M中断优先级中最违背常理的一方面,它和大多数非ARM Cortex-M内核的微控制器恰好相反。

与FreeRTOS的关联

FreeRTOS中以“FromISR”结尾的函数是中断安全的,但是即使这些函数不能从中断中调用,它们仍然拥有一个大于configMAX_SYSCALL_INTERRUPT_PRIORITY设置值的逻辑优先级。因此,任何使用了FreeRTOS API的中断服务程序必须设置自己的优先级值,十七在数值上大于等于configMAX_SYSCALL_INTERRUPT_PRIORITY的值。CORTEX-M中断的默认优先级值是0,0是最高逻辑优先级。因此,不要让使用了FreeRTOS 中断安全API的中断优先级使用默认值。

CORTEX-M内部优先级的表示方式

CORTEX_M内核的硬件描述

ARM CORTEX-M内核将中断优先级值存储在八比特宽度的中断优先级寄存器的最高有效位,比如,如果一个ARM CORTEX-M内核的微控制器使用了三个比特来实现优先级,这三个比特将会被分别的传送至bit5,bit6和bit7的位置。bit0~4可以是任意值,考虑到未来的兼容性,bit0~4最好设置为1。
ARM CORTEX-M内部优先级的表示方式如上图所示:CORTEX-M优先级寄存器最大宽度是8bit,假设一个微控制器使用了三个比特来表示优先级,那么上图展示了这三个比特的使用情况。
对于一个使用了三个比特来表示优先级的微控制器,上图展示优先级值为5(二进制101)时在优先级寄存器中的存储形式,上图也展示了当三个比特被移到需要的位置且保留位被设置为1时,为什么值5(二进制0000 0101)也可以被写为191(二进制1011 1111)。
对于一个使用了四个比特来表示优先级的微控制器,上图展示优先级值为5(二进制101)时在优先级寄存器中的存储形式,上图也展示了当四个比特被移到需要的位置且保留位被设置为1时,为什么值5(二进制0000 0101)也可以被写为95(二进制0101 1111)。

与FreeRTOS的关联

如上所述,中断服务程序的基本要求是确保使用的FreeRTOS的API的逻辑优先级小于等于configMAX_SYSCALL_INTERRUPT_PRIORITY表示的逻辑优先级(低逻辑优先级意味着数值更大)。CMSIS和各个微控制器厂商提供的库函数可以用来设置中断的优先级,有些库函数期望中断优先级设置在8bit宽度字节的最低有效位,也有些库函数期望中断优先级设置在8bit宽度字节的最高有效位,检查你使用的函数的文档来确认你使用的库函数属于哪一种,这可以使你免受不必要的错误。FreeRTOSConfig.h中的configMAX_SYSCALL_INTERRUPT_PRIORITY和configKERNEL_INTERRUPT_PRIORITY的设置值应该和ARM Cortex-M内核期望的保持一致:即中断优先级设置值在8bit宽度字节的最高有效位。这也是为什么在每一个官方提供的应用案例的FreeRTOSConfig.h头文件中,可以设置configKERNEL_INTERRUPT_PRIORITY的值为255(二进制1111 1111)来使中断处于最低逻辑优先级。这样设置值的原因有以下几个:FreeRTOS内核直接访问ARM Cortex-M的硬件资源(不经过任何第三方库),FreeRTOS内核实现了大多数库提供的功能,FreeRTOS使用的这种机制是ARM Cortex-M3推向市场的第一个库的所使用的机制。

临界区

CORTEX_M内核的硬件描述

FreeRTOS内核使用ARM Cortex-M内核的BASEPRI寄存器实现临界区,这意味着FreeRTOS内核仅能屏蔽子中断,因此FreeRTOS内核提供了一个灵活的中断嵌套模型。BASEPRI是一个屏蔽位,给BASEPRI设置一个值后将使所有的逻辑优先级小于等于设置值的中断被屏蔽,因此不可以将BASEPRI设置为0。另外,在中断中调用FreeRTOS函数通过使用BASEPRI寄存器来实现中断安全临界区是安全的,进入临界区时BASEPRI设置为configMAX_SYSCALL_INTERRUPT_PRIORITY定义的值,出临界区时BASEPRI设置为0。我们接收到的很多bug报告指出退出临界区的时候BASEPRI应该返回它的原始值,而不仅仅是设置为0,但是不管BASEPRI设置为什么值,Cortex-M NVIC永远不会接受一个逻辑优先级低于当前正在执行中断的中断,在编译器优化选项打开的前提下,将BASEPRI设置为0的代码执行速度要比存储BASEPRI的值快得多。

与FreeRTOS的关联

FreeRTOS内核通过将configMAX_SYSCALL_INTERRUPT_PRIORITY的值写进ARM Cortex-M BASEPRI寄存器来创建一个临界区,当中断优先级为0()的时候不能使用BASEPRI屏蔽,configMAX_SYSCALL_INTERRUPT_PRIORITY不可以设置为0。