1、中断优先级位数
我们知道,配置STM32F10x的优先级时,范围是015,因为STM32F10x只用到了Cortex-M3内核用于配置寄存器的8位中的其中4位(MSB,高4位)。然而,freertos的这两个中断宏是直接配置Cortex-M3内核中的相应寄存器的,**因此,要喂给这两个中断宏的值的范围应该从4位的00001111转化为8位的0000xxxx~1111xxxx**。以configKERNEL_INTERRUPT_PRIORITY为例,具体执行该配置的位置在调度器启用函数xPortStartScheduler()当中,如下图所示。可以看到,configKERNEL_INTERRUPT_PRIORITY直接用于配置寄存器SCB_SHPR3。
注:此图来自野火的《FreeRTOS 内核实现与应用开发实战—基于STM32》
关于图中#define portNVIC_SYSPRI2_REG (*(volatile uint32_t *) 0xe000ed20);这个句子的语法,以后也可以写篇帖子讲讲,关于怎么用指针操作寄存器。
2、configKERNEL_INTERRUPT_PRIORITY
configKERNEL_INTERRUPT_PRIORITY(以下简称KERNEL)用于设置 FreeRTOS 内核本身使用的中断优先级。因为FreeRTOS内核中断不应当抢占用户使用的中断,所以KERNEL这个宏需要设置为最低优先级(1111xxxx) 。
从上边那个图可知,实际上KERNEL这个宏是用于配置内核中断PendSV和Systick的。PendSV和Systick其实是Cortex-M3内核的两个系统异常,在学习STM32比较粗浅的阶段,更习惯将其叫作中断。异常是Cortex-M3(ARMv7-M处理器架构)非常核心的概念,以后再开新的帖子单独讲讲。
在freertos中,PendSV用于上下文切换,为了使用户使用的中断能够得到快速响应,PendSV需要设置为最低优先级(1111xxxx)。而Systick在freertos中的功能之一是用于同优先级任务之间的时间片轮循,优先级也不应该设置得太过高,一般应该设置为不大于configMAX_SYSCALL_INTERRUPT_PRIORITY,也就是下边将要讲到的第二个中断宏。
正是因为KERNEL这个宏用于配置PendSV,所以需要设置为最低优先级。又因为在freertos中,也将KERNEL这个宏同时用于配置Systick,所以Systick也被配置为了最低优先级。
上边讲到的关于Systick在freertos中的优先级配置也是很多帖子没讲清楚的东西:理论上,Systick只是需要被设置为低一些,不是非得和PendSV一样设置为最低优先级。只是freertos将其和PendSV绑定了,用同一个宏去配置,所以也才被配置为了最低优先级。
具体配置方法可以参考野火的做法:
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 // stm32中的中断配置
#define configKERNEL_INTERRUPT_PRIORITY
( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) //转化成Cortex-M3内核寄存器的8位寄存器配置
3、configMAX_SYSCALL_INTERRUPT_PRIORITY
关于configMAX_SYSCALL_INTERRUPT_PRIORITY这个宏(以下简称SYSCALL)的作用,网上很多帖子讲得很乱。实际上,SYSCALL这个宏有两个作用:
- 用于设置可以调用中断安全的API函数的最高中断优先级,任何优先级高于这个值的中断都不能调用FreeRTOS的API函数。这是因为如果高优先级中断调用了FreeRTOS的API函数,并且打断了内核的关键部分,可能会导致数据不一致或死锁。因此,必须限制可以调用中断安全的API函数的优先级。
所谓中断安全的API函数,是指可以被中断安全调用的函数。由于中断服务例程的执行环境与普通任务上下文不同,某些FreeRTOS API函数在中断上下文中调用时可能会导致问题,如数据竞争、优先级反转或其他未定义行为。为了确保在中断上下文中调用API函数的安全性,FreeRTOS提供了一组专门设计的中断安全API函数。这些函数通常以FromISR
结尾,例如:xQueueSendFromISR()
、xQueueReceiveFromISR()和``xSemaphoreGiveFromISR()等。
- 设置临界区的中断屏蔽级别。 在FreeRTOS中,临界区是指在执行期间不会被中断打断的一个代码段,进入临界区通常通过禁用中断来实现。在ARM Cortex-M系列处理器中,临界区是通过设置SYSCALL这个宏来实现的。FreeRTOS使用SYSCALL这个宏来设置一个阈值,低于这个阈值的中断会被屏蔽,从而保护临界区内的代码不被打断。
前边讲到,KERNEL这个宏本质上是在配置SCB_SHPR3这个寄存器。那么,配置KERNEL这个宏,本质上是在配置哪个寄存器呢?实际上,配置SYSCALL这个宏本质上是在配置BASEPRI这个寄存器。BASEPRI是Cortex-M3处理器用于管理中断的使能和除能的三个寄存器之一,这三个寄存器分别是BASEPRI、PRIMASK和FAULTMASK。BASEPRI用于设置一个基准优先级,低于这个优先级的中断将被屏蔽,而高于或等于这个优先级的中断则可以被处理。
综合SYSCALL这两点的作用,可得到结论:在SYSCALL这个宏的作用下,一个正在工作的 API 函数,只会被高优先级中断打断,并且这个高优先级中断不会调用任何能操作系统内核的 API 函数。这样,既能保证 API 函数得以在完全安全的环境下运行,也能允许高优先级中断正常工作。
具体配置方法可以参考野火的做法:
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configMAX_SYSCALL_INTERRUPT_PRIORITY
(configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))