FreeRTOS-系统配置与裁剪
- 在使用FreeRTOS的时候,我们可以根据自己的需要来配置和裁剪FreeRTOS。我们在FreeRTOSConfig.h文件中完成对FreeRTOS系统的裁剪,通过裁剪来减少系统占用RAM和ROM的大小。所谓裁剪其实就是对FreeRTOS中的函数和一些变量、结构体等选择性的定义。
FreeRTOS裁剪原理
-
FreeRTOS裁剪主要就是用到了条件编译的方法,其实不仅仅是FreeRTOS是这样,很多RTOS系统都是采用这样的方法。所谓条件编译就是当该部分满足条件的时候,该部分编译。如下图是FreeRTOS中的静态任务创建函数的条件编译。 -
在上图中,我们为了能够清晰地看出这个条件编译,将函数的具体内容给折叠了,只留下函数声明部分。我们可以看到,当INCLUDE_vTaskDelay==1的时候该部分才会被编译。我们通过定义追踪找到了INCLUDE_vTaskDelay在FreeRTOSConfig.h文件中通过宏定义如下。 -
此时INCLUDE_vTaskDelay被定义为1,说明这部分对应的函数将会被编译。同样,我们通过追踪INCLUDE_vTaskDelay被引用的地方,追踪到INCLUDE_vTaskDelay在FreeRTOS.h文件中被引用如下。 -
因为FreeRTOS文件是包含FreeRTOSConfig.h文件的,所以如果说INCLUDE_vTaskDelay在FreeRTOSConfig.h中没有被定义,那么系统将会默认INCLUDE_vTaskDelay=0,即对应函数部分不编译。 -
到这里,我们以vTaskDelay函数为例就将FreeRTOS条件编译大致过程叙述完了,其他函数的编译与该函数相似,不再赘述。下面是官方给出的STM32F10移植例程中的FreeRTOSConfig,h文件的配置。 -
-
从中可以看出,该文件定义的宏分为两部分,一部分是以config为开头的宏,另一部分是以INCLUDE_为开头的宏,通过上述步骤追踪其引用可以发现以下规律:
- 以config为开头的宏多是与任务相关参数、系统工作方式等相关的,也有少部分与函数使能和除能相关。
- 以INCLUDE_为开头的宏几乎都是与函数的使能和失能相关的。
下面就来简单介绍一下两部分常用宏的功能
config为开头的宏
1、configUSE_PREEMPTION
- configUSE_PREEMPTION=1时FreeRTOS系统为抢占式内核,configUSE_PREEMPTION=0时,使用协程内核。协程内核可以节省开销,但是功能有限。协程内核主要是针对以前性能较低的MCU使用的,但现在的MCU功能都比较强,所以不需要使用协程内核。
2、configUSE_TIME_SLICING
+如果使用抢占式内核, configUSE_TIME_SLICING=1时,因为调度器永远都在执行准备就绪的最高优先级任务,优先级任务相同的在时钟节拍中断进行切换,否则不会切换。
3、configUSE_PORT_OPTIMISED_TASK_SELECTION
- FreeRTOS有硬件和软件两种方法选择下一个执行的任务。软件方法是通用方法,在任何MCU上均能使用,且不限制最大优先级数目,但是这种方法会占用CPU开销,所以效率比较低。硬件方法需要所使用的的MCU支持前导计算指令,且最大优先级数目会有所限制,但效率高。
- 当configUSE_PORT_OPTIMISED_TASK_SELECTION=1时,使用硬件方法,否则使用软件方法。
4、configUSE_TICKLESS_IDLE
- configUSE_TICKLESS_IDLE=1使用tickless低功耗模式,此时在执行空闲任务的时候系统时钟节拍中断就会停止,当然停止的这段时间也必须要补上。
5、configUSE_QUEUE_SETS
6、configCPU_CLOCK_HZ
- 设置CPU频率,一般来说配置为和MCU相同的主频。
7、configTICK_RATE_HZ
- 设置系统时钟节拍频率,该频率就是滴答定时器的中断频率,该中断频率最小单位为1us。
8、configMAX_PRIORITIES
- 可使用的最大优先级数量,设置完成以后就可以设置任务优先级,但必须在0~(configMAX_PRIORITIES-1)区间内,其中0为最低优先级,configMAX_PRIORITIES-1为最高优先级。一般来说,最低优先级只分配给空闲任务,最高优先级只分配给软件定时器,所以实际可供用户使用的优先级数量为configMAX_PRIORITIES-2。
9、configMINIMAL_STACK_SIZE
- 空闲任务使用的堆栈大小,要注意这个大小的单位不一定是1byte,比如说stm32是32位单片机,那么大小的单位就是4byte,当configMINIMAL_STACK_SIZE=100时,就相当于空闲任务堆栈大小为400byte。
10、configMAX_TASK_NAME_LEN
11、configUSE_16_BIT_TICKS
- 系统节拍计数器变量数据类型,根据自己使用的是多少位的单片机来做修改,stm32是32位MCU,所以选择32位。下图是其定义。
- 从中可以看到,当configUSE_16_BIT_TICKS=1时,定义系统节拍计数器变量类型为16位,否则为32位。
11、configIDLE_SHOULD_YIELD
- 选择空闲任务是否放弃CPU使用权给其他同优先级的用户任务,configIDLE_SHOULD_YIELD=1,让出CPU使用权给其他同级任务;否则不会让出。一般来说我们会关闭这个功能,即置零。因为如果使能的话,可能会导致有些用户任务运行的时间变少。所以一般将最低优先级只留给空闲任务。
12、configUSE_TASK_NOTIFICATIONS
- configUSE_TASK_NOTIFICATIONS=1,开启任务通知功能,此时相关函数将会被编译,每个任务将会多消耗8byte。
13、configUSE_MUTEXES
- configUSE_MUTEXES=1时,使用互斥信号量。相关函数将会被编译。
14、configQUEUE_REGISTRY_SIZE
- configQUEUE_REGISTRY_SIZE = 1,启用队列记录,用以记录队列和信号量的最大数量,在使用内核调试器的时候可以查看该记录。
15、configCHECK_FOR_STACK_OVERFLOW
- 设置是否启用堆栈溢出检测。在FreeRTOS中系统运行有一个总堆栈,而用户创建的每个任务堆栈都是从系统总堆栈中分配的。堆栈溢出是导致程序不稳定的主要因素,FreeRTOS提供两种机制来检测和调试堆栈溢出,前提是需要设置该宏,同时用户还需要提供一个回调函数,堆栈溢出的时候就调用回调函数。
- 检测机制1:设置configCHECK_FOR_STACK_OVERFLOW=1,此时将会不断检测任务堆栈指针是否指向有效空间,如果指向了无效空间,那么就调用回调函数,这种方法检测速度快,但是不能检测出所有堆栈溢出。
- 检测机制2:设置configCHECK_FOR_STACK_OVERFLOW=2,此时将会不断检测堆栈后面几个字节(标记值)是否被改写,被改写则调用回调函数,这种方法检测速度慢,但几乎能检测所有堆栈溢出。但是当溢出值和标记值相同时,不能检测到。
16、configUSE_RECURSIVE_MUTEXES
- configUSE_RECURSIVE_MUTEXES=1,使用互斥递归信号量,相关函数将会被编译。
17、configUSE_MALLOC_FAILED_HOOK
- configUSE_MALLOC_FAILED_HOOK=1,内存分配失败调用回调函数,用户需要实现回调函数。
18、configUSE_COUNTING_SEMAPHORES
- configUSE_COUNTING_SEMAPHORES=1,启用计数型信号量,相关函数会被编译。
19、configSUPPORT_DYNAMIC_ALLOCATION
- configSUPPORT_DYNAMIC_ALLOCATION=1,支持动态申请内存,这时,用户创建任务时,系统就会自动为该任务从系统总堆栈中获取内存。此时函数xTaskCreate(…)就会被编译。
20、configSUPPORT_STATIC_ALLOCATION
- configSUPPORT_DYNAMIC_ALLOCATION=1,支持静态申请内存,即用户需要自己为FreeRTOS系统分配内存,并且在用户创建任务的时候也需要自行为任务分配内存。使用静态分配内存的时候需要将vApplicationGetIdleTaskMemory(…)和vApplicationGetTimerTaskMemory(…)两函数实现。
21、configTOTAL_HEAP_SIZE
- 当设置为动态内存管理时,FreeRTOS在创建任务、队列、信号量时就会自动使用heap_x.c中的内存申请函数来申请内存。这些内存就是从系统总堆栈ucHeap[configTOTAL_HEAP_SIZE]中分配的。configTOTAL_HEAP_SIZE用来设置总堆栈大小。下图是系统总堆栈申请部分代码。
- 从代码中可以看到,当configAPPLICATION_ALLOCATED_HEAP=1时,表示用户已经自己定义了总堆大小,不需要再次定义。否则,FreeRTOS自动定义总堆栈大小。根据FreeRTOS中的宏定义。
- 即如果是系统自动定义总堆大小的话,不需要用户在FreeRTOSConfig.h文件中定义configAPPLICATION_ALLOCATED_HEAP。
22、configUSE_IDLE_HOOK
- configUSE_IDLE_HOOK=1,使用空闲回调函数,用户需要自行定义。
23、configUSE_TICK_HOOK
- configUSE_TICK_HOOK=1,使用时间片回调函数,用户需自行定义。
24、configGENERATE_RUN_TIME_STATS
- configGENERATE_RUN_TIME_STATS=1,开启时间统计功能,相关函数会被编译,并且用户还需要自行实现portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()和portGET_RUN_TIME_COUNTER_VALUE()。
25、configUSE_TIMERS
- configUSE_TIMERS=1,启用软件定时器。
26、configTIMER_TASK_PRIORITY
- 设置软件定时器优先级,一般来说软件定时器优先级为最高,即configMAX_PRIORITIES-1。
27、configTIMER_QUEUE_LENGTH
- 设置软件定时器队列长度,FreeRTOS会通过命令队列向软件定时器任务发送消息。
28、configTIMER_TASK_STACK_DEPTH
常用的FreeRTOS配置宏就先总结到这里,其他有用到再看即可。INCLUDE_开头的宏由于都是使能或失能函数的,比较简单,不再赘述。
总之,FreeRTOS系统的裁剪主要就是通过条件编译来实现的,用户需要FreeRTOS哪些功能,直接在FreeRTOS文件中配置即可。
|