临界段代码也叫临界区,是指那些必须完整运行、不能被打断的代码段,比如有的外设的初始化需要严格的时序,初始化过程中不能被打断。FreeRTOS在进入临界段代码的时候需要关闭中断,处理完临界段代码以后再打开中断。FreeRTOS系统本身就有很多的临界段,这些代码都加了临界段代码保护,写自己用户程序的时候有些地方也需要添加临界段代码保护。
FreeRTOS与临界段代码保护有关的函数有4个,在task.h中定义,分别是:taskENTER_CRITICAL()、taskEXIT_CRITICAL() 、taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR( x )。其中前2个是任务级的临界段代码保护,后2个是中断级的临界段代码保护,无论哪种情况临界段的代码都要尽量短小,下面分别来看。
1. 任务级临界段代码保护
taskENTER_CRITICAL()和taskEXIT_CRITICAL()是任务级的临界段代码保护,一个是进入临界段,一个是退出临界段,这2个函数是成对使用的,这函数的定义如下:
而portENTER_CRITICAL()和portEXIT_CRITICAL()也是宏定义,在portmacro.h中有定义,如下:
函数vPortEnterCritical()和vPortExitCritical()在文件port.c中,函数如下:
可以看出,进入函数vPortEnterCritical()以后首先调用函数portDISABLE_INTERRUPTS()来关闭中断,然后给变量uxCriticalNesting加1。uxCriticalNesting是一个全局变量,用来记录临界段嵌套次数。函数vPortExitCritical()是退出临界段调用,函数每次将uxCriticalNesting 减1,只有当uxCriticalNesting 减到0才会调用函数portENABLE_INTERRUPTS()来使能中断。这样保证了在有多个临界段代码的时候不会因为某一个临界段代码的退出而打乱其他临界段的保护,只有所有的临界段代码都退出以后才会使能中断。
2. 中断级临界段代码保护
函数taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR( x )为中断级临界段代码保护函数,用在中断服务程序中,而且这个中断的优先级一定要小于等于configMAX_SYSCALL_INTERRUPT_PRIORITY。这2个函数在task.h中有如下定义:
接下来看portSET_INTERRUPT_MASK_FROM_ISR()和portCLEAR_INTERRUPT_MASK_FROM_ISR( x ),这2个函数在文件portmacro.h中有如下定义:
函数usPortRaiseCpuIPL()和vPortSetCpuIPL( x )在文件port.c中,函数如下:
可以看出,进入函数usPortRaiseCpuIPL()实现的功能为首先保存当前CPU的优先级IPL[2:0] 到变量usOldIPL中,作为函数返回值用于vPortSetCpuIPL( x ) 的形参在退出临界段时恢复IPL[2:0] 。另一个usPortRaiseCpuIPL() 的操作就是将CPU的优先级IPL[2:0]赋值configMAX_SYSCALL_INTERRUPT_PRIORITY,使优先级小于等于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断被屏蔽。