跟着野火从零开始手搓FreeRTOS(4)临界段的概念及应用

本文详细解释了在FreeRTOS中如何使用CPS指令管理和控制中断,以保护临界段,包括关中断、开中断函数,以及进入和退出临界段的宏及其嵌套处理。特别强调了中断保护版本和非保护版本的区别。

临界段

        临界段就是一段在执行的时候不能被中断的代码段。在FreeRTOS最常用的情况是对全局变量的操作。

        基本上只有和系统调度,外部中断能打断临界段。在FreeRTOS中,系统调度也就是 PendSV 中断,所以对临界段的保护还是在于对中断的开和关的控制。中断被关闭之后,临界段就无法响应中断,从而做到保护临界段。

        Cortex-M 内核专门设置了一条 CPS 指令来实现快速开关中断,PRIMASK 和 FAULTMAST 是 Cortex-M 内核 里面三个中断屏蔽寄存器中的两个,还有一个是 BASEPRI。用法如下。

CPSID I ; PRIMASK=1;    关中断
CPSIE I ; PRIMASK=0;    开中断
CPSID F ; FAULTMASK=1;  关异常
CPSIE F ; FAULTMASK=0;  开异常

         通过对这三个寄存器的操作,使内核能够实现中断的开关。

关中断

        关中断的函数在 portmacro.h 中定义, 分不带返回值和带返回值两种。

        下面是不带返回值的。

/* 不带返回值的关中断函数 */
#define portDISABLE_INTERRUPTS()				vPortRaiseBASEPRI()

static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		msr basepri, ulNewBASEPRI    //值为11,大于11的一律不能响应
		dsb
		isb
	}
}

        因为不带返回值,即返回值为空,因此不能嵌套。

        configMAX_SYSCALL_INTERRUPT_PRIORITY 是一个在

