首页 > 技术文章 > FreeRTOS — 调度锁,任务锁和中断锁

Liu-Jing 2017-07-27 12:34 原文

以下内容转载自安富莱电子:http://forum.armfly.com/forum.php

1、 调 度 锁

  调度锁就是 RTOS 提供的调度器开关函数,如果某个任务调用了调度锁开关函数,处于调度锁开和调度锁关之间的代码在执行期间是不会被高优先级的任务抢占的,即任务调度被禁止。这一点要跟临界段的作用区分开,调度锁只是禁止了任务调度,并没有关闭任何中断,中断还是正常执行的。而临界段进行了开关中断操作。

2、 中 断 锁

  中断锁就是 RTOS 提供的开关中断函数,FreeRTOS 没有专门的中断锁函数,使用 “中断服务程序临界段处理”函数就可以实现同样效果。

3 、 任 务 锁

  简单的说,为了防止当前任务的执行被其它高优先级的任务打断而提供的锁机制就是任务锁。FreeRTOS 也没有专门的任务锁函数,但是使用 FreeRTOS 现有的功能有两种实现方法:

 通过给调度器加锁实现

  利用 FreeRTOS 的调度锁功能给调度器加锁的话,将关闭任务切换功能,从而高优先级任务也就无法抢占低优先级任务的执行,同时高优先级任务也是无法向低优先级任务切换的。另外特别注意,调度锁只是禁止了调度器工作,并没有关闭任何中断。

 通过关闭任务切换中断 PendSV 和系统时钟节拍中断 Systick

  利用 FreeRTOS 的任务代码临界段处理函数就可以关闭 PendSV 中断和 Systick 中断。因为进入临界段前,操作寄存器 basepri 关闭了所有小于等于宏定义configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 所定义的中断优先级(实现任务切换功能的 PendSV 中断和滴答定时器中断是最低优先级中断,所以也是被关闭的),这样低优先级任务在执行临界段代码期间是不会被高优先级任务打断的,从而就实现了任务锁的效果。

4、FreeRTOS调度锁开启

  使用如下函数可以实现 FreeRTOS 的调度锁开启: vTaskSuspendAll()

关于这个函数的讲解及其使用方法可以看 FreeRTOS 在线版手册:

  

 

这里也对此函数进行下介绍。
函数原型:
void vTaskSuspendAll( void );
函数描述:
函数 vTaskSuspendAll 用于实现 FreeRTOS 调度锁开启。
使用这个函数要注意以下问题:
1. 调度锁函数只是禁止了任务调度,并没有关闭任何中断。
2. 调度锁开启函数 vTaskSuspendAll 和调度锁关闭函数 xTaskResumeAll 一定要成对使用。

3. 切不可在调度锁开启函数 vTaskSuspendAll 和调度锁关闭函数 xTaskResumeAll 之间调用任何会引
起任务切换的 API,比如 vTaskDelayUntil、vTaskDelay、xQueueSend 等
使用举例:

5、FreeRTOS 调 度 锁 关 闭

使用如下函数可以实现 FreeRTOS 的调度锁关闭:
 xTaskResumeAll ()
关于这个函数的讲解及其使用方法可以看 FreeRTOS 在线版手册

 

 

这里也对此函数进行下介绍。
函数原型:
BaseType_t xTaskResumeAll(void)
函数描述:
函数 xTaskResumeAll 用于实现 FreeRTOS 调度锁关闭
 调度锁关闭后,如果需要任务切换,此函数返回 pdTRUE,否则返回 pdFALSE。
使用这个函数要注意以下问题:
1. 调度锁函数只是禁止了任务调度,并没有关闭任何中断。
2. 调度锁开启函数 vTaskSuspendAll 和调度锁关闭函数 xTaskResumeAll 一定要成对使用。
3. 切不可在调度锁开启函数 vTaskSuspendAll 和调度锁关闭函数 xTaskResumeAll 之间调用任何会引
起任务切换的 API,比如 vTaskDelayUntil、vTaskDelay、xQueueSend 等。

使用举例:

FreeRTOSConfig.h 文件中的几个重要选项说明:

 #define configUSE_PREEMPTION 1
使能抢占式调度器
 #define configCPU_CLOCK_HZ ( ( unsigned long ) 168000000 )
系统主频 168MHz。
 #define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
系统时钟节拍 1KHz,即 1ms。
 #define configMAX_PRIORITIES ( 5 )
定义可供用户使用的最大优先级数,如果这个定义的是 5,那么用户可以使用的优先级号是 0,1,2,3,4,
不包含 5,对于这一点,初学者要特别的注意。
 #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 30 * 1024 ) )
定义堆大小,FreeRTOS 内核,用户动态内存申请,任务栈等都需要用这个空间。
 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 0x01
定义受 FreeRTOS 管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用
FreeRTOS 的 API 的最高优先级。为了进一步说明这个宏定义的作用,解释如下:
 使用 CM 内核的 MCU,官方强烈建议将 NVIC 的优先级分组配置为全抢占式优先级,全部配置
为抢占式优先级的好处就是方便管理。
 对于 STM32 来说,设置 NVIC 的优先级分组为 4 时,
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为
STM32 的优先级设置仅使用 CM 内核 8bit 中的高 4bit,即只能区分 2^4 = 16 种优先级。因此
当优先级分组设置为 4 的时候可供用户选择抢占式优先级为 0 到 15,共 16 个优先级,配置为 0
表示最高优先级,配置为 15 表示最低优先级,不存在子优先级。
 这里配置 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 为 0x01 表示用户可以在抢
占式优先级为 1 到 15 的中断里面调用 FreeRTOS 的 API 函数,抢占式优先级为 0 的中断里面是
不允许调用的。

创建任务函数:

 实验现象:LED闪,蜂鸣响,当按下K1时开启调度锁,执行串口打印,完成后关闭调度锁。

当调用函数xTaskResumeAll()时; 如果有上下文切换,函数已经进行了上下文切换。函数返回pdTRUE,程序跳过上面框中的if函数,

如果没有上下文切换函数返回pdFALSE,执行框中的if函数。taskYIELD();这个函数是强制上下文转换:

这也就证明了,如果调用xTaskResumeAll()没有进行上下文切换,我们将强制进行上下文切换,如果进行了上下文切换,这个if判断是多余的。官方解释:

另外需要注意,强制进行上下文切换,不代表本任务中xTaskResumeAll()后面的语句就不执行了,这里led灯的亮灭依旧会执行,只是调度器重新获得了调度权限,可以在多个任务间进行调度和切换。还有注意调度锁,不能锁中断(只是关闭了任务的调度)。

实验效果:

按下k1后等依然在闪,蜂鸣也在响。后面的语句依然在执行。

 

推荐阅读