邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛
分类: 嵌入式
2015-09-24 11:43:12
首先需要清楚有效优先级的总数,这取决于微控制器制造商怎么使用Cortex内核。所以,并不是所有的Cortex-M内核微处理器都具有相同的中断优先级级别。 Cortex-M构架自身最多允许256级可编程优先级(优先级配置寄存器最多8位,所以优先级范围从0x00~0xFF),但是绝大多数微控制器制造商只是使用其中的一部分优先级。比如,TI Stellaris Cortex-M3和Cortex-M4微控制器使用优先级配置寄存器的3个位,能提供8级优先级。再比如,NXP LPC17xx Cortex-M3微控制器使用优先级配置寄存器的5个位,能提供32级优先级;而ST公司Stm32F103系列使用配置寄存器的4个位,最多有16个优先级(0-15)。
如果你的工程包含CMSIS库头文件(这里是指Cortex M3的CMSIS库头文件,也就是core_cm3.h,可以在ARM官网下载),则头文件中的宏__NVIC_PRIO_BITS定义使用多少优先级寄存器的位(默认是4位)。
应用到RTOS:
RTOS中断嵌套方案将有效的中断优先级分成两组:一组可以通过RTOS临界区屏蔽,另一组不受RTOS影响,永远都是使能的。
configMAX_SYSCALL_INTERRUPT_PRIORITY在FreeRTOSConfig.h中配置,定义两组中断优先级的边界。逻辑优先级高于此值的中断不受RTOS影响。
最优值取决于微控制器使用的优先级配置寄存器的位数。
Cortex-M 硬件详述
有必要先解释一下优先级值和逻辑优先级:在Cortex-M内核中,假如有8级优先级,我们说优先级值是0~7,但数值最大的优先级7却代表着最低的逻辑优先级。很多使用传统传统中断优先级架构的工程师会觉得这样比较绕,违反直觉。以下内容提到的优先级要仔细区分是优先级数值还是逻辑优先级。
接下来需要清楚的是,在Cortex-M内核中,一个中断的优先级数值越低,逻辑优先级却越高。比如,中断优先级为2的中断可以抢占中断优先级为5的中断,但反过来就不行。换句话说,中断优先级2比中断优先级5的优先级更高。
这是Cortex-M内核最容易让人犯错之处,因为大多数的非Cortex-M内核微控制器的中断优先级表述是与之相反的。
应用到 RTOS
以“FromISR”结尾的FreeRTOS函数是具有中断调用保护的(执行这些函数会进入临界区),但是就算是这些函数,也不可以被逻辑优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断服务函数调用。(宏
configMAX_SYSCALL_INTERRUPT_PRIORITY定义在头文件FreeRTOSConfig.h中)。
因此,任何使用RTOS
API函数的中断服务例程的Cortex-M架构的芯片的中断优先级的数值应该:大于等于configMAX_SYSCALL_INTERRUPT_PRIORITY宏的值。这样就能保证中断的逻辑优先级等于或低于configMAX_SYSCALL_INTERRUPT_PRIORITY。
Cortex中断默认情况下有一个数值为0的优先级。大多数情况下0代表最高级优先级。因此,绝对不可以在优先级为0的中断服务例程中调用RTOS API函数。
Cortex-M 硬件详述
Cortex-M内核的中断优先级寄存器是以最高位(MSB)对齐的。比如,如果使用了3位来表达优先级,则这3个位位于中断优先级寄存器的bit5、bit6、bit7位。剩余的bit0~bit4可以设置成任何值,但为了兼容,最好将他们设置成1.
Cortex-M优先级寄存器最多有8位,如果一个微控制器只使用了其中的3位,那么这3位是以最高位对齐的,见下图:
|
|
|
结论:
在中断服务例程中调用RTOS API函数的中断逻辑优先级必须低于或等于configMAX_SYSCALL_INTERRUPT_PRIORITY(低逻辑优先级意味着高优先级数值)。
注意:
CMSIS以及不同的微控制器供应商提供了可以设置某个中断优先级的库函数。一些库函数的参数使用最低位对齐,另一些库函数的参数可能使用最高位对齐,所以,使用时应该查阅库函数的应用手册进行正确设置。
可以在FreeRTOSConfig.h中设置宏configMAX_SYSCALL_INTERRUPT_PRIORITY和
configKERNEL_INTERRUPT_PRIORITY的值。这两个宏需要根据Cortex-M内核自身的情况进行设置,要以最高有效位对齐。比如某微控制器使用中断优先级寄存器中的3位,设置configKERNEL_INTERRUPT_PRIORITY的值为5,
则代码为:
#define configKERNEL_INTERRUPT_PRIORITY (5<<(8-3))
对于每一个官方FreeRTOS演示例程,这也是在FreeRTOSConfig.h中要设置宏 configKERNEL_INTERRUPT_PRIORITY为最低优先级时,为什么要将它设置为255(1111 1111B)的原因。使用这种方式指定这个值的原因是:FreeRTOS内核是直接在Cortex-M内核硬件上运行的(没有使用第三方接口库函数),要比大多数库函数先运行。
Cortex-M 硬件详述
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 kernel
RTOS内核通过写configMAX_SYSCALL_INTERRUPT_PRIORITY的值到BASEPRI寄存器的方法创建临界区。中断优先级0(具有最高的逻辑优先级)不能被BASEPRI寄存器屏蔽,因此,configMAX_SYSCALL_INTERRUPT_PRIORITY绝不可以设置成0。
点击(此处)折叠或打开
点击(此处)折叠或打开
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置NVIC中断分组2, 2位抢占优先级,2为子优先级 |
#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_InitTypeDef NVIC_InitStructure; // 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); // 根据指定的参数初始化NVIC寄存器 |
对于Sysick使用core_cm3.h的函数,如果写成如下形式:
NVIC_SetPriority(SysTick_IRQn, 4);
|
#ifdef __NVIC_PRIO_BITS #define configPRIO_BITS __NVIC_PRIO_BITS #else #define configPRIO_BITS 4 #endif |
|
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY = 2,作为可以调用FreeRTOS API函数的分界线。大于这个优先级的中断不能调用API函数,而小于等于的中断可以调用API函数。
#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) // 定义表示系统时钟中断的优先级为15,为最低硬件中断优先级。
NVIC_InitTypeDef NVIC_InitStructure; // Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; 中断优先级配置: NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; // 抢占优先级为3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 子优先级为 3 上面两句修改为: NVIC_SetPriority(TIM3_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1); // 说明: 这表示优先级数值大于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY,逻辑优先级反而小,所以能够调用FreeRTOS API函数。 // 只有抢占优先级和子优先级这里不用关心,只需要知道中断之间的优先顺序就OK了。 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能 NVIC_Init(&NVIC_InitStructure); // 根据指定的参数初始化NVIC寄存器 |