FreeRTOS临界段的保护

什么是临界段

      临界段用一句话概括就是一段在执行的时候不能被中断的代码段。在 FreeRTOS 里面, 这个临界段最常出现的就是对全局变量的操作,全局变量就好像是一个枪把子,谁都可以 对他开枪,但是我开枪的时候,你就不能开枪,否则就不知道是谁命中了靶子。可能有人 会说我可以在子弹上面做个标记,我说你能不能不要瞎扯淡。

      那么什么情况下临界段会被打断?一个是系统调度,还有一个就是外部中断。在 FreeRTOS,系统调度,最终也是产生 PendSV 中断,在 PendSV Handler 里面实现任务的切 换,所以还是可以归结为中断。既然这样,FreeRTOS 对临界段的保护最终还是回到对中断 的开和关的控制。

Cortex-M 内核快速关中断指令

为了快速地开关中断, Cortex-M 内核专门设置了一条 CPS 指令,有 4 种用法,具体见代码

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

       代码中PRIMASK 和 FAULTMAST 是 Cortex-M内核 里面三个中断屏蔽寄存 器中的两个,还有一个是 BASEPRI,有关这三个寄存器的详细用法.

Cortex-M 内核中断屏蔽寄存器组描述

       但是,在 FreeRTOS 中,对中断的开和关是通过操作 BASEPRI 寄存器来实现的,即大 于等于 BASEPRI 的值的中断会被屏蔽,小于 BASEPRI 的值的中断则不会被屏蔽,不受 FreeRTOS 管理。用户可以设置 BASEPRI 的值来选择性的给一些非常紧急的中断留一条后路。

关中断

FreeRTOS 关中断的函数在 portmacro.h 中定义,分不带返回值和带返回值两种,具体实现

 /* 不带返回值的关中断函数,不能嵌套,不能在中断里面使用 */ 
 #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
 
 void vPortRaiseBASEPRI( void )
 {
 uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; 
 __asm
 {
 msr basepri, ulNewBASEPRI 
 dsb
 isb
 }
 }
 
 /* 带返回值的关中断函数,可以嵌套,可以在中断里面使用 */ 
 #define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
 ulPortRaiseBASEPRI( void )
 {
 uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; 
 __asm
 {
 mrs ulReturn, basepri 
 msr basepri, ulNewBASEPRI 
 dsb
 isb
 }
 return ulReturn;
 }

开中断

FreeRTOS 开中断的函数在 portmacro.h 中定义,

 /* 不带中断保护的开中断函数 */
 #define portENABLE_INTERRUPTS() vPortSetBASEPRI
 
 /* 带中断保护的开中断函数 */
 #define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x) 
 
 void vPortSetBASEPRI( uint32_t ulBASEPRI ) 
 {
 __asm
 {
 msr basepri, ulBASEPRI
 }
 }

进入/退出临界段的宏

进入和退出临界段的宏在 task.h 中定义

 #define taskENTER_CRITICAL() portENTER_CRITICAL()
 #define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
 #define taskEXIT_CRITICAL() portEXIT_CRITICAL()
 #define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )

      进入和退出临界段的宏分中断保护版本和非中断版本,但最终都是通过开/关中断来实 现。有关开/光中断的底层代码我们已经讲解,那么接下来的退出和进入临界段的代码配套 注释来理解即可。

临界段代码的应用

在 FreeRTOS 中,对临界段的保护出现在两种场合,一种是在中断场合一种是在非中 断场合,具体的应用见

 /* 在中断场合,临界段可以嵌套 */
 {
 uint32_t ulReturn;
 /* 进入临界段,临界段可以嵌套 */
 ulReturn = taskENTER_CRITICAL_FROM_ISR();
 
 /* 临界段代码 */
 
 /* 退出临界段 */
 taskEXIT_CRITICAL_FROM_ISR( ulReturn );
 }
 /* 在非中断场合,临界段不能嵌套 */
 {
 /* 进入临界段 */
 taskENTER_CRITICAL();
 
 /* 临界段代码 */
 
 /* 退出临界段*/
 taskEXIT_CRITICAL();
 }

猜你喜欢

转载自blog.csdn.net/qq_61672347/article/details/125714377