🎀 文章作者:二土电子
🌸 关注文末公众号获取其他资料和工程文件!
🐸 期待大家一起学习交流!
文章目录
- 一、“INCLUDE”开头的宏定义
- 二、“config”开始的宏定义
- 2.1 configAPPLICATION_ALLOCATED_HEAP
- 2.2 configASSERT
- 2.3 configCHECK_FOR_STACK_OVERFLOW
- 2.4 configCPU_CLOCK_HZ
- 2.5 configSUPPORT_DYNAMIC_ALLOCATION
- 2.6 configENABLE_BACKWARD_COMPATIBILITY
- 2.7 configGENERATE_RUN_TIME_STATS
- 2.8 configIDLE_SHOULD_YIELD
- 2.9 configMAX_CO_ROUTINE_PRIORITIES
- 2.10 configMAX_PRIORITIES
- 2.11 configMAX_TASK_NAME_LEN
- 2.12 configMINIMAL_STACK_SIZE
- 2.13 configNUM_THREAD_LOCAL_STORAGE_POINTERS
- 2.14 configQUEUE_REGISTRY_SIZE
- 2.15 configSUPPORT_STATIC_ALLOCATION
- 2.16 configTICK_RATE_HZ
- 2.17 configTIMER_QUEUE_LENGTH
- 2.18 configTIMER_TASK_PRIORITY
- 2.19 configTIMER_TASK_STACK_DEPTH
- 2.20 configTOTAL_HEAP_SIZE
- 2.21 configUSE_16_BIT_TICKS
- 2.22 configUSE_APPLICATION_TASK_TAG
- 2.23 configUSE_CO_ROUTINES
- 2.24 configUSE_COUNTING_SEMAPHORES
- 2.25 configUSE_DAEMON_TASK_STARTUP_HOOK
- 2.26 configUSE_IDLE_HOOK
- 2.27 configUSE_MALLOC_FAILED_HOOK
- 2.28 configUSE_MUTEXES
- 2.29 configUSE_PORT_OPTIMISED_TASK_SELECTION
- 2.30 configUSE_PREEMPTION
- 2.31 configUSE_QUEUE_SETS
- 2.32 configUSE_RECURSIVE_MUTEXES
- 2.33 configUSE_STATS_FORMATTING_FUNCTIONS
- 2.34 configUSE_TASK_NOTIFICATIONS
- 2.35 configUSE_TICK_HOOK
- 2.36 configUSE_TICKLESS_IDLE
- 2.37 configUSE_TIMERS
- 2.38 configUSE_TIME_SLICING
- 2.39 configUSE_TRACE_FACILITY
FreeRTOS是可以配置裁剪的,需要什么功能就可以使能什么功能,需要什么配置也可以更改。FreeRTOS 的配置基本是通过在 FreeRTOSConfig.h 中使用“#define”这样的语句来定义宏定义实现的。
一、“INCLUDE”开头的宏定义
1.1 “INCLUDE”开头的宏定义功能
使用“INCLUDE”开头的宏定义,作用是使能或使能 FreeRTOS 中相应的 API 函数。其实这个的实现方法就是我们在之前开发时使用的条件编译。这里可以看一下“task.c”文件中的一段代码
#if ( INCLUDE_vTaskPrioritySet == 1 )
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
{
// ...
}
#endif
当宏INCLUDE_vTaskPrioritySet 设置为0时,不能使用函数 vIaskPrioritySet0)。当设置为 1 时,可以使用函数vIaskPrioritySet0。
FreeRTOS 中的裁剪和配置就是这种用条件编译的方法来实现的。利用条件编译的方法,不需要的功能就不再编译,可以节省空间,减少系统占用的 ROM 和 RAM 大小。
以“INCLUDE”开头的宏定义的使用方法比较简单,需要使用某些特定函数时,需要将对应的以“INCLUDE”开头的宏定义设置为1。修改位置如下
1.2 一些“INCLUDE”开头的宏定义
这里简单标注一下各个以“INCLUDE”开头的宏定义对应的函数
/***************************************************************************************************************/
/* FreeRTOS可选函数配置选项 */
/***************************************************************************************************************/
#define INCLUDE_xTaskGetSchedulerState 1 // 使用函数 xTaskGetSchedulerState()
#define INCLUDE_vTaskPrioritySet 1 // 使用函数 vTaskPrioritySet()
#define INCLUDE_uxTaskPriorityGet 1 // 使用函数 uxTaskPriorityGet()
#define INCLUDE_vTaskDelete 1 // 使用函数 vTaskDelete()
#define INCLUDE_vTaskSuspend 1 // 使用函数vTaskSuspend(),vTaskResume(),prvTaskIsTaskSuspended(),xTaskResumeFromISR()
#define INCLUDE_vTaskDelayUntil 1 // 使用函数 vTaskDelayUntil()
#define INCLUDE_vTaskDelay 1 // 使用函数 vTaskDelay()
#define INCLUDE_eTaskGetState 1 // 使用函数 eTaskGetState()
#define INCLUDE_xTimerPendFunctionCall 1 // 使用函数 xTimerPendFunctionCall()和xTimerPendFunctionCallFromISR()
二、“config”开始的宏定义
“config”开始的宏和“INCLUDE_”开始的宏一样,都是用来完成 FreeRTOS 的配置和裁剪的。
2.1 configAPPLICATION_ALLOCATED_HEAP
FreeRTOS的堆内存默认是由编译器来分配的。如果将configAPPLICATION_ALLOCATED_HEAP 定义为 1 的话,堆内存就可以由用户自行设置。堆内存在 heap_1.c、heap_2.c、heap_3.c、heap_4.c 和 heap_5.c 中有定义,我们选用的是“heap_4.c”,在“heap_4.c”中可以修改该宏定义。
/* Allocate the memory for the heap. */
#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
/* The application writer has already defined the array used for the RTOS
heap - probably so it can be placed in a special segment or address. */
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif /* configAPPLICATION_ALLOCATED_HEAP */
2.2 configASSERT
//断言
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
断言。类似于C语言中的“assert()”。断言的意思是对某种假设条件进行检测,如果条件成立就不进行任何操作,如果条件不成立就捕捉到这种错误,并打印出错误信息,终止程序执行。在这里,如果x为0,说明发生了错误。
使用断言虽然方便在程序调试时发现错误,但是会导致程序编译后的目标代码体积变大,降低最终发布的程序效率。断言只会报告程序是错误的,而不会对错误进行相应的处理。因此,断言一般只适合用在调试阶段。
vAssertCalled()函数需要用户自行去定义,既可以将错误信息显示到显示器件上,也可以是通过串口打印出来。上面的vAssertCalled()函数是当参数 x 错误是,会通过串口打印出发生错误的文件名和错误所在的行号。
2.3 configCHECK_FOR_STACK_OVERFLOW
设置堆栈溢出检测。每个任务都有一个任务堆栈,如果使用函数 xTaskCreate()创建一个任务的话那么这个任务的堆栈是自动从 FreeRTOS 的堆(ucHeap)中分配的,堆栈的大小是由函数xTaskCreate)的参数 usStackDepth 来决定的。如果使用函数 xTaskCreateStatic0创建任务的话任务堆栈是由用户设置的,参数 pxStackBuffer 为任务堆栈,一般是一个数组。
堆栈溢出是导致应用程序不稳定的主要因素,FreeRTOS 提供了两种可选的机制来帮助检测和调试堆栈溢出,不管使用哪种机制都要设置宏 configCHECK FOR STACK OVERFLOW。不管使用哪种检测机制,都需要事先定义好一个钩子函数(回调函数)。当检测到有堆栈溢出时,会调用这个钩子函数。
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName );
参数 xTask 是任务句柄,pcTaskName 是任务名字,要注意的是堆栈溢出太严重的话可能会损毁这两个参数,如果发生这种情况的话可以直接查看变量 pxCurrentTCB 来确定哪个任务发生了堆栈溢出。堆栈溢出检测一般也只是在调试阶段使用。
2.3.1 堆栈溢出检测方法一
上下文切换时需要保存现场,而现场是保存在堆栈中的,这个时候任务堆栈使用率很可能达到量大值。方法一就是不断的检测任条堆栈指针是否指向有效空间,如果指向了无效空旧的话就会调用钩子函数。该方法的优点就是快,但是不能检测全部类型的堆栈溢出。
2.3.2 堆栈溢出检测方法二
方法二在创建任务时,会向任务堆栈填充一个已知的标记值,方法二会一直检测堆栈后面的几个bvtes(标记值)是否被改写,如果被改写的话就会调用堆栈溢出钩子函数。方法二虽然比方法一慢一点。但是方法二能检测到几乎所有的堆栈溢出。但是也有一些情况检测不到,比如溢出值和标记值相同的时候。
2.4 configCPU_CLOCK_HZ
设置CPU频率。
2.5 configSUPPORT_DYNAMIC_ALLOCATION
该宏定义为1时,创建 FreeRTOS 的内核对象所需要的 RAM 会从 FreeRTOS 的堆中动态的获取。如果为 0 ,所需的 RAM 就需要用户自行提供,默认情况下为1。
2.6 configENABLE_BACKWARD_COMPATIBILITY
开启数据类型宏定义的条件编译。在“FreeRTOS.h”文件中定义了一些数据类型,当configENABLE_BACKWARD_COMPATIBILITY为1时,使能这些数据类型的定义。
#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 */
2.7 configGENERATE_RUN_TIME_STATS
设置为 1 开启时间统计功能,相应的 API 函数会被编译,为 0 时关闭时间统计功能。如果设置为1,还需要再配置下面两个宏定义
2.8 configIDLE_SHOULD_YIELD
当configIDLE_SHOULD_YIELD为0时,处于空闲状态的任务不会将CPU使用权让给同等优先级的其他就绪状态任务。当configIDLE_SHOULD_YIELD为1时,处于空闲状态的任务会将CPU使用权让给其他同等优先级的就绪状态任务,除非没有处于就绪状态的任务。这样会减少在空闲任务上花费的时间。但是,当把configIDLE_SHOULD_YIELD设置为1时,也会带来副作用。
图中T0~T1为一个时间片。任务A,B,C,I是三个同等优先级的任务。但是I是一个空闲任务。任务B和任务C执行完成后,执行任务I。此时,任务A处于就绪状态。由于configIDLE_SHOULD_YIELD设置为1。所以任务I会将CPU使用权让给任务A。任务A执行一段时间后继续执行其他任务。可以看出,任务A的执行时间会比其他两个任务少。通常我们现在使用的CPU性能都比较强,所以一般都会关闭这个功能。
2.9 configMAX_CO_ROUTINE_PRIORITIES
设置可以分配给协程的最大优先级,也就是协程的优先级数。设置完成后,协程的优先级可以从 0 到 configMAX CO ROUTINE PRIORITIES-1,其中0是最低
的优先级configMAX CO ROUTINE PRIORITIES1 为最高的优先级
。协程,英文Coroutines,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。协程不是被操作系统内核所管理,而完全是由程序所控制。
2.10 configMAX_PRIORITIES
设置任务的优先级数量。设置完成后,任务就可以使用从 0 到 configMAX_PRIORITIES-1 的优先级,其中 0 是最低优先级
,configMAX_PRIORITIES-1 是最高优先级
。
2.11 configMAX_TASK_NAME_LEN
设置任务名最大长度。
2.12 configMINIMAL_STACK_SIZE
设置空闲任务的最小任务堆栈大小,以字为单位,不是字节
。比如在 STM32 上设置为 100的话,那么真正的堆栈大小就是 100*4=400 字节。
2.13 configNUM_THREAD_LOCAL_STORAGE_POINTERS
设置每个任务的本地存储指针数组大小,任务控制块中有本地存储数组指针,用户应用程序可以在这些本地存储中存入一些数据。
2.14 configQUEUE_REGISTRY_SIZE
设置可以注册的队列和信号量的最大数量,在使用内核调试器查看信号量和队列的时候需要设置此宏,而且要先将消息队列和信号量进行注册,只有注册了的队列和信号量才会再内核调试器中看到,如果不使用内核调试器的话此宏设置为 0 即可。
2.15 configSUPPORT_STATIC_ALLOCATION
当此宏定义为1,在创建一些内核对象的时候需要用户指定 RAM,当为0的时候就会自使用 heap.c 中的动态内存管理函数来自动的申请 RAM。
2.16 configTICK_RATE_HZ
设置 FreeRTOS 的系统时钟节拍频率,单位为 HZ,这个频率就是滴答定时器的中断频率,需要使用此宏定义来配置滴答定时器的中断。
2.17 configTIMER_QUEUE_LENGTH
配置 FreeRTOS 软件定时器,FreeRTOS 的软件定时器 API函数会通过命令队列向软件定时器任务发送消息,此宏用来设置这个软件定时器的命令队列长度。
2.18 configTIMER_TASK_PRIORITY
设置软件定时器任务的任务优先级。
2.19 configTIMER_TASK_STACK_DEPTH
设置定时器服务任务的任务堆栈大小。
2.20 configTOTAL_HEAP_SIZE
设置堆大小,如果使用了动态内存管理的话,FreeRTOS 在创建任务、信号量、队列等的时候就会使用 heap x.cx 为 1~5)中的内存申请函数来申请内存。这些内存就是从堆ucHeap[configTOTAL HEAP SIZE]中申请的,堆的大小由 configTOTAL HEAP SIZE 来定义。
2.21 configUSE_16_BIT_TICKS
设置系统节拍计数器变量数据类型,系统节拍计数器变量类型为 TickIype t,当configUSE 16 BIT TICKS 为1 的时候 TickType t 就是 16 位的,当 configUSE 16 BIT TICKS为 0的话 TickType t就是 32 位的。
2.22 configUSE_APPLICATION_TASK_TAG
此宏设置为1的话函数configUSE APPLICATION TASK TAGFO 和xTaskCallApplicationTaskHook(就会被编译。
2.23 configUSE_CO_ROUTINES
此宏为 1的时候启用协程,协程可以节省开销,但是功能有限,现在的 MCU 性能已经非常强大了,建议关闭协程
。
2.24 configUSE_COUNTING_SEMAPHORES
设置为 1的时候启用计数型信号量,相关的 API 函数会被编译。
2.25 configUSE_DAEMON_TASK_STARTUP_HOOK
当宏 configUSE TIMERS 和 configUSE DAEMON TASK STARTUP HOOK 都为1时,需要定义函数 VApplicationDaemonTaskStartupHook()
void vApplicationDaemonTaskStartupHook( void );
2.26 configUSE_IDLE_HOOK
为 1 时使用空闲任务钩子函数,用户需要实现空闲任务钩子函数
void vApplicationIdleHook( void );
2.27 configUSE_MALLOC_FAILED_HOOK
为 1 时使用内存分配失败钩子函数,用户需要实现内存分配失败钩子函数
void vApplicationMallocFailedHook( void );
2.28 configUSE_MUTEXES
为 1 时使用互斥信号量,相关的 API 函数会被编译。
2.29 configUSE_PORT_OPTIMISED_TASK_SELECTION
FreeRTOS 有两种方法来选择下一个要运行的任务,一个是通用的方法,另外一个是特殊的方法,也就是硬件方法,使用 MCU自带的硬件指今来实现。该宏定义设置为0时,为通用方法。设置为1时,为特殊方法。
2.30 configUSE_PREEMPTION
为1时使用抢占式调度器,为 0 时使用协程。如果使用抢占式调度器的话,内核会在每个时钟节拍中断中进行任务切换。当使用协程的话,会在如下地方进行任务切换
- 一个任务调用了函数 taskYIELD()
- 一个任务调用了可以使任务进入阻塞态的 API 函数
- 应用程序明确定义了在中断中执行上下文切换
2.31 configUSE_QUEUE_SETS
为 1 时启用队列集功能。
2.32 configUSE_RECURSIVE_MUTEXES
为 1 时使用递归互斥信号量,相关的 API 函数会被编译。
2.33 configUSE_STATS_FORMATTING_FUNCTIONS
宏 configUSE TRACE FACILITY 和 configUSE STATS FORMATTING FUNCTIONS 都为1的时候函数 vTaskList0和 vTaskGetRunTimeStats0会被编译。
2.34 configUSE_TASK_NOTIFICATIONS
为1的时候使用任务通知功能,相关的 API函数会被编译,开启了此功能的话每个任务会多消耗8 个字节
。
2.35 configUSE_TICK_HOOK
为 1时使能时间片钩子函数,用户需要实现时间片钩子函数
void vApplicationTickHook( void );
2.36 configUSE_TICKLESS_IDLE
为1时使能低功耗 tickless 模式。
2.37 configUSE_TIMERS
为1时使用软件定时器,相关的 API 函数会被编译,当宏 configUSE TIMERS 为1的话那 么 宏configTIMER TASK PRIORITYconfigTIMER OUEUE LENGTH和configTIMER TASK STACK DEPTH 必须定义。
2.38 configUSE_TIME_SLICING
默认情况下,FreeRTOS 使用抢占式调度器,这意味着调度器永远都在执行已经就绪了的最高优先级任务,优先级相同的任务在时钟节拍中断中进行切换。当宏 configUSE TIME SLICING为 0 时,不会在时钟节拍中断中执行相同优先级任务的任务切换,默认情况下宏configUSE TIME SLICING 为1。
2.39 configUSE_TRACE_FACILITY
为 1 启用可视化跟踪调试,会增加一些结构体成员和 API 函数。