RTOS实时操作系统
FreeRTOS移植
-
在工程文件夹中添加两个文件夹
FreeRTOS_CORE
和FreeRTOS_PORT
-
将FreeRTOS中Source中的list.c 、queue.c、tasks.c添加到
FreeRTOS_CORE
内核源码文件夹中 -
查看
FreeRTOS
中的Source
中的portable
移植文件夹中Keil
、MemMang
、RVDS
三个文件夹,然后在RVDS
文件夹中找到对应芯片型号的文件夹,例如stm32f4系列,则找到ARM_CM4F,以及在MemMang找到对应的文件,这里我是需要heap_4.c
该.c文件。把该步骤的文件都添加到FreeRTOS_PORT
工程目录文件夹中。 -
将上面的文件在工程中添加头文件路径
- FreeRTOS\include
- FreeRTOS\RVDS\ARM_CM4F
-
添加FreeRTOSConfig.h文件
FreeRTOSConfig.h是FreeRTOS操作系统的配置文件,FreeRTOS操作系统是可裁剪的,用户可以根据需求对FreeRTOS进行裁剪,裁剪掉不需要用到的FreeRTOS功能,以此来节约MCU中寸土寸金的内存资源。那么FreeRTOSConfig.h文件从哪里来呢?
从官方的Demo文件夹中找到对应芯片的例程中
- 然后修改FreeRTOSConfig.h头文件,进行配置和裁剪
/* Ensure stdint is only used by the compiler, and not the assembler. */
#include <stdio.h>
#include <stdint.h>
extern uint32_t SystemCoreClock;
#define configUSE_PREEMPTION 1 // 是否使用抢占式任务调用
#define configUSE_IDLE_HOOK 0 // 空闲任务回调函数是否开启
#define configUSE_TICK_HOOK 0 // 节拍任务的回调函数是否开启
#define configCPU_CLOCK_HZ ( SystemCoreClock ) // CPU主频参数 168MHZ
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) // OS 节拍频率 1ms/节拍
#define configMAX_PRIORITIES ( 32 ) // 最大的任务优先级 最大:32
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 130 ) // 空闲任务的栈大小 单位:字 word
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 75 * 1024 ) ) // 给FreeRTOS使用的堆大小 75KB
#define configMAX_TASK_NAME_LEN ( 32 )
#define configUSE_TRACE_FACILITY 1 // 可视化调试追踪
#define configUSE_16_BIT_TICKS 0 // 选32bit变量记录系统节拍数
#define configIDLE_SHOULD_YIELD 1 // 空闲任务的行为设置 1 0
#define configUSE_MUTEXES 1 // 使用互斥锁
#define configQUEUE_REGISTRY_SIZE 8 // 可以注册和定义的队列信号及信号量的最大数量
#define configCHECK_FOR_STACK_OVERFLOW 0 // 堆栈溢出检查 0:不做溢出检查 1 做溢出检查
#define configUSE_RECURSIVE_MUTEXES 1 // 支持互斥锁嵌套 0:不能嵌套互斥锁
#define configUSE_MALLOC_FAILED_HOOK 0 // 开启malloc失败的回调
#define configUSE_APPLICATION_TASK_TAG 0
#define configUSE_COUNTING_SEMAPHORES 1 // 开启使用信号量 0:关闭信号量使用
#define configGENERATE_RUN_TIME_STATS 0 // 不开启运行时间统计 1:还要开启另外2个宏
/* 软件定时器功能的定义 TIM1~TIM14--硬件定时器 */
#define configUSE_TIMERS 0 // 开启了
#define configTIMER_TASK_PRIORITY ( 2 )
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )
/* 将以下宏定义设置为1可开启对应的函数,或者设置为0,排除函数. */
#define INCLUDE_vTaskPrioritySet 1 // 可以使用任务优先级修改函数
#define INCLUDE_uxTaskPriorityGet 1 // 任务的优先级获取
#define INCLUDE_vTaskDelete 1 // 任务删除函数
#define INCLUDE_vTaskCleanUpResources 1 // 清除资源函数
#define INCLUDE_vTaskSuspend 1 // 任务挂起函数
#define INCLUDE_vTaskDelayUntil 1 // 任务的延时函数
#define INCLUDE_vTaskDelay 1 // 精准延时函数
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4 /* 15 priority levels */
#endif
/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
‘config’ 参数
configUSE_PREEMPTION
设置为 1,使用抢占式 RTOS 调度器;设置为 0,使用协同式 RTOS 调度器。
configUSE_PORT_OPTIMISED_TASK_SELECTION
一些 FreeRTOS 移植有两种选择下一个执行任务的方法,一种是通用方法, 另一种是移植特定的方法。
通用方法:
-
在 configUSE_PORT_OPTIMISED_TASK_SELECTION 设置为 0 时,或 没有实现移植特定的方法时使用。
-
可用于所有 FreeRTOS 移植。
-
完全使用 C 编写,比移植特定的方法低效 。
-
不限制可用优先级的最大数量。
移植特定的方法:
-
部分移植不可用。
-
当 configUSE_PORT_OPTIMISED_TASK_SELECTION 设置为 1 时使用。
-
依赖于一种或多种架构特定的汇编指令 (通常是前导零计数 [CLZ] 指令或同等指令) ,因此 仅适用于专为其编写该指令的架构。
-
比通用方法更高效。
-
通常限制可用优先级的最大数量为 32 。
configUSE_滴答LESS_IDLE
将 configUSE_滴答LESS_IDLE 设置为 1,使用 低功耗无 滴答 模式, 或设置为 0,保持 滴答 中断始终运行。 并非 所有 FreeRTOS 移植都实现了低功耗无 滴答 模式。
configUSE_IDLE_HOOK
将其设置为 1,使用空闲钩子, 或设置为 0,忽略空闲钩子。
configUSE_MALLOC_FAILED_HOOK
创建任务、队列或信号量时,内核调用 pvPortMalloc() 从堆中分配内存 。 官方 FreeRTOS 下载内容中 包括4 种内存分配方案样例。 这些方案 分别实现在 heap_1.c、heap_2.c、heap_3.c、heap_4.c 和 heap_5.c 源文件中 。 configUSE_MALLOC_FAILED_HOOK
仅在用到这3 种方案时适用。 。
malloc() 失败的钩子函数是一个钩子(或回调)函数,在 已经定义和配置的情况下,如果 pvPortMalloc()
返回 NULL ,它将被调用。 只有当 FreeRTOS 堆剩余的内存不足以分配请求的内存时,它才会返回 NULL 。
如果 configUSE_MALLOC_FAILED_HOOK
设置为 1,那么应用程序必须定义 malloc() 失败的钩子函数。 如果 configUSE_MALLOC_FAILED_HOOK
设置为 0,那么 malloc() 失败的钩子函数将不会被调用,即使定义了钩子函数。 malloc() 失败的钩子函数必须具有如下所示的名称和原型。
void vApplicationMallocFailedHook( void );
configUSE_DAEMON_TASK_STARTUP_HOOK
如果 configUSE_TIMERS
和 configUSE_DAEMON_TASK_STARTUP_HOOK
都设置为 1, 那么应用程序必须定义钩子函数,而且它必须和如下的函数具有相同的名称和原型 。 当 RTOS 守护进程任务(也称为 定时器服务任务) 首次执行时,钩子函数将会且只会被调用一次。 任何 需要 RTOS 运行的应用程序初始化代码,可放在钩子函数中。
void void vApplicationDaemonTaskStartupHook( void );
configUSE_SB_COMPLETED_CALLBACK
设置 configUSE_SB_COMPLETED_CALLBACK()
时需要设置构建中的 xStreamBufferCreateWithCallback()
和 xStreamBufferCreateStaticWithCallback()
(及其消息缓冲区等效函数)。 使用这些函数创建的流缓冲区和消息缓冲区可以有自己 唯一的发送完成回调和接收完成回调,其中通过 xStreamBufferCreate()
和 xStreamBufferCreateStatic ()
(及其消息缓冲区等效函数) 创建的流缓冲区和消息缓冲区都共享 sbSEND_COMPLETED()
和 sbRECEVE_COMPLETED()
宏定义的回调。configUSE_SB_COMPLETED_CALLBACK
默认为 0 ,以便向后兼容。
configUSE_滴答_HOOK
将其设置为 1,使用 滴答 钩子, 或设置为 0,忽略 滴答 钩子。
configCPU_CLOCK_HZ
输入驱动生成 滴答 中断的外设所用的内部时钟的执行频率(以赫兹为单位)。 内部时钟通常也是 驱动内部 CPU 的时钟。 此值是 正确配置定时器外围设备所必需的。
configSYS滴答_CLOCK_HZ
仅适用于 ARM Cortex-M 移植的可选参数。
ARM Cortex-M 移植默认从 Cortex-M Sys滴答 定时器生成 RTOS 滴答 中断。大多数 Cortex-M MCU 以与 MCU 自身相同的频率运行 SysTick 定时器,如果这样,则不需要 configSYSTICK_CLOCK_HZ,而且它应保持未定义状态。如果 SysTick 定时器的频率与 MCU 核心不同,那么应将 configCPU_CLOCK_HZ 设置为正常的 MCU 时钟频率,将 configSYSTICK_CLOCK_HZ 设置为 SysTick 时钟频率。
configTICK_RATE_HZ
RTOS 滴答 中断的频率。
滴答中断用于测定时间。 因此,滴答 频率越高,时间测定的分辨率越高 。 然而,滴答频率高也意味着 RTOS 内核将使用更多的 CPU 时间,因此效率会降低 。 RTOS 演示应用程序使用的 滴答 频率均为 1000 Hz。 此频率用于测试 RTOS 内核, 高于正常的频率要求。
多个任务可以共用相同的优先级。 RTOS 调度器将会 在每个 RTOS 滴答期间切换具有相同优先级的任务,让它们共享处理器时间。 因此,滴答频率高也会 减少分给每个任务的“时间切片”。
configMAX_PRIORITIES
应用程序任务可用的优先级数量 。 任意数量的任务可共用相同的优先级。 协程单独分配优先级,请参阅 configMAX_CO_ROUTINE_PRIORITIES。
每个可用的优先级都消耗了 RTOS 内核一些 RAM,因此不应将该值设置得高于应用程序实际需要的优先级数量 。
如果 configUSE_PORT_OPTIMISED_TASK_SELECTION 设置为 1,那么将限制最大允许值。
configMINIMAL_STACK_SIZE
空闲任务使用的堆栈大小。 通常,该值不应小于 FreeRTOSConfig.h 中设置的值, 该头文件位于您使用的移植的演示应用程序。
此堆栈大小与 xTaskCreate()
和 xTaskCreateStatic()
函数的堆栈大小参数类似, 是按字指定,而不是按字节指定。 如果堆栈上的每个元素为 32 位,那么 大小为 100 元素的堆栈包含 400 字节(每个堆栈元素 32 位,消耗 4 个字节)。
configMAX_TASK_NAME_LEN
创建任务时,任务描述性名称的最大允许长度。 该长度按字符数指定, 包括 NULL 终止字节。
configUSE_TRACE_FACILITY
如果想包含其他结构成员和函数来辅助执行可视化和跟踪,将其设置为 1。
configUSE_STATS_FORMATTING_FUNCTIONS
将 configUSE_TRACE_FACILITY 和 configUSE_STATS_FORMATTING_FUNCTIONS 设置为 1, 包含 vTaskList() 和 vTaskGetRunTimeStats() 函数中的功能。 设置任意一个为 0,会在构建时忽略 vTaskList() 和 vTaskGetRunTimeStates()。
configUSE_16_BIT_滴答S
时间以 ‘滴答s’ 为单位测定,这是 RTOS 内核启动后,滴答中断已经执行的次数。 滴答数存放在 滴答Type_t 类型的变量中。
将 configUSE_16_BIT_滴答S 定义为 1,会将 滴答Type_t 定义为 (typedef) 无符号的 16 位类型。 将 configUSE_16_BIT_滴答S 定义为 0,会将 滴答Type_t 定义为 (typedef) 无符号的 32 位类型。
在 8 位和 16 位架构上使用 16 位类型,将大大提高性能,但会限制最大可指定的 时间段为 65535 ‘滴答s’。 因此,假设滴答频率为 250 Hz,当使用 16 位计数器时,任务可以延迟或阻塞的最大时间为 262 秒,而 使用 32 位计数器时,则为 17179869 秒。
增加了 configTICK_TYPE_WIDTH_IN_BITS,以支持大于 32 位的滴答计数。新设计应使用 configTICK_TYPE_WIDTH_IN_BITS,而不是 configUSE_16_BIT_TICKS。
configTICK_TYPE_WIDTH_IN_BITS
时间以 ‘滴答s’ 为单位测定,这是 RTOS 内核启动后,滴答中断已经执行的次数。滴答数存放在 滴答Type_t 类型的变量中。
configTICK_TYPE_WIDTH_IN_BITS 控制 TickType_t 的类型(以及位宽):
- 将
configTICK_TYPE_WIDTH_IN_BITS
定义为TICK_TYPE_WIDTH_16_BITS
,会导致TickType_t
被定义(typedef’ed)为无符号 16 位类型。 - 将
configTICK_TYPE_WIDTH_IN_BITS
定义为TICK_TYPE_WIDTH_32_BITS
,会导致TickType_t
被定义(typedef’ed)为无符号 32 位类型。 - 将
configTICK_TYPE_WIDTH_IN_BITS
定义为TICK_TYPE_WIDTH_64_BITS
,会导致TickType_t
被定义(typedef’ed)为无符号 64 位类型。
使用 16 位类型可大大提高 8 位和 16 位架构的性能,但会将可指定的最大时间段限制为 65535 个“滴答”。同样,使用 32 位类型 可提高 32 位架构的性能,但会将可指定的最大周期限制为 4294967295 个“滴答”。
configIDLE_SHOULD_YIELD
此参数控制空闲优先级任务的行为。 它仅在以下情况下有效:
- 使用抢占式调度器。
- 应用程序创建运行在空闲优先级的任务。
如果 configUSE_TIME_SLICING 设置为 1(或未定义),那么 优先级相同的任务将使用时间切片。 如果没有任务被抢占,那么可以假设每个给定优先级的任务 将分配相同的处理时间——如果优先级高于 空闲优先级,那么情况确实如此。
如果任务均为空闲优先级,其行为可能略有不同。 如果 configIDLE_SHOULD_YIELD 设置为 1,那么 如果其他空闲优先级任务准备运行,空闲任务将立即挂起。 这确保 当有应用程序任务可调度时,空闲任务花费的时间最少。 然而,这种行为可能 产生如下所示的不良效应(取决于您的应用需求):
上图显示了四个空闲优先级任务的执行模式 。 任务 A、B 和 C 是应用程序任务。 任务 I 是空闲任务。 上下文在时间 T0、T1、……、T6 定期切换。 当空闲任务挂起,任务 A 开始执行,但当前时间片已经被空闲任务消耗了一部分 。 这导致实际上任务 I 和任务 A 共用同一时间 片。 因此,应用程序任务 B 和 C 获得的处理时间多于 应用任务 A。
这种情况可以通过以下方式避免:
- 如果适当,使用空闲钩子代替 单独的空闲优先级任务。
- 以高于空闲的优先级创建所有应用程序任务。
- 将 configIDLE_SHOULD_YIELD 设置为 0。
将 configIDLE_SHOULD_YIELD 设置为 0 会阻止空闲任务让出 处理时间,直到其时间片结束。 这可以确保所有空闲优先级任务 分配相同的处理时间(如果没有 任务被抢占),但这是以分配给空闲任务更高比例的处理时间为代价的 。
FreeRTOS 内核开发者文档
- 任务
- 队列、互斥锁、信号量……
- 直达任务通知
- 流缓冲区 & 消息缓冲区
- 软件定时器
- 事件组(或“标志” )
- 源代码组织
- FreeRTOSConfig.h
- 静态内存 Vs 动态内存
- 堆内存管理
- 堆栈溢出保护
- 创建一个新项目
- 协程(已弃用)
任务
可以仅使用任务,仅使用协程,或结合两者设计应用程序。然而,任务和协程使用不同的 API 函数,因此不能通过队列(或信号量)将数据从任务传递到协程,反之亦然。
协程实际上仅用于 RAM 限制较大,体积非常小的处理器。
“任务”的特点
简而言之 :使用 RTOS 的实时应用程序可以被构建为一组独立的任务。每个任务在自己的上下文中执行,不依赖于系统内的其他任务或 RTOS 调度器本身。在任何时间点,应用程序中只能执行一个任务,实时 RTOS 调度器负责决定所要执行的任务。因此, RTOS 调度器可以在应用程序执行时重复启停每个任务(将任务调入或调出)。由于任务不了解 RTOS 调度器活动,因此实时 RTOS 调度器负责确保任务调入时的处理器上下文(寄存器值、堆栈内容等)与任务调出时的处理器上下文完全相同。为实现这一点,每个任务都分配有自己的堆栈。当任务调出时,执行上下文被保存到该任务的堆栈中,以便以后再调入相同的任务时可以准确地恢复其执行上下文。
任务总结
😀 操作简单。
😀 没有使用限制。
😀 支持完全抢占式机制。
😀 完全按优先顺序排列。
😟 每个任务都保留自己的堆栈,从而提高 RAM 使用率。
😟 如果使用抢占式机制,则必须谨慎考虑重入问题。
任务状态
任务可以存在于以下状态中:
- 运行
当任务实际执行时,它被称为处于运行状态。 任务当前正在使用处理器。 如果运行 RTOS 的处理器只有一个内核, 那么在任何给定时间内都只能有一个任务处于运行状态。
- 准备就绪
准备就绪任务指那些能够执行(它们不处于阻塞或挂起状态), 但目前没有执行的任务, 因为同等或更高优先级的不同任务已经处于运行状态。
- 阻塞
如果任务当前正在等待时间或外部事件,则该任务被认为处于阻塞状态。 例如,如果一个任务调用vTaskDelay(),它将被阻塞(被置于阻塞状态), 直到延迟结束-一个时间事件。 任务也可以通过阻塞来等待队列、信号量、事件组、通知或信号量 事件。 处于阻塞状态的任务通常有一个"超时"期, 超时后任务将被超时,并被解除阻塞, 即使该任务所等待的事件没有发生。
“阻塞”状态下的任务不使用任何处理时间,不能 被选择进入运行状态。
- 挂起
与“阻塞”状态下的任务一样, “挂起”状态下的任务不能 被选择进入运行状态,但处于挂起状态的任务 没有超时。 相反,任务只有在分别通过 vTaskSuspend() 和 xTaskResume() API 调用明确命令时 才会进入或退出挂起状态。
任务优先级
每个任务均被分配了从 0 到 ( configMAX_PRIORITIES - 1 ) 的优先级,其中的 configMAX_PRIORITIES 在 FreeRTOSConfig.h 中定义。
如果正在使用的端口实现了端口优化的任务选择机制,该机制使用 “前导零计数”类指令(用于单个指令中的任务选择)且 在 FreeRTOSConfig.h 中将 configUSE_PORT_OPTIMISED_TASK_SELECTION 设置为 1,则 configMAX_PRIORITIES 不得大于 32。 在所有其他情况下,configMAX_PRIORITIES 可以采取任何 合理范围内的值,但出于 RAM 使用效率的原因,应保持在 实际需要的最小值。
低优先级数字表示低优先级任务。 空闲任务的优先级为零 (tskIDLE_PRIORITY)。
FreeRTOS 调度器可确保在就绪或运行状态的任务将 始终在同样处于就绪状态的较低优先级任务之前获得处理器 (CPU) 时间 。 换句话说,被置于运行状态的任务 始终是可运行的最高优先级任务。
任意数量的任务可共用相同的优先级。 如果 configUSE_TIME_SLICING 未经定义, 或者如果 configUSE_TIME_SLICING 设置为 1,则相同优先级的就绪状态任务 将使用时间切片轮询调度方案共享可用的处理时间 。
FreeRTOS 调度
(单核、AMP 和 SMP)
本页面简要介绍了 FreeRTOS
调度算法, 主要针对单核、非对称多核 (AMP
)、和对称多核 (SMP
) RTOS
配置。调度算法是决定 哪个 RTOS
任务应处于运行状态的软件程序。在任何给定时间, 每个处理器核心只能有一个任务处于运行状态。在 AMP
中, 每个处理器核心运行自身的 FreeRTOS
实例。在 SMP
中, 存在一个 FreeRTOS
实例,可以跨多核调度 RTOS
任务 。
默认 RTOS 调度策略(单核)
FreeRTOS 默认使用固定优先级的抢占式 调度策略,对同等优先级的任务执行时间片轮询调度:
-
“固定优先级” 是指调度器不会永久更改任务的优先级, 尽管它可能会因 优先级继承而暂时提高任务的优先级。
-
“抢占式调度” 是指调度器始终运行优先级最高且可运行的 RTOS 任务, 无论任务何时能够运行。例如, 如果中断服务程序 (ISR) 更改了优先级最高且可运行的任务, 调度器会停止当前正在运行的低优先级任务 并启动高优先级任务——即使这发生在同一个时间片内 。此时,据说高优先级任务 “抢占”了低优先级任务。
-
“轮询调度” 是指具有相同优先级的任务轮流进入运行状态。
-
“时间片” 是指调度器会在每个 tick 中断上在同等优先级任务之间进行切换, tick 中断之间的时间构成一个时间片。tick 中断是 RTOS 用来衡量时间的周期性中断。
使用优先排序的抢占式调度器,避免任务饥饿
总是运行优先级最高且可运行的任务的后果是, 永远不会进入“阻塞”或 “挂起”状态的高优先级任务会让所有任意执行时长的低优先级任务永久饥饿 。这就是为什么通常最好创建事件驱动型任务的原因之一 。例如,如果一个高优先级任务正在等待一个事件, 那么它就不应处于该事件的循环(轮询)中,因为如果处于轮询中,它会一直运行,永远不进入“阻塞”或“挂起”状态。 反之,该任务应进入“阻塞” 状态来等待事件。可以使用众多 FreeRTOS 任务间通信和同步原语之一将事件发送给任务。接收到 事件后, 优先级更高的任务会自动解除“阻塞”状态。高优先级任务处于“阻塞”状态时, 低优先级任务会运行。
配置 RTOS 调度策略
以下 FreeRTOSConfig.h 设置更改了默认调度 行为:
configUSE_PREEMPTION
如果 configUSE_PREEMPTION 设置为 0,则关闭“抢占”, 只有当运行状态的任务进入“阻塞”或“挂起”状态, 或运行状态任务调用 taskYIELD(), 或中断服务程序 (ISR) 手动请求上下文切换时,才会发生上下文切换。
configUSE_TIME_SLICING
如果 configUSE_TIME_SLICING 设置为 0,则表示时间切片已关闭, 因此调度器不会在每个 tick 中断上在同等优先级的任务之间切换 。
FreeRTOS AMP 调度策略
使用 FreeRTOS 的非对称多处理 (AMP) 是指多核设备的每个核心都单独运行自己的 FreeRTOS 实例。这些 核心并不都需要具有相同架构, 但如果 FreeRTOS 实例之间需要进行通信,则需要共享一些内存。
每个核心都会运行自己的 FreeRTOS 实例, 因此任何给定核心上的调度算法与上文的单核系统调度算法完全相同 。您可以使用流缓冲区或消息缓冲区作为核间通信原语, 这样一来,一个核心上的任务可以进入“阻塞”状态, 以等待另一个核心发来的数据或事件。
FreeRTOS SMP 调度策略
使用 FreeRTOS 的对称多处理 (SMP) 是指 一个 FreeRTOS 实例可以跨多个处理器核心调度 RTOS 任务。由于 只有一个 FreeRTOS 实例在运行,一次只能使用 FreeRTOS 的一个端口, 因此每个核心必须具有相同的处理器架构并共用 相同的内存空间。
FreeRTOS SMP 调度策略使用与单核调度策略相同的算法, 但与单核和 AMP 场景不同的是, SMP 在任何给定时间都会导致多个任务处于运行状态 (每个核心上都有一个运行状态的任务)。这意味着, 只有缺乏可运行的高优先级任务时,才会运行低优先级任务的假设不再成立 。要想了解其中的原因,请考虑一下, 若起初只有一个高优先级任务 和两个中等优先级任务处于 “就绪”状态,SMP 调度器会如何选择在双核微控制器上运行的任务。调度器需要选择两个任务,每个核心对应一个任务。 首先,高优先级任务是指可运行的最高优先级任务, 因此会选择将它用于第一个核心。 这样就剩下了两个中等优先级的任务 作为可运行的最高优先级任务,因此会将它们用于第二个核心 。结果是高优先级和中等优先级的任务同时运行。
配置 SMP RTOS 调度策略
以下配置选项有助于移动下述代码:将为单核或 AMP RTOS 配置编写的代码移动到 SMP RTOS 配置中, 且这些代码依赖于该假设——如果有一个可运行的高优先级任务,那么低优先级任务不会运行。
configRUN_MULTIPLE_PRIORITIES
如果在下述文件中将 configRUN_MULTIPLE_PRIORITIES 设置为 0: FreeRTOSConfig.h,则调度器只会同时运行 具有相同优先级的多个任务。这可能会修复基于“一次只运行一个任务”这一假设编写的代码, 但这就无法享受到 SMP 配置带来的一些好处。
configUSE_CORE_AFFINITY
如果在 FreeRTOSConfig.h 中将 configUSE_CORE_AFFINITY 设置为 1, 则 vTaskCoreAffinitySet() API 函数可用于定义某个任务可以在哪些核心上运行 以及不可以在哪些核心上运行。使用该方法,应用程序编写者可以防止 同时执行假设了自身执行顺序的两个任务 。