目录
一、基本原理
临界区(临界段代码):必须完整运行,不能被打断的代码段。
临界区本质:屏蔽中断。
适用场合:
1、外设 需严格按照时序初始化的外设,如IIC、SPI等等
2、系统自身需求
3、用户需求
打断当前程序的运行:中断与任务调度
以下转载自官网:
原文链接:https://www.freertos.org/zh-cn-cmn-s/RTOS-Cortex-M3-M4.html
临界区
Cortex-M 硬件详情
RTOS 内核使用 ARM Cortex-M 核心的 BASEPRI 寄存器 来实现临界区。 因此,RTOS 内核能够仅屏蔽一部分中断, 从而提供灵活的中断嵌套模型。
BASEPRI 是一个位掩码。 为 BASEPRI 设置为一个值后,它可以屏蔽所有逻辑优先级等于 或低于该值的中断。 因此,无法使用 BASEPRI 来 屏蔽优先级为 0 的中断。
题外话: 可以从中断中安全调用的 FreeRTOS API 函数 使用 BASEPRI 来实现中断安全临界区。 进入临界区时, BASEPRI 的值设置为 configMAX_SYSCALL_interrupt_PRIORITY, 退出临界区时,设置为 0。 我们收到许多故障报告 认为 BASEPRI 应在退出临界区时返回到原始值,而不仅仅是设置为 0, 但是 Cortex-M NVIC 绝不会接受一个优先级低于当前正在执行的中断的优先级的中断, 不管 BASEPRI 设置为多少。 一个总是 将 BASEPRI 设置为 0 的实现执行代码的速度比一个 存储然后恢复 BASEPRI 值的实现更快 (编译器的优化器打开的情况下)。
使用 RTOS 内核时的相关性
RTOS 内核通过将 configMAX_SYSCALL_INTRUPT_PRIORITY 的值 写入 ARM Cortex-M BASEPRI 寄存器来创建临界区。 因为 BASEPRI 无法屏蔽 优先级为 0 的中断(最高的优先级), 所以不得将 configMAX_SYSCALL_INTERRUPT_PRIORITY 设置为 0。
二、函数
使用注意:
成对使用;
支持嵌套;
尽量保持临界段耗时短
1、任务级进出临界段
void taskENTER_CRITICAL( void ); void taskEXIT_CRITICAL( void );
通过调用 taskENTER_CRITICAL() 进入临界区,随后 通过调用 taskEXIT_CRITICAL() 退出临界区。
宏 taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 提供了一个基本 临界区实现,只需禁用中断即可使其全局运作, 或在特定的中断优先级范围内运作。
如果所使用的 FreeRTOS 移植未使用 configMAX_SYSCALL_INTERRUPT_PRIORITY 内核配置常量(也称为 configMAX_API_CALL_INTERRUPT_PRIORITY),则调用 taskENTER_CRITICAL() 将 全局禁用中断。 如果所使用的 FreeRTOS 移植 使用了 configMAX_SYSCALL_INTERRUPT_PRIORITY 内核配置常量, 则调用 taskENTER_CRITICAL() 会将中断保留在 由已禁用的 configMAX_SYSCALL_INTERRUPT_PRIORITY 设置的中断优先级一下, 并启用所有更高优先级的中断。
抢占式上下文切换仅在中断内发生, 在中断被禁用时不会发生。 因此,可保证 调用 taskENTER_CRITICAL() 的任务维持在运行状态,直到 退出临界区,除非任务明确试图阻塞或让出 (它不应在临界区的内部进行该操作)。
对 taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 的调用旨在嵌套。 因此,只有在执行了一次对 taskEXIT_CRITICAL() 的调用, 用于所有先前的 taskENTER_CRITICAL() 调用之后, 才会退出临界区。
临界区必须保持非常短,否则将影响 中断响应时间。 每次 taskENTER_CRITICAL() 调用都必须紧密配合 taskEXIT_CRITICAL() 调用。
不得从临界区调用 FreeRTOS API 函数。
taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 不得从中断服务程序 (ISR) 调用——请参阅 taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR(),获取中断安全等效项。
参数:
无 |
返回:
无
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS();
uxCriticalNesting++;//进入临界区支持嵌套的原因,调用一次加1
/* This is not the interrupt safe version of the enter critical function so
* assert() if it is being called from an interrupt context. Only API
* functions that end in "FromISR" can be used in an interrupt. Only assert if
* the critical nesting count is 1 to protect against recursive calls if the
* assert function also uses a critical section. */
if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
}
}
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );//断点,方便调试
uxCriticalNesting--;调用一次减1,直到0时退出临界区
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}
2、中断级进出临界段
UBaseType_t taskENTER_CRITICAL_FROM_ISR( void ); void taskEXIT_CRITICAL_FROM_ISR( UBaseType_t uxSavedInterruptStatus );
taskENTER_CRITICAL() and taskEXIT_CRITICAL() 版本 可用于中断服务程序 (ISR)。
在 ISR 中,通过调用 taskENTER_CRITICAL_FROM_ISR() 进入临界区, 然后通过调用 taskEXIT_CRITICAL_FROM_ISR() 退出。
taskENTER_CRITICAL_FROM_ISR() 宏和 taskEXIT_CRITICAL_FROM_ISR() 宏提供了 基本临界区的实现,只需禁用中断即可使其全局运作, 可以是全局禁用,也可以是禁用到特定的中断优先级。
如果使用的 FreeRTOS 移植支持中断嵌套,则调用 taskENTER_CRITICAL_FROM_ISR() 将在内核配置常量 configMAX_SYSCALL_INTERRUPT_PRIORITY 设置的中断优先级或以下级别禁用中断,并 启用所有其他中断优先级。 如果使用的 FreeRTOS 移植不支持中断嵌套,则 taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR() 将不起作用。
调用 taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR() 旨在用于嵌套,但宏的使用方式的语义不同于 taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 等效项。
临界区必须保持非常短,否则将影响 更高优先级的中断的响应时间,会导致该中断嵌套。 每次 taskENTER_CRITICAL_FROM_ISR() 调用都必须紧密配合 taskEXIT_CRITICAL_FROM_ISR() 调用一起使用。
不得从临界区调用 FreeRTOS API 函数。
参数:
uxSavedInterruptStatus | taskEXIT_CRITICAL_FROM_ISR() 将 uxSavedInterruptStatus 作为其 唯一参数。 作为 uxSavedInterruptStatus 参数使用的值 必须是从匹配的 taskENTER_CRITICAL_FROM_ISR() 调用返回的值。 taskENTER_CRITICAL_FROM_ISR() 不采用任何 参数。 |
返回:
taskENTER_CRITICAL_FROM_ISR() 返回调用宏之前的中断掩码状态 。 taskENTER_CRITICAL_FROM_ISR() 返回的值 必须作为 uxSavedInterruptStatus 参数用于匹配的 taskEXIT_CRITICAL_FROM_ISR() 调用。
taskEXIT_CRITICAL_FROM_ISR() 不返回值。