FreeRTOS 之三 全配置项(FreeRTOSConfig.h)详解、裁剪、使用示例
首先,我们需要明确一个问题,FreeRTOSConfig.h 是一个用户级别的配置文件,不属于内核文件。每个用户可以有不同的FreeRTOSConfig.h,从而实现不同的功能配置。
FreeRTOS 作为一个可高度配置的实时内核,其绝大多数配置选项都体现在 FreeRTOS.h(注意是 FreeRTOS.h
不是FreeRTOSConfig.h
)中。为什么这么说?打开 FreeRTOS.h
看看就知道了,这个文件唯一要干的活就是负责根据宏值来对 FreeRTOS 进行配置的。
配置实现
如果用户想要根据自己的需要来配置 FreeRTOS,那该怎么办呢?难道需要直接改内核头文件 FreeRTOS.h
?当然不是。FreeRTOS 的设计者采用了一个很灵活的方式:FreeRTOS.h
通过检查一个名为 FreeRTOSConfig.h
的用户级别的配置文件来实现对 FreeRTOS 的配置。这样,既实现了灵活配置,又保证了所有用户就只有 FreeRTOSConfig.h
不同,而不需要修改内核源码。
那么用户具体如何配置呢?简单,用户只需要根据需要,在 FreeRTOSConfig.h
中以宏值的形式给出定义即可。用户可用的宏值几乎全部来自于 FreeRTOS.h
这个系统级头文件,用户需要什么功能,需要在该文件中查找对应的宏值;FreeRTOS.h
中会检查特定功能的宏值有没有在 FreeRTOSConfig.h
中定义,如果用户定义了指定宏值,则 FreeRTOS 就根据用户的定义来实现。
配置详解
以下解释主要是依据官方原文地址 https://www.freertos.org/a00110.html 的翻译,但是其中增加了很多个人的理解以及示例,如果有任何疑问可以直接到官方看英文说明。
configUSE_PREEMPTION
为 1 时 RTOS 使用抢占式调度器,即当进程位于内核空间时,有一个更高优先级的任务出现时,如果当前内核允许抢占,则可以将当前任务挂起,执行优先级更高的进程;为 0 时 RTOS 使用协作式调度器(时间片)高优先级的进程不能中止正在内核中运行的低优先级的进程而抢占 CPU 运行。
configUSE_PORT_OPTIMISED_TASK_SELECTION
某些运行 FreeRTOS 的硬件有两种方法选择下一个要执行的任务:通用方法和特定于硬件的方法。官方使用了 FreeRTOS port 这个词,源码的 port.c 即与特定硬件相关的实现,因此下面翻译为特定于硬件
-
通用方法:
configUSE_PORT_OPTIMISED_TASK_SELECTION
设置为 0 或者没有实现特定于硬件方法时,将显式或者隐式使用通用方法。- 可以用于所有 FreeRTOS 支持的硬件。
- 完全用 C 实现,效率略低于特殊方法。
- 不强制要求限制最大可用优先级数量。
-
特定于硬件方法:
- 并非所有硬件都支持
- 必须将
configUSE_PORT_OPTIMISED_TASK_SELECTION
设置为 1 。 - 依赖于一个或多个架构特定汇编指令(通常为计数前导零(CLZ)或等效指令),因此只能与其专门编写的体系结构一起使用。
- 比通用方法更有效率。
- 一般强制限定最大可用优先级数目为 32
- 通常是在
portmacro.h
中以宏的形式实现一些接口
configUSE_TICKLESS_IDLE
设置 configUSE_TICKLESS_IDLE
为 1,使能低功耗 tickless 模式;为 0 保持系统节拍(SysTick)中断一直运行。关于低功耗模式,可参见此文 https://blog.csdn.net/zcshoucsdn/article/details/77879746
并非所有 FreeRTOS 移植(
port.c
中)都实现了低功耗 tickless 模式
通常情况下,需要设计者自己实现 FreeRTOS 回调空闲任务钩子函数,在空闲任务钩子函数中设置微处理器进入低功耗模式来达到省电的目的。因为系统要响应系统节拍中断事件,因此使用这种方法会周期性的退出、再进入低功耗状态。如果系统节拍中断频率过快,则大部分电能和 CPU 时间会消耗在进入和退出低功耗状态上。
FreeRTOS 的 tickless 空闲模式会在空闲周期时停止周期性系统节拍中断。停止周期性系统节拍中断可以使微控制器长时间处于低功耗模式。移植层需要配置外部唤醒中断,当唤醒事件到来时,将微控制器从低功耗模式唤醒。微控制器唤醒后,会重新使能系统节拍中断。
由于微控制器在进入低功耗后,系统节拍计数器是停止的,但我们又需要知道这段时间能折算成多少次系统节拍中断周期,这就需要有一个不受低功耗影响的外部时钟源,即微处理器处于低功耗模式时它也在计时的,这样在重启系统节拍中断时就可以根据这个外部计时器计算出一个调整值并写入 RTOS 系统节拍计数器变量中。
configUSE_IDLE_HOOK
设置为 1 使用空闲钩子(类似于回调函数),为 0 忽略空闲钩子。空闲任务钩子是一个函数,这个函数由用户来实现,FreeRTOS 规定了函数的名字和参数,这个函数在每个空闲任务周期都会被调用。要创建一个空闲钩子:
- 设置
FreeRTOSConfig.h
文件中的configUSE_IDLE_HOOK
为 1; - 定义一个函数,函数名和参数如下所示:
void vApplicationIdleHook(void );
- 该钩子函数不可以调用会引起空闲任务阻塞的 API 函数(例如:vTaskDelay()、带有阻塞时间函数),在钩子函数内部可以使用协程。
- 使用空闲钩子函数设置 CPU 进入省电模式是很常见的用法
当 RTOS 调度器开始工作后,为了保证至少有一个任务在运行,空闲任务被自动创建,占用最低优先级(0 优先级)。***对于已经删除的 RTOS 任务,空闲任务可以释放分配给它们的堆栈内存。因此,在应用中应该注意,使用 vTaskDelete() 函数时要确保空闲任务获得一定的处理器时间。***除此之外,空闲任务没有其它特殊功能,因此可以随意的剥夺空闲任务的处理器时间。
configUSE_MALLOC_FAILED_HOOK
每当一个任务、队列、信号量被创建时,内核使用一个名为 pvPortMalloc()
的函数来从堆(configTOTAL_HEAP_SIZE
定义大小)中分配内存。官方的源码包中包含 5 个简单内存分配策略,分别保存在源文件 heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c
中。仅当使用以上这五个简单策略之一时,这个宏才有意义。
如果宏 configUSE_MALLOC_FAILED_HOOK
设置为 1,那么必须定义一个 malloc()
失败钩子函数,如果宏configUSE_MALLOC_FAILED_HOOK
设置为 0,malloc()
失败钩子函数不会被调用,即便已经定义了这个函数。malloc()
失败钩子函数的函数名和原型为 void vApplicationMallocFailedHook( void);
。
如果定义并正确配置 malloc()
失败钩子函数,则这个函数会在 pvPortMalloc()
函数返回 NULL 时被调用。只有 FreeRTOS 在响应内存分配请求时发现堆内存不足才会返回 NULL。
configUSE_DAEMON_TASK_STARTUP_HOOK
如果 configUSE_TIMERS
和 configUSE_DAEMON_TASK_STARTUP_HOOK
都设置为 1,那么应用程序必须定义一个钩子函数,其名称和原型为 void void vApplicationDaemonTaskStartupHook( void );
。
当 RTOS 守护程序任务(也称为定时器服务任务)第一次执行时,钩子函数将被精确调用一次。 需要 RTOS 运行的任何应用程序初始化代码都可以放在 hook 函数中。
configUSE_TICK_HOOK
设置为 1 使用时间片钩子(Tick Hook),0 忽略时间片钩子。时间片中断可以周期性的调用一个被称为钩子函数(回调函数)的应用程序。时间片钩子函数可以很方便的实现一个定时器功能。
只有在 FreeRTOSConfig.h
中的 configUSE_TICK_HOOK
设置成 1 时才可以使用时间片钩子。一旦此值设置成 1,就要定义钩子函数,函数名和参数如下所示:void vApplicationTickHook( void );
vApplicationTickHook()
函数在中断服务程序中执行,因此这个函数必须非常短小,不能大量使用堆栈,不能调用以 ”FromISR" 或 "FROM_ISR” 结尾的 API 函数。
configCPU_CLOCK_HZ
写入实际的 CPU 内核时钟频率,也就是 CPU 指令执行频率,通常称为 $F_{cclk}$
。配置此值是为了正确的配置系统节拍中断周期。例如,在 STM32 中时,常用 #define configCPU_CLOCK_HZ ( SystemCoreClock )
configSYSTICK_CLOCK_HZ
改配置项仅适用于 ARM Cortex-M 移植的可选参数。ARM Cortex-M 移植默认从 Cortex-M Systick 定时器生成 RTOS 滴答中断。大多数 Cortex-M MCU 以与 MCU 自身相同的频率运行 SysTick 定时器,如果这样,则不需要 configSYSTICK_CLOCK_HZ
,而且它应保持未定义状态。如果 SysTick 定时器的频率与 MCU 核心不同,那么应将 configCPU_CLOCK_HZ
设置为正常的 MCU 时钟频率,将 configSYSTICK_CLOCK_HZ
设置为 SysTick 时钟频率。
configTICK_RATE_HZ
RTOS 系统自己的节拍中断的频率。即一秒中断的次数,每次中断 RTOS 都会进行任务调度。系统节拍中断用来测量时间,因此,越高的测量频率意味着可测到越高的分辨率时间。但是,高的系统节拍中断频率也意味着 RTOS 内核占用更多的 CPU 时间,因此会降低效率。
RTOS 演示例程都是使用系统节拍中断频率为 1000HZ,这是为了测试 RTOS 内核,比实际使用的要高。(实际使用时不用这么高的系统节拍中断频率)
多个任务可以共享一个同等的优先级,RTOS 调度器为相同优先级的任务分享 CPU 时间,在每一个 RTOS 系统节拍中断到来时进行任务切换(见 configUSE_TIME_SLICING
)。高的系统节拍中断频率会降低分配给每一个任务的时间片持续时间。
configMAX_PRIORITIES
配置应用程序有效的优先级数目。任何数量的任务都可以共享一个同等优先级,使用协程可以单独的给与它们优先权(见configMAX_CO_ROUTINE_PRIORITIES
)。在 RTOS 内核中,每个有效优先级都会消耗一定量的 RAM,因此这个值不要超过你的应用实际需要的优先级数目。
每一个任务都会被分配一个优先级,优先级值从 0 ~ (configMAX_PRIORITIES - 1)
之间。数字越小优先级越低。 空闲任务的优先级为 0(tskIDLE_PRIORITY
),因此它是最低优先级任务。
FreeRTOS 调度器将确保处于就绪状态(Ready)或运行状态(Running)的高优先级任务比同样处于就绪状态的低优先级任务优先获取处理器时间。换句话说,处于运行状态的任务永远是高优先级任务。
处于就绪状态的相同优先级任务使用时间片调度机制共享处理器时间。
configMINIMAL_STACK_SIZE
定义空闲任务使用的堆栈大小。通常此值不应小于对应处理器演示例程文件 FreeRTOSConfig.h
中定义的数值。就像xTaskCreate()
和 xTaskCreateStatic()
函数的堆栈大小参数一样,堆栈大小不是以字节为单位而是以字为单位的,比如在 32 位架构下,栈大小为 100 表示栈内存占用 400 字节的空间。
configMAX_TASK_NAME_LEN
创建任务函数时,需要设置描述任务信息的字符串(任务名字),这个宏用来定义该字符串的最大长度。这里定义的长度包括字符串结束符 '\0'
。
configUSE_TRACE_FACILITY
设置成 1 表示启动可视化跟踪调试,会激活一些附加的结构体成员和函数。该配置通常在调试时才会使用,在真正发布程序时必须将其关闭,因为其对于 FreeRTOS 的性能是有影响的。如下图是使用 Percepio Tracealyzer 分析的 FreeRTOS 的运行情况图:
configUSE_STATS_FORMATTING_FUNCTIONS
设置宏 configUSE_TRACE_FACILITY
和 configUSE_STATS_FORMATTING_FUNCTIONS
为 1 会编译 vTaskList()
和vTaskGetRunTimeStats()
函数。如果将这两个宏任意一个设置为 0,上述两个函数将被忽略。通常也是在调试时才使用,用来观察各任务。
configUSE_16_BIT_TICKS
时间是以 ticks
来衡量的 - 这是自 RTOS 内核启动以来 tick 事件中断已执行的次数。 tick 计数保存在 TickType_t
类型的变量中。将 configUSE_16_BIT_TICKS
定义为 1 会使 TickType_t
为无符号的 16 位类型定义。 将 configUSE_16_BIT_TICKS
定义为 0 会使TickType_t
为无符号 32 位类型定义。
使用 16 位类型将大大提高 8 位和 16 位架构的性能,但将最大可指定时间限制为 65535 个 “ticks”。 因此,假设节拍频率为250Hz,当使用 16 位计数器时,任务可以延迟或阻塞的最大时间为 262 秒,而使用 32 位计数器时为 17179869 秒。
configIDLE_SHOULD_YIELD
如果 configIDLE_SHOULD_YIELD
设置为 0,则空闲任务将在其整个时间片内保持运行状态,除非它被更高优先级的任务抢占;如果 configIDLE_SHOULD_YIELD
设置为 1,那么如果有其他空闲优先级任务处于就绪状态,则空闲任务将在其循环的每次迭代中让步(自愿放弃其分配的时间片的剩余部分)。
通过时间片共享同一个优先级的多个任务,如果共享的优先级大于空闲优先级,并假设没有更高优先级任务,这些任务应该获得相同的处理器时间。但如果共享的是空闲任务的优先级 0 时,情况会稍微有些不同。当 configIDLE_SHOULD_YIELD
为 1 时,其它共享空闲任务优先级的用户任务就绪时,空闲任务立刻让出 CPU,用户任务运行,这样确保了能最快响应用户任务。处于这种模式下也会有不良效果(取决于你的程序需要),描述如下:
图中描述了四个处于空闲优先级的任务,任务 A、B 和 C 是用户任务,任务 I 是空闲任务。上下文切换周期性的发生在 T0、T1…T6 时刻。当用户任务运行时,空闲任务立刻让出 CPU,但是,空闲任务已经消耗了当前时间片中的一定时间。这样的结果就是空闲任务 I 和用户任务 A 共享一个时间片。用户任务 B 和用户任务 C 因此获得了比用户任务 A 更多的处理器时间。可以通过下面方法避免:
- 如果合适的话,将处于空闲优先级的各单独的任务放置到空闲钩子函数中;
- 创建的用户任务优先级大于空闲任务优先级;
- 设置
IDLE_SHOULD_YIELD
为 0;
设置 configIDLE_SHOULD_YIELD
为 0 将阻止空闲任务为用户任务让出 CPU,直到空闲任务的时间片结束。这确保所有处在空闲优先级的任务分配到相同多的处理器时间,但是,这是以分配给空闲任务更高比例的处理器时间为代价的。
configUSE_TASK_NOTIFICATIONS
设置宏 configUSE_TASK_NOTIFICATIONS
为 1(或不定义 configUSE_TASK_NOTIFICATIONS
)将会开启任务通知功能,有关的API 函数也会被编译。设置宏 configUSE_TASK_NOTIFICATIONS
为 0 则关闭任务通知功能,相关 API 函数也不会被编译。
开启后,每个任务多增加 8 字节 RAM。
RTOS 任务通知
每个 RTOS 任务具有一个 32 位的通知值(V8.2.0 开始)或者任务通知数组(V10.4.0 开始),RTOS 任务通知相当于直接向任务发送一个事件,接收到通知的任务可以解除任务的阻塞状态(因等待任务通知而进入阻塞状态)。
自 FreeRTOS V8.2.0 起可用,自 V10.4.0 起支持单任务多条通知。配置项 configTASK_NOTIFICATION_ARRAY_ENTRIES
可设置任务通知数组中的索引数量。
官方介绍 RTOS 任务通知
相对于以前必须分别创建队列、二进制信号量、计数信号量或事件组的情况,使用任务通知显然更灵活。更好的是,相比于使用信号量解除任务阻塞,使用任务通知可以快 45%(使用 GCC 编译器,-o2 优化级别)。
configTASK_NOTIFICATION_ARRAY_ENTRIES
每个 RTOS 任务都有一个任务通知数组。configTASK_NOTIFICATION_ARRAY_ENTRIES
设置数组的索引数。在 FreeRTOS V10.4.0 之前,任务只有一个通知值,而不是通知值数组,因此出于向后兼容考虑,如果 configTASK_NOTIFICATION_ARRAY_ENTRIES
未定义,则默认为 1。
configUSE_MUTEXES
设置为 1 表示使用互斥量,设置成 0 表示忽略互斥量。读者应该了解在 FreeRTOS 中互斥量和二进制信号量的区别。关于互斥量和二进制信号量简单说:
- 互斥型信号量必须是同一个任务申请,同一个任务释放,其他任务释放无效。
- 二进制信号量,一个任务申请成功后,可以由另一个任务释放。
- 互斥型信号量是二进制信号量的子集。
configUSE_RECURSIVE_MUTEXES
设置成 1 表示使用递归互斥量,设置成 0 表示不使用。
configUSE_COUNTING_SEMAPHORES
设置成 1 表示使用计数信号量,设置成 0 表示不使用。
configUSE_ALTERNATIVE_API
设置成 1 表示使用“替代”队列函数(‘alternative’ queue functions),设置成 0 不使用。“替代” API 在 queue.h
头文件中有详细描述。
注:“替代”队列函数已经被弃用,在新的设计中不要使用它!
configCHECK_FOR_STACK_OVERFLOW
每个任务维护自己的栈空间,任务创建时会自动分配任务需要的占内存,分配内存大小由创建任务函数(xTaskCreate()
)的一个参数指定。堆栈溢出是设备运行不稳定的最常见原因,因此 FreeeRTOS 提供了两个可选机制用来辅助检测和改正堆栈溢出。配置宏configCHECK_FOR_STACK_OVERFLOW
为不同的常量来使用不同堆栈溢出检测机制。
官网 stack overflow detection章节内容
注意,这个选项仅适用于内存映射未分段的微处理器架构。并且,在 RTOS 检测到堆栈溢出发生之前,一些处理器可能先产生故障(fault)或异常(exception)来反映堆栈使用的恶化。如果宏 configCHECK_FOR_STACK_OVERFLOW
没有设置成 0,用户必须提供一个栈溢出钩子函数,这个钩子函数的函数名和参数必须为 void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName );
参数 xTask
和 pcTaskName
为堆栈溢出任务的句柄和名字。请注意,如果溢出非常严重,这两个参数信息也可能是错误的!在这种情况下,可以直接检查 pxCurrentTCb
变量。推荐仅在开发或测试阶段使用栈溢出检查,因为堆栈溢出检测会增大上下文切换开销。
- 方法一:
任务切换出去后,该任务的上下文环境被保存到自己的堆栈空间,这时很可能堆栈的使用量达到了最大(最深)值。在这个时候,RTOS 内核会检测堆栈指针是否还指向有效的堆栈空间。如果堆栈指针指向了有效堆栈空间之外的地方,堆栈溢出钩子函数会被调用。
这个方法速度很快,但是不能检测到所有堆栈溢出情况(比如,堆栈溢出没有发生在上下文切换时)。设置configCHECK_FOR_STACK_OVERFLOW
为 1 会使用这种方法。
- 方法二:
当堆栈首次创建时,在它的堆栈区中填充一些已知值(标记)。当任务切换时,RTOS 内核会检测堆栈最后的 16 个字节,确保标记数据没有被覆盖。如果这 16 个字节有任何一个被改变,则调用堆栈溢出钩子函数。
这个方法比第一种方法要慢,但也相当快了。它能有效捕捉堆栈溢出事件(即使堆栈溢出没有发生在上下文切换时),但是理论上它也不能百分百的捕捉到所有堆栈溢出(比如堆栈溢出的值和标记值相同,当然,这种情况发生的概率极小)。使用这个方法需要设置configCHECK_FOR_STACK_OVERFLOW
为 2 .
configQUEUE_REGISTRY_SIZE
通过此定义来设置可以注册的信号量和消息队列个数。队列记录有两个目的,都涉及到 RTOS 内核的调试,除了进行内核调试外,队列记录没有其它任何目的:
- 注册队列的时候,可以给队列起一个名字,当使用调试组件的时候,通过名字可以很容易的区分不同队列。
- 包含调试器需要的每一个记录队列和信号量定位信息;
configQUEUE_REGISTRY_SIZE
定义可以记录的队列和信号量的最大数目。如果你想使用 RTOS 内核调试器查看队列和信号量信息,则必须先将这些队列和信号量进行注册,只有注册后的队列和信号量才可以使用 RTOS 内核调试器查看。查看 API 参考手册中的vQueueAddToRegistry()
和vQueueUnregisterQueue()
函数获取更多信息。
configUSE_QUEUE_SETS
设置成 1 使能队列集功能(可以阻塞、挂起到多个队列和信号量),设置成 0 取消队列集功能。
configUSE_TIME_SLICING
默认情况下,该宏会被定义为 1。1 表示 FreeRTOS 使用基于时间片的优先级抢占式调度器。这意味着 FreeRTOS 调度器总是运行处于最高优先级的就绪任务,在每个 FreeRTOS 系统节拍中断时在相同优先级的多个任务间进行任务切换。
如果 configUSE_TIME_SLICING
设置为 0,那么 RTOS 调度程序仍将运行处于就绪状态的最高优先级任务,但是不会因为发生滴答中断而在相同优先级的任务之间切换。
configUSE_NEWLIB_REENTRANT
设置为 1,每一个创建的任务会分配一个newlib(一个嵌入式 C 库)reent 结构。
注意 Newlib 支持已被普遍需求包括,但 FreeRTOS 维护者本身不使用。 FreeRTOS 不对产生的 newlib 操作负责。 用户必须熟悉 newlib,并且必须提供必要存根的系统范围实现。 请注意(在编写本文时),当前的 newlib 设计实现了必须提供锁的系统范围的malloc()。
configENABLE_BACKWARD_COMPATIBILITY
头文件 FreeRTOS.h
包含一系列 #define宏定义
,用来映射版本 V8.0.0 和 V8.0.0 之前版本的数据类型名字。这些宏可以确保RTOS 内核升级到 V8.0.0 或以上版本时,之前的应用代码不用做任何修改。在 FreeRTOSConfig.h
文件中设置宏configENABLE_BACKWARD_COMPATIBILITY
为 0 会去掉这些宏定义,并且需要用户确认升级之前的应用没有用到这些名字。
就是为了兼容之前的版本用的宏。例如:在之前的版本中,FreeRTOS 的各种类型均为 xAAA(如 xSemaphoreHandle),在最新版中,均使用 AAA_t(如 SemaphoreHandle_t)。因此,在新项目中,最好使用 FreeRTOS 的最新的各种类型定义。具体如下:
#if configENABLE_BACKWARD_COMPATIBILITY == 1
#define eTaskStateGet eTaskGetState
#define portTickType TickType_t
#define xTaskHandle TaskHandle_t
#define xQueueHandle QueueHandle_t
#define xSemaphoreHandle SemaphoreHandle_t
#define xQueueSetHandle QueueSetHandle_t
#define xQueueSetMemberHandle QueueSetMemberHandle_t
#define xTimeOutType TimeOut_t
#define xMemoryRegion MemoryRegion_t
#define xTaskParameters TaskParameters_t
#define xTaskStatusType TaskStatus_t
#define xTimerHandle TimerHandle_t
#define xCoRoutineHandle CoRoutineHandle_t
#define pdTASK_HOOK_CODE TaskHookFunction_t
#define portTICK_RATE_MS portTICK_PERIOD_MS
#define pcTaskGetTaskName pcTaskGetName
#define pcTimerGetTimerName pcTimerGetName
#define pcQueueGetQueueName pcQueueGetName
#define vTaskGetTaskInfo vTaskGetInfo
/* Backward compatibility within the scheduler code only - these definitions
are not really required but are included for completeness. */
#define tmrTIMER_CALLBACK TimerCallbackFunction_t
#define pdTASK_CODE TaskFunction_t
#define xListItem ListItem_t
#define xList List_t
#endif /* configENABLE_BACKWARD_COMPATIBILITY */
configNUM_THREAD_LOCAL_STORAGE_POINTERS
设置每个任务的线程本地存储指针数组大小。关于这部分的说明,详细参考 Thread Local Storage Pointers
configSTACK_DEPTH_TYPE
设置调用 xTaskCreate()
时用于指定堆栈深度的类型,以及许多其他使用堆栈大小的地方(例如,返回 堆栈高水位线)。旧版的 FreeRTOS 使用 UBaseType_t
类型变量指定堆栈大小, 但这对 8 位微控制器的限制过于严格。 configSTACK_DEPTH_TYPE
通过让应用程序开发人员指定使用的类型来解除该限制 。
configMESSAGE_BUFFER_LENGTH_TYPE
FreeRTOS 消息缓冲区使用 configMESSAGE_BUFFER_LENGTH_TYPE
类型的变量存储 每个消息的长度。 如果未定义 configMESSAGE_BUFFER_LENGTH_TYPE
, 那么它将默认为 size_t
。 如果存储在消息缓冲区中的消息不会超过 255 字节,那么在 32 位微控制器上将 configMESSAGE_BUFFER_LENGTH_TYPE
定义为 uint8_t
将为每个消息节约 3 个字节。 类似地,如果 存储在消息缓冲区中的消息不会超过 65535 字节, 那么在 32 位微控制器上将 configMESSAGE_BUFFER_LENGTH_TYPE
定义为 uint16_t
将为每个消息节约 2 个字节 。
configSUPPORT_STATIC_ALLOCATION
设置为 1,那么可以使用应用程序编写器提供的 RAM 创建 RTOS 对象。设置为 0,则只能使用从 FreeRTOS 堆分配的 RAM 创建 RTOS 对象。默认(未定义时)为 0。
如果设置为 1,则应用程序编写器还必须提供两个回调函数:vApplicationGetIdleTaskMemory()
以提供内存供 RTOS 空闲任务使用;(如果 configUSE_TIMERS
设置为 1)vApplicationGetTimerTaskMemory()
以提供内存供 RTOS 守护进程/定时器服务任务使用。下面是官方给出的一个例子:
/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an
implementation of vApplicationGetIdleTaskMemory() to provide the memory that is
used by the Idle task. */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize )
{
/* If the buffers to be provided to the Idle task are declared inside this
function then they must be declared static - otherwise they will be allocated on
the stack and so not exists after this function exits. */
static StaticTask_t xIdleTaskTCB;
static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];
/* Pass out a pointer to the StaticTask_t structure in which the Idle task's
state will be stored. */
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
/* Pass out the array that will be used as the Idle task's stack. */
*ppxIdleTaskStackBuffer = uxIdleTaskStack;
/* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
Note that, as the array is necessarily of type StackType_t,
configMINIMAL_STACK_SIZE is specified in words, not bytes. */
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
/*-----------------------------------------------------------*/
/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the
application must provide an implementation of vApplicationGetTimerTaskMemory()
to provide the memory that is used by the Timer service task. */
void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize )
{
/* If the buffers to be provided to the Timer task are declared inside this
function then they must be declared static - otherwise they will be allocated on
the stack and so not exists after this function exits. */
static StaticTask_t xTimerTaskTCB;
static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ];
/* Pass out a pointer to the StaticTask_t structure in which the Timer
task's state will be stored. */
*ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
/* Pass out the array that will be used as the Timer task's stack. */
*ppxTimerTaskStackBuffer = uxTimerTaskStack;
/* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer.
Note that, as the array is necessarily of type StackType_t,
configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
更多详细信息,参考 Static Vs Dynamic Memory Allocation
configSUPPORT_DYNAMIC_ALLOCATION
设置为 1,则可以使用从 FreeRTOS 堆中自动分配的 RAM 创建 RTOS 对象。设置为 0,则只能使用应用程序编写器提供的RAM 创建 RTOS 对象。默认(未定义)为 1
configTOTAL_HEAP_SIZE
FreeRTOS 堆中可用的 RAM 总量。该值仅在 configSUPPORT_DYNAMIC_ALLOCATION
设置为 1 且应用程序使用 FreeRTOS 源代码中提供的示例内存分配方案(portable\MemMang
目录下的 5 种内存管理方案)之一时该定义才有效。
configAPPLICATION_ALLOCATED_HEAP
默认情况下,FreeRTOS 堆由 FreeRTOS 声明,并由链接器放置在内存中。 将 configAPPLICATION_ALLOCATED_HEAP
设置为 1 允许应用程序编写器声明堆,这允许应用程序将堆放置在内存中的任何位置。
如果使用 heap_1.c,heap_2.c 或 heap_4.c
,并且 configAPPLICATION_ALLOCATED_HEAP
设置为 1,那么应用程序编写器必须提供一个具有固定名称和维度的 uint8_t
数组:uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
configGENERATE_RUN_TIME_STATS
将该宏值设置为 1 使能运行时间统计功能(依赖于 configUSE_TRACE_FACILITY
、configUSE_STATS_FORMATTING_FUNCTIONS
和 configSUPPORT_DYNAMIC_ALLOCATION
均为 1 )。需要注意,改宏值仅用于调试阶段,因为其会严重影响 FreeRTOS 的性能。一旦设置为 1,则下面两个宏必须被定义:
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
:用户程序需要提供一个基准时钟函数,函数完成初始化基准时钟功能,这个函数要被 define 到宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
上。这是因为运行时间统计需要一个比系统节拍中断频率还要高分辨率的基准定时器,否则,统计可能不精确。
基准定时器中断频率要比统节拍中断快 10 ~ 100 倍。基准定时器中断频率越快,统计越精准,但能统计的运行时间也越短(比如,基准定时器 10ms 中断一次,8 位无符号整形变量可以计到 2.55 秒,但如果是 1 秒中断一次,8 位无符号整形变量可以统计到 255 秒)。
如果将configGENERATE_RUN_TIME_STATS
定义为 1,那么 RTOS 内核会在启动时在vTaskStartScheduler()
API 中自动调用portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
。portGET_RUN_TIME_COUNTER_VALUE()
:用户程序需要提供一个返回基准时钟当前时间(通常是计数值)的函数,这个函数要被 define 到宏portGET_RUN_TIME_COUNTER_VALUE()
上。
示例
例如,假如我们已经配置好了一个每 500us 中断一次的定时器。在定时器中断服务函数中简单的使长整形变量 ulHighFrequencyTimerTicks
自增。那么上面提到两个宏定义如下(可以在 FreeRTOSConfig.h
中添加):
extern volatile unsigned longulHighFrequencyTimerTicks;
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( ulHighFrequencyTimerTicks = 0UL )
#define portGET_RUN_TIME_COUNTER_VALUE() ulHighFrequencyTimerTicks
如果没有配置好的定时器,则需要用户实现一个定时器配置函数(例如 vConfigureTimerForRunTimeStats
)独立用于运行时间统计功能,然后将定时器配置函数 define 到 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
上。
extern void vConfigureTimerForRunTimeStats( void );
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() vConfigureTimerForRunTimeStats()
#define portGET_RUN_TIME_COUNTER_VALUE() 定时器计数寄存器
官方文档 Run Time Statistics
vTaskGetRunTimeStats
void vTaskGetRunTimeStats( char *pcWriteBuffer );
API 函数用于检索收集的统计数据。参数 pcWriteBuffer
指向一个缓冲区, 执行时间将以 ASCII 形式写入其中。 该函数假设此缓冲区足够大, 足以容纳生成的报告(大约为每个任务分配 40 字节的缓冲区就足够了) 。
此函数将在其持续时间内禁用中断。 它不是作为正常的应用程序运行时的工具,而是作为调试辅助工具。
我们只需要在自己程序的合适位置及时间,先调用 vTaskGetRunTimeStats
将信息输出到提供的缓冲区中,然后输出缓冲区中的内容即可(我将 printf 重定位到了串口,这里直接从串口输出)。
vTaskGetRunTimeStats()
调用 uxTaskGetSystemState()
, 然后将 uxTaskGetSystemState()
生成的原始数据格式化为人类可读的 (ASCII) 表格, 显示每个任务在运行状态下花费的时间(每个任务消耗了多少 CPU 时间)。 数据以绝对值和百分比值的形式提供。 绝对值的分辨率取决于应用程序提供的运行时间统计时钟的频率。
时间统计(第二列)不代表具体执行了多次少时间,仅仅是我们提供的
portGET_RUN_TIME_COUNTER_VALUE
的计数个数。需要乘以我们自己的计数周期才是时间
vTaskGetIdleRunTimeCounter
TickType_t xTaskGetIdleRunTimeCounter( void );
返回空闲任务的运行时间计数器。 此函数可用于确定空闲任务获得了多少 CPU 时间。
configUSE_CO_ROUTINES
设置成 1 表示使用协程,0 表示不使用协程。如果使用协程,必须在工程中包含 croutine.c
文件。
- 协程(Co-routines)主要用于资源发非常受限的嵌入式系统(RAM 非常少),通常不会用于 32 位微处理器。
- 在当前嵌入式硬件环境下,不建议使用协程,FreeRTOS 的开发者早已经停止开发协程。
configMAX_CO_ROUTINE_PRIORITIES
应用程序协程(Co-routines)的有效优先级数目,任何数目的协程都可以共享一个优先级。使用协程可以单独的分配给任务优先级。见configMAX_PRIORITIES
。
configUSE_TIMERS
设置成1使用软件定时器,为0不使用软件定时器功能。详细描述见 Software Timers。
configTIMER_TASK_PRIORITY
设置软件定时器服务/守护进程的优先级。详细描述见 Software Timers。
configTIMER_QUEUE_LENGTH
设置软件定时器命令队列的长度。详详细描述见 Software Timers。
configTIMER_TASK_STACK_DEPTH
设置软件定时器服务/守护进程任务的堆栈深度。详详细描述见 Software Timers。
configKERNEL_INTERRUPT_PRIORITY、configMAX_SYSCALL_INTERRUPT_PRIORITY 和configMAX_API_CALL_INTERRUPT_PRIORITY
这三个宏值用来定义 FreeRTOS 对于中断的使用,与要移植的芯片有密切关系。其中,configMAX_SYSCALL_INTERRUPT_PRIORITY
和 configMAX_API_CALL_INTERRUPT_PRIORITY
这两个宏是等价的,后者是前者的新名字,用于更新的移植层代码。
Cortex-M3、PIC24、dsPIC、PIC32、SuperH 和 RX600 硬件设备需要设置宏 configKERNEL_INTERRUPT_PRIORITY
;PIC32、RX600 和 Cortex-M 硬件设备需要设置宏 configMAX_SYSCALL_INTERRUPT_PRIORITY
。
应该将
configKERNEL_INTERRUPT_PRIORITY
设置为最低优先级。
仅需要 configKERNEL_INTERRUPT_PRIORITY
的移植
configKERNEL_INTERRUPT_PRIORITY
用来设置 FreeRTOS 内核自己的中断优先级。调用 FreeRTOS API 的中断必须运行在这个优先级;不调用 FreeRTOS API 的中断,可以运行在更高的优先级,所以这些中断不会被因 FreeRTOS 内核活动而延时。
需要 configKERNEL_INTERRUPT_PRIORITY
和 configMAX_SYSCALL_INTERRUPT_PRIORITY
的移植
configKERNEL_INTERRUPT_PRIORITY
用来设置 FreeRTOS 内核自己的中断优先级。因为 FreeRTOS 内核中断不允许抢占用户使用的中断,因此这个宏一般定义为硬件最低优先级。
configMAX_SYSCALL_INTERRUPT_PRIORITY
用来设置可以在中断服务程序中安全调用 FreeRTOS API 函数的最高中断优先级。如果中断的优先级小于等于这个宏所代表的优先级时,则此中断服务程序中可以安全的调用 FreeRTOS API 函数;如果中断的优先级大于这个宏所代表的优先级,表示 FreeRTOS 无法禁止这个中断,在这个中断服务程序中绝不可以调用任何 FreeRTOS API 函数(即使是FromISR 结尾的函数)。
通过设置 configMAX_SYSCALL_INTERRUPT_PRIORITY
的优先级级别高于 configKERNEL_INTERRUPT_PRIORITY
可以实现完整的中断嵌套模式。这意味着 FreeRTOS 内核不能完全禁止中断,即使在临界区。此外,这对于分段内核架构的微处理器是有利的。
需要注意的是,当一个新中断发生后,某些微处理器架构会(在硬件上)禁止中断,这意味着从硬件响应中断到 FreeRTOS 重新使能中断之间的这段短时间内,中断是不可避免的被禁止的。
不调用 FreeRTOS API 的中断可以运行在比 configMAX_SYSCALL_INTERRUPT_PRIORITY
高的优先级,这些级别的中断不会被FreeRTOS 禁止,因此不会因为执行 RTOS 内核而被延时。
例如:假如一个微控制器有 8 个中断优先级别:0 表示最低优先级,7 表示最高优先级(Cortex-M3 和 Cortex-M4 内核优先数和优先级别正好与之相反)。当两个配置选项分别为 4 和 0 时,下图描述了每一个优先级别可以和不可做的事件:
这些配置参数允许非常灵活的中断处理:
-
可以根据系统中的任何其他任务编写中断处理“任务”并设置优先级。这些任务通过一个相应中断唤醒。中断服务例程(ISR)内容应尽可能的精简—仅用于更新数据然后唤醒高优先级任务。ISR 退出后,直接运行被唤醒的任务,因此中断处理(根据中断获取的数据来进行的相应处理)在时间上是连续的,就像 ISR 在完成这些工作。这样做的好处是当中断处理任务执行时,所有中断都可以处在使能状态。
-
configMAX_SYSCALL_INTERRUPT_PRIORITY
接口有着更深一层的意义:在优先级介于 RTOS 内核中断优先级(等于configKERNEL_INTERRUPT_PRIORITY
)和configMAX_SYSCALL_INTERRUPT_PRIORITY
之间的中断允许全嵌套中断模式并允许调用 API 函数。大于configMAX_SYSCALL_INTERRUPT_PRIORITY
的中断优先级绝不会因为执行 RTOS 内核而延时。 -
运行在大于
configMAX_SYSCALL_INTERRUPT_PRIORITY
的优先级中断是不会被 RTOS 内核所屏蔽的,因此也不受 RTOS 内核功能影响。这主要用于非常高的实时需求中。比如执行电机转向。但是,这类中断的中断服务例程中绝不可以调用 FreeRTOS的API 函数。
为了使用这个方案,应用程序要必须符合以下规则:调用 FreeRTOS API 函数的任何中断,都必须和 RTOS 内核处于同一优先级(由宏 configKERNEL_INTERRUPT_PRIORITY
设置)或者小于等于宏 configMAX_SYSCALL_INTERRUPT_PRIORITY
定义的优先级。
ARM Cortex-M3 和 ARM Cortex-M4 用户的特别注意事项:请阅读专用于中断 ARM Cortex-M 器件优先级设置的页面。至少,请记住,ARM Cortex-M3 内核使用数字优先级较低的数字来表示高优先级的中断,这看起来是违反直觉的,很容易忘记!如果你想分配一个低优先级的中断,不要给它赋予 0(或者其他低数字值)的优先级,因为这会导致系统中实际上具有最高优先级的中断,因此如果这样会导致系统崩溃优先级高 configMAX_SYSCALL_INTERRUPT_PRIORITY
。
实际上,ARM Cortex-M3 内核的最低优先级为 255。不过,不同的 ARM Cortex-M3 供应商实现了不同数量的优先级位,并提供了期望以不同方式指定优先级的库函数。例如,在 STM32 上,您可以在 ST 驱动程序库调用中指定的最低优先级实际上是 15(可以指定的最高优先级为 0)。
configASSERT
configASSERT()
宏的语义与标准 C 语言的 assert()
宏的语义相同。 如果传递给 configASSERT()
的参数为零,则触发断言。
在整个 FreeRTOS 源文件中调用 configASSERT()
,以检查应用程序如何使用 FreeRTOS。 强烈建议使用 configASSERT()
定义来开发 FreeRTOS 应用程序。
举一个例子,我们想把非法参数所在的文件名和代码行数打印出来,可以先定义一个函数 vAssertCalled,该函数有两个参数,分别接收触发 configASSERT 宏的文件名和该宏所在行,然后通过显示屏或者串口输出。代码如下:
#define configASSERT( ( x ) ) if( ( x ) == 0 )vAssertCalled( __FILE__, __LINE__ )
这里__FILE__ 和 __LINE__
是大多数编译器预定义的宏,分别表示代码所在的文件名(字符串格式)和行数(整形)。
如果在调试器的控制下运行 FreeRTOS,那么可以将 configASSERT()
定义为仅禁用中断并置于循环中,如下所示。 这将有效的停止在 assert 测试失败的代码 - 暂停调试器然后会立即带你到出错的行,所以你可以看到为什么它失败。
/* Define configASSERT() to disable interrupts and sit in a loop. */
#define configASSERT( ( x ) ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
这个例子虽然看起来很简单,但由于要把整形 __LINE__
转换成字符串再显示,在效率和实现上,都不能让人满意。我们可以使用 C 标准库 assert 的实现方法,这样函数 vAssertCalled 只需要接收一个字符串形式的参数(推荐仔细研读下面的代码并理解其中的技巧):
#define STR(x) VAL(x)
#define VAL(x) #x
#define configASSERT(x) ((x) ? (void) 0 :xAssertCalld(__FILE__ ":" STR(__LINE__) " " #x"\n"))
由于内置宏 __LINE__
是整数型的而不是字符串型,把它转化成字符串需要一个额外的处理层。宏 STR
和和宏 VAL
正是用来辅助完成这个转化。宏 STR
用来把整形行号替换掉 __LINE__
,宏 VAL
用来把这个整形行号字符串化。忽略宏 STR
和 VAL
中的任何一个,只能得到字符串 __LINE__
,这不是我们想要的。
这里使用三目运算符 ? :
来代替参数判断 if
语句,这样可以接受任何参数或表达式,代码也更紧凑,更重要的是代码优化度更高,因为如果参数恒为真,在编译阶段就可以去掉不必要的输出语句。
configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS
该值仅被 FreeRTOS MPU 使用。如果设置为 1,那么应用程序编写器必须提供一个名为 application_defined_privileged_functions.h
的头文件,在该头文件中可以实现应用程序编写者需要以特权模式执行的功能。
在 application_defined_privileged_functions.h
中实现的函数必须分别使用 prvraisepprivilege()
函数和 portRESET_PRIVILEGE()
宏保存和恢复处理器的特权状态。例如,如果库提供的 print 函数访问的 RAM 不在应用程序编写器的控制范围内,因此不能分配给受内存保护的用户模式任务,那么可以使用以下代码将 print 函数封装在特权函数中:
void MPU_debug_printf( const char *pcMessage )
{
/* State the privilege level of the processor when the function was called. */
BaseType_t xRunningPrivileged = prvRaisePrivilege();
/* Call the library function, which now has access to all RAM. */
debug_printf( pcMessage );
/* Reset the processor privilege level to its original value. */
portRESET_PRIVILEGE( xRunningPrivileged );
}
注意,尽管有 .h 扩展名,头文件应该包含 C 函数的实现(静态内联函数,类似于 ARM 提供的 Cortex-m 的内核源文件),而不仅仅是函数的原型。
INCLUDE Parameters
以 INCLUDE
起始的宏允许用户不编译那些应用程序不需要的实时内核组件(函数),这可以确保在你的嵌入式系统中 RTOS 占用最少的 ROM 和 RAM。每个宏以 INCLUDE_FunctionName
这样的形式出现。
这里的 FunctionName 表示一个你可以控制是否编译的 API 函数。如果你想使用该函数,就将这个宏设置成 1,如果不想使用,就将这个宏设置成 0。例如,对于 API 函数 vTaskDelete()
:
#define INCLUDE_vTaskDelete 1
表示希望使用vTaskDelete()
,允许编译器编译该函数#define INCLUDE_vTaskDelete 0
表示禁止编译器编译该函数。
模板
最后,看看官网给出的 FreeRTOSConfig.h
的模板:
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/* 再此包含自己的头文件,例如使用 STM32 系列芯片时,可以再次包含 STM32 库的头文件 */
#include "something.h"
/* 以下为一些常用的配置项目 */
#define configUSE_PREEMPTION 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configUSE_TICKLESS_IDLE 0
#define configCPU_CLOCK_HZ 60000000 /* 这个 必须 更改为自己芯片的实际频率 */
#define configTICK_RATE_HZ 250 /* FreeRTOS 的中断频率,根据需要修改 */
#define configMAX_PRIORITIES 5 /* 任务的最大优先级,根据需要修改*/
#define configMINIMAL_STACK_SIZE 128
#define configMAX_TASK_NAME_LEN 16
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
#define configUSE_TASK_NOTIFICATIONS 1
#define configUSE_MUTEXES 0
#define configUSE_RECURSIVE_MUTEXES 0
#define configUSE_COUNTING_SEMAPHORES 0
#define configUSE_ALTERNATIVE_API 0 /* Deprecated! */
#define configQUEUE_REGISTRY_SIZE 10
#define configUSE_QUEUE_SETS 0
#define configUSE_TIME_SLICING 0
#define configUSE_NEWLIB_REENTRANT 0
#define configENABLE_BACKWARD_COMPATIBILITY 0
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
/* Memory allocation related definitions. */
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configTOTAL_HEAP_SIZE 10240 /* 为FreeRTOS分配的内存,具体和选择的heap_x.c相关 */
#define configAPPLICATION_ALLOCATED_HEAP 1
/* Hook function related definitions. */
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_MALLOC_FAILED_HOOK 0
#define configUSE_DAEMON_TASK_STARTUP_HOOK 0
/* Run time and task stats gathering related definitions. */
#define configGENERATE_RUN_TIME_STATS 0
#define configUSE_TRACE_FACILITY 0
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
/* Co-routine related definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES 1
/* Software timer related definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY 3
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
/* Interrupt nesting behaviour configuration. */
#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor]
#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application]
#define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application]
/* Define to trap errors during development. */
#define configASSERT( ( x ) ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ )
/* FreeRTOS MPU specific definitions. */
#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
/* Optional functions - most linkers will remove unused functions anyway. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_xResumeFromISR 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark 0
#define INCLUDE_xTaskGetIdleTaskHandle 0
#define INCLUDE_eTaskGetState 0
#define INCLUDE_xEventGroupSetBitFromISR 1
#define INCLUDE_xTimerPendFunctionCall 0
#define INCLUDE_xTaskAbortDelay 0
#define INCLUDE_xTaskGetHandle 0
#define INCLUDE_xTaskResumeFromISR 1
/* A header file that defines trace macro can be included here. */
#endif /* FREERTOS_CONFIG_H */
参考
- http://blog.csdn.net/zhzht19861011/article/details/50134883
- http://www.freertos.org/a00110.html
更多推荐
所有评论(0)