目录
一、临界段
1.1临界段的作用
实际上就是一段代码(进入和退出之间操作),为了保护共享资源被暂时单独使用
对于不想被打断访问的资源有这些:
-
全局变量 (比如SysTick中断时增加tick值就是进入临界段操作的)
-
不可重入函数——不能被多个任务或线程同时调用的函数,或者当被同时调用时可能导致不确定或不正确的结果
-
-
外设(硬件资源)
-
对时许有精准要求的操作
-
用户不想被打断的代码
1.2临界段API
1.2.1关闭中断
#inlcude "FreeRTOS.h"
#include "task.h"
/*关闭中断 实际上保留了某些紧急中断 下面实现原理会提到*/
void taskDISABLE_INTERRUPTS(void)
注意事项: 使用此API时,不允许嵌套操作
特殊情况下才使用此API,因为此操作相当于把所有中断都关掉了
1.2.2恢复中断
#inlcude "FreeRTOS.h"
#include "task.h"
/*恢复中断*/
void taskENABLE_INTERRUPTS(void)
1.2.3任务中进入临界段
#inlcude "FreeRTOS.h"
#include "task.h"
/*进入临界段,支持嵌套,任务在运行态时调用,不被中断打断*/
void taskENTER_CRITICAL(void)
注意事项:
该函数需要和对应的退出函数成对使用;
确保临界段中的操作尽快执行
1.2.4任务中退出临界段
#inlcude "FreeRTOS.h"
#include "task.h"
/*退出临界段,支持嵌套,任务在运行态内调用,不被中断打断*/
void taskEXTI_CRITICAL(void)
这对API成对使用,其实很好操作,就是把要保护的代码,用这两个API上下包裹住
1.2.5中断中进入临界段
#inlcude "FreeRTOS.h"
#include "task.h"
/*中断中 进入临界段*/
UBaseType_t taskENTER_CRITICAL_FROM_ISR(void)
return: 返回中断屏蔽寄存器的值,退出时需要从传入该参数
1.2.6中断中退出临界段
#inlcude "FreeRTOS.h"
#include "task.h"
/*中断中 退出临界段*/
void taskEXTI_CRITICAL_FROM_ISR(UBaseType_t uxSavedInterruptStatus)
params: 传入taskENTER_CRITICAL_FROM_ISR()的返回值
1.5应用
实现如下功能:两个任务分别间隔50ms打印一次运行信息,通过临界段解决共享资源冲突问题
1.第一次尝试,两个任务分别间隔50ms打印,结果如下
可以看出:两个打印任务“打架”了,原因就是两个任务优先级相同,其中一个任务执行时,遇上另一个任务从阻塞恢复成就绪态时,就影响了上个任务
解决办法:任务中进入临界段来执行打印任务
注意:
2.第二次尝试
可以看出,两个任务不会再打架了
二、临界段实现原理
2.1FreeRTOS中断管理
HAL_Init--->HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4)
实际上就是图上分组3,无响应优先级,只有抢占优先级
FreeRTOS采用更灵活的BASEPRI指令,同时,中断优先级,值越小,优先级越大,而任务优先级, 值越大优先级越高
这里需要注意一下:basepri值默认为0,表示不关闭任何中断。而一旦设置成某个值,表示>=该值的中断都会被屏蔽
即:中断屏蔽门限值5,>=5的中断都被关 (这个是用来关中断的) 同时 内核调度中断优先级为最低的15
因此,
- 关中断:将中断屏蔽门限值(5)赋值给basepri (这一点其实就能看出来,FreeRTOS并没有将所有中断都关了,还是保留了一部分紧急的中断)
- 开中断:basepri赋值0
2.2任务中进入临界段
-
taskENTER_CRITICAL 是宏替换,原函数属于Cortex M4的硬件外设资源,Port说明移植到其他平台现需要自己实现这段代码
-
函数内部:
-
关中断,临界段嵌套值++(这个值在内核启动时初始化为0,后续如果嵌套就再自增)
-
2.3任务中退出临界段
-
函数内部:临界段嵌套值 — — (说明确实需要成对出现,不然是无法恢复中断的)
-
恢复中断,basepri置0
2.4中断中进入临界段
-
函数内部主要做了两件事:先把当前basepri值返回,再重新basepri赋值
2.5中断中退出临界段
-
把上次保存的basepri值赋值给basepri