7 FreeRTOS 临界段代码保护及调度器挂起与恢复

1 临界段代码保护简介

临界段:临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段。

适用场景:

1)外设,需严格按照时序初始化的外设:IIC、SPI等

2)系统,系统自身需求

3)自身,用户需求

问题:什么可以打断当前程序的运行?

中断、任务调度。由于任务调度也是由PendSV所管理的中断,并且PendSV的中断优先级最低,因此,关闭中断也可以关闭任务调度,但是FreeRTOS可以关闭的中断只能在优先级5~15的范围内。

2 临界段代码保护函数简介

FreeRTOS在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断

函数描述
taskENTER_CRITICAL()任务级进入临界段
taskEXTI_CRITICAL()任务级退出临界段
taskENTER_CRITICAL_FROM_ISR()中断级进入临界段
taskEXTI_CRITICAL_FROM_ISR()中断级退出临界段

任务级临界区调用格式:

taskENTER_CRITICAL();
{
      ...  ...  /*临界区*/

}
taskEXTI_CRITICAL();

中断级临界区调用格式:

unit32_t save_state;
save_state = taskENTER_CRITICAL_FROM_ISR();
{
    ... ...  /*临界区*/
}
taskEXTI_CRITICAL_FROM_ISR(save_state);

临界段代码特点:

1)成对使用

2)支持嵌套

3)尽量保持临界段耗时短

4)强悍。临界区是直接屏蔽了中断,系统任务调度靠中断,ISR也靠中断。

  • 任务级进入临界段

进入 taskENTER_CRITICAL()函数可以看到,其内部也是使用之前定时器实验中的关闭中断portDISABLE_INTERRUPTS()函数。但是多了一句uxCriticalNesting++; ,其作用是可以用来进行中断嵌套。

  • 任务级退出临界段

只有当uxCriticalNesting=0后才能退出临界段,即打开中断。

  • 中断级进入临界段

 ulPortRaiseBASEPRI()会返回一个无符号的32位的值。其内部首先会把中断屏蔽寄存器basepri读取出来ulReturn,然后把中断给关闭掉。

  • 中断级退出临界段

退出临界段有一个入口参数,其实就是带入返回值,然后将返回值设置到中断屏蔽寄存器basepri,这样就可以保持进入临界区,以及退出临界区的时候中断状态是一样的。

3 任务调度器的挂起与恢复

挂起任务调度器,调用此函数不需要关闭中断。

函数描述
vTaskSuspendAll()挂起任务调度器
vTaskResumeAll()恢复任务调度器

使用格式示例:

vTaskSuspendAll();
{
    /* 内容。不允许被其他任务打断,但是可以被中断打断,防止任务与任务之间的资源抢夺*/
}
xTaskResumeAll();

1)与临界区不一样的是,挂起任务调度器未关闭中断(最大区别);

2)它仅仅是为了防止任务之间的资源抢夺,中断照样可以直接响应;

3)挂起调度器的方式,适用于临界区位于任务与任务之间;既不用去延时中断,又可以做到临界区的安全

  • 挂起任务调度器
void vTaskSuspendAll( void )
{
    /* A critical section is not required as the variable is of type
     * BaseType_t.  Please read Richard Barry's reply in the following link to a
     * post in the FreeRTOS support forum before reporting this as a bug! -
     * https://goo.gl/wu4acr */

    /* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that
     * do not otherwise exhibit real time behaviour. */
    portSOFTWARE_BARRIER();//只是提供了一个接口而已

    /* The scheduler is suspended if uxSchedulerSuspended is non-zero.  An increment
     * is used to allow calls to vTaskSuspendAll() to nest. */
    ++uxSchedulerSuspended;
//其实是通过该句程序,进行任务调度器的挂起.任务调度器本质上执行的是任务切换,而任务切换是通过PendSV中断,uxSchedulerSuspended默认初始值是pdFalse
    /* Enforces ordering for ports and optimised compilers that may otherwise place
     * the above increment elsewhere. */
    portMEMORY_BARRIER();
}

4 总结

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值