<think> 我们正在讨论FreeRTOS中的临界知识。根据用户提供的引用内容,我们可以总结如下: 1. 临界代码是指必须完整运行、不能被打断的代码,常见于外设初始化(如IIC、SPI)、系统需求或用户自定义需求。 2. FreeRTOS通过关闭中断来保护临界代码,处理完毕后重新打开中断。 3. FreeRTOS关闭中断时,实际上只关闭了优先级在某个阈值(如5~15)的中断,而更高优先级的中断(0~4)仍然可以响应,以保证系统的实时性。 4. FreeRTOS提供了临界保护函数,这些函数可以在任务或中断中使用(部分函数有使用限制),并且支持嵌套。 接下来,我们将详细介绍FreeRTOS临界的相关知识。 ### 一、临界代码保护概述 临界代码(Critical Section)是指一必须完整执行、不可被中断的代码。在FreeRTOS中,临界保护通过暂时关闭中断来实现,以防止任务调度或中断服务程序打断临界代码的执行[^1][^2]。 ### 二、FreeRTOS临界保护机制 #### 1. 中断屏蔽机制 FreeRTOS使用ARM Cortex-M处理器的BASEPRI寄存器来实现部分中断屏蔽: - **BASEPRI寄存器**:屏蔽优先级低于某个阈值的中断。 - **配置阈值**:`configMAX_SYSCALL_INTERRUPT_PRIORITY`(通常为5)表示优先级≥5的中断会被屏蔽,而优先级<5(即数值更小,优先级更高)的中断不会被屏蔽[^3][^4]。 #### 2. 临界保护函数 FreeRTOS提供了两套临界保护函数: - **任务级临界保护**:在任务中使用,可嵌套。 - **中断级临界保护**:在中断服务程序(ISR)中使用,可嵌套。 ##### 任务级临界保护函数 ```c // 进入临界(关闭中断) taskENTER_CRITICAL(); // 临界代码 // 退出临界(开启中断) taskEXIT_CRITICAL(); ``` ##### 中断级临界保护函数 ```c // 进入临界(保存当前中断状态,关闭中断) UBaseType_t uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); // 临界代码 // 退出临界(恢复之前的中断状态) taskEXIT_CRITICAL_FROM_ISR(uxSavedInterruptStatus); ``` ### 三、临界保护实现原理 #### 1. 任务级临界保护实现 当调用`taskENTER_CRITICAL()`时,FreeRTOS会递增一个嵌套计数器,并在第一次进入临界时关闭中断: ```c void vPortEnterCritical(void) { portDISABLE_INTERRUPTS(); // 关闭中断(通过设置BASEPRI) uxCriticalNesting++; // 嵌套计数器递增 } ``` 退出临界时,嵌套计数器递减,当计数器归零时重新开启中断: ```c void vPortExitCritical(void) { uxCriticalNesting--; if (uxCriticalNesting == 0) { portENABLE_INTERRUPTS(); // 开启中断(设置BASEPRI为0) } } ``` #### 2. 中断级临界保护实现 在中断服务程序中,由于中断已经发生,FreeRTOS通过保存和恢复BASEPRI的值来实现嵌套: ```c // 进入临界 UBaseType_t taskENTER_CRITICAL_FROM_ISR(void) { UBaseType_t uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); return uxSavedInterruptStatus; } // 退出临界 void taskEXIT_CRITICAL_FROM_ISR(UBaseType_t uxSavedInterruptStatus) { portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); } ``` ### 四、使用注意事项 1. **临界长度**:临界代码应尽可能短,否则会影响系统实时性。 2. **避免阻塞操作**:在临界内不能调用任何可能导致任务阻塞的函数(如`vTaskDelay()`)。 3. **中断服务程序**:在中断服务程序中使用临界保护时,必须使用中断级函数(带`FROM_ISR`后缀)[^4]。 4. **优先级设置**:确保高优先级中断(如硬件故障、系统时钟)的优先级低于`configMAX_SYSCALL_INTERRUPT_PRIORITY`,以免被屏蔽[^2]。 ### 五、示例代码 #### 任务中使用临界保护 ```c void vTaskFunction(void *pvParameters) { for (;;) { taskENTER_CRITICAL(); // 进入临界 // 访问共享资源(如全局变量) xGlobalVariable++; taskEXIT_CRITICAL(); // 退出临界 vTaskDelay(100); // 延时(不能在临界内调用) } } ``` #### 中断服务程序中使用临界保护 ```c void vInterruptHandler(void) { UBaseType_t uxSavedStatus = taskENTER_CRITICAL_FROM_ISR(); // 处理共享资源 xQueueSendToBackFromISR(xQueue, &data, NULL); taskEXIT_CRITICAL_FROM_ISR(uxSavedStatus); } ``` ### 六、与调度器挂起的关系 FreeRTOS还提供了调度器挂起功能(`vTaskSuspendAll()`),它与临界保护的区别在于: - **临界保护**:关闭中断(部分中断),防止任务和中断打断代码。 - **调度器挂起**:暂停任务调度,但中断仍可执行(中断中触发的任务切换会延迟到调度器恢复后)[^1]。 > 调度器挂起适用于较长的操作(如批量更新多个共享资源),且可嵌套调用[^1]。 §§相关问题§§ 1. FreeRTOS临界保护与调度器挂起有何异同? 2. 如何配置`configMAX_SYSCALL_INTERRUPT_PRIORITY`以保证系统实时性? 3. 在FreeRTOS中,临界保护能否防止多核竞争? 4. FreeRTOS任务级临界保护函数是否可以在中断中使用? [^1]: FreeRTOS-临界代码保护及调度器挂起与恢复 [^2]: 7 FreeRTOS 临界代码保护及调度器挂起与恢复 [^3]: FreeRTOS——临界 [^4]: FreeRTOS-临界 </think>### FreeRTOS临界详解 #### 一、临界基本概念 临界Critical Section)是指必须**完整运行且不可被打断**的代码,常见于以下场景: 1. **外设操作**:需严格时序控制的IIC、SPI等外设初始化[^1] 2. **系统核心操作**:内核数据结构修改 3. **资源共享**:多任务/中断共享资源(全局变量、硬件寄存器等) #### 二、保护机制实现原理 FreeRTOS通过**中断屏蔽**实现临界保护: ```c // ARM Cortex-M内核实现 (portmacro.h) #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() static portFORCE_INLINE void vPortRaiseBASEPRI(void) { uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; __asm { msr basepri, ulNewBASEPRI // 设置中断屏蔽阈值 dsb isb } } ``` 1. **BASEPRI寄存器**:设置中断屏蔽优先级阈值 - 优先级 ≥ `configMAX_SYSCALL_INTERRUPT_PRIORITY`(如5)的中断被屏蔽 - 优先级 < 5(数值更小,优先级更高)的中断仍可响应[^3][^4] 2. **部分中断保留**:确保硬件故障等紧急中断能及时响应 #### 三、关键API函数 ##### 1. 任务级临界保护(可嵌套) ```c // 进入临界(关闭可屏蔽中断) taskENTER_CRITICAL(); // 临界代码(如修改共享资源) xSharedVariable++; // 退出临界(恢复中断) taskEXIT_CRITICAL(); ``` 特点: - 支持嵌套调用(内部维护嵌套计数器) - 不能在中断服务程序(ISR)中使用[^4] ##### 2. 中断级临界保护(ISR专用) ```c // 在中断服务程序中使用 UBaseType_t uxSavedStatus = taskENTER_CRITICAL_FROM_ISR(); // 临界代码 xQueueSendFromISR(...); // 恢复中断状态 taskEXIT_CRITICAL_FROM_ISR(uxSavedStatus); ``` #### 四、与调度器的关系 1. **任务调度本质**:通过PendSV中断(优先级最低)实现 2. **关中断的副作用**: - 关闭中断 ≥5时,会同时禁用任务调度[^2] - 但高优先级中断(如SysTick)仍可运行 3. **临界与挂起调度器**: ```mermaid graph LR A[临界保护] -->|关闭部分中断| B[保证代码原子性] C[挂起调度器] -->|禁止任务切换| D[保证任务级代码连续执行] ``` #### 五、最佳实践 1. **保持临界简短**:理想执行时间 < 10μs 2. **避免阻塞操作**:禁止在临界调用`vTaskDelay()`等函数 3. **优先级配置原则**: ```c // FreeRTOSConfig.h 典型配置 #define configMAX_SYSCALL_INTERRUPT_PRIORITY 5 // 可屏蔽中断阈值 #define configKERNEL_INTERRUPT_PRIORITY 255 // 内核中断最低优先级 ``` 4. **嵌套处理**: ```c taskENTER_CRITICAL(); // 嵌套层1 taskENTER_CRITICAL(); // 嵌套层2 /* 临界代码 */ taskEXIT_CRITICAL(); // 层2退出(中断仍关闭) taskEXIT_CRITICAL(); // 层1退出(恢复中断) ``` #### 六、典型应用场景 1. **外设寄存器原子操作** ```c taskENTER_CRITICAL(); GPIOA->ODR |= 0x01; // 原子性修改GPIO状态 taskEXIT_CRITICAL(); ``` 2. **链表数据结构操作** ```c taskENTER_CRITICAL(); listINSERT_END(&xList, &xItem); // 安全插入链表节点 taskEXIT_CRITICAL(); ``` 3. **全局计数器自增** ```c taskENTER_CRITICAL_FROM_ISR(&uxSavedStatus); ulInterruptCount++; // ISR中的共享计数器 taskEXIT_CRITICAL_FROM_ISR(uxSavedStatus); ``` > 临界保护是FreeRTOS实时性的关键机制,需平衡安全性与中断延迟[^1][^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pQAQqa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值