1 系统的剪裁
嵌入式系统通常都支持用户自定义进行剪裁,比较常见的Linux
系统通过Kbuild
在构建系统的时候进行剪裁,同样,国产的RT-Thread
也支持Kbuild
,可以很方便地通过menuconfig
进行内核的剪裁,具体如下所示;
另外也支持通过对config
文件进行配置,Linux的内核构建过程可以参考文章《探索Linux内核:Kconfig / kbuild的秘密》;
FreeRTOS
支持使用FreeRTOSConfig.h
配置文件进行定制,下面有几点需要注意的地方;
- 每个FreeRTOS应用程序的预处理器必须包含头文件
FreeRTOSConfig.h
; FreeRTOSConfig.h
是根据用户需求而进行定制产生的文件,它应位于应用程序的目录中,而不是RTOS
内核代码的目录;FreeRTOS
源码中的每一个demo
程序都有属于自己的FreeRTOSConfig.h
文件,如果一下配置选项被省略,那这些设置为默认值;- 用户通过修改
FreeRTOSConfig.h
头文件中的宏定义,从而删减系统的模块,设置任务栈空间大小,设置系统分配的堆大小等等,下面配合源码进行详细解析。
2 FreeRTOSConfig.h
这里先找到文件FreeRTOSConfig.h
,这个FreeRTOS
工程是通过CubeIDE
进行快速整合的,具体可以参考《【FreeRTOS学习01】CubeIDE快速整合FreeRTOS创建第一个任务》;工程结构如下图所示;
本文的FreeRTOSConfig.h
头文件是根据集成在CubeIDE
中的CubeMX
插件自动生成的,源码如下所示;
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/* Ensure definitions are only used by the compiler, and not by the assembler. */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
void xPortSysTickHandler(void);
#endif
#define configUSE_PREEMPTION 1
#define configSUPPORT_STATIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configUSE_IDLE_HOOK 0 //
#define configUSE_TICK_HOOK 0 //
#define configCPU_CLOCK_HZ ( SystemCoreClock ) //输入以Hz为单位的CPU频率:例如 72Mb->72000000
#define configTICK_RATE_HZ ((TickType_t)1000) //输入以Hz为单位的滴答中断频率
#define configMAX_PRIORITIES ( 7 ) //应用程序任务可用的优先级数
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
#define configTOTAL_HEAP_SIZE ((size_t)15360)
#define configMAX_TASK_NAME_LEN ( 16 ) //创建任务时,任务的名称允许的最大长度
#define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1 //使用互斥功能
#define configQUEUE_REGISTRY_SIZE 8
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 0
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 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
#endif
/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
/* 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. */
/* USER CODE BEGIN 1 */
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
/* USER CODE END 1 */
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
/* IMPORTANT: This define is commented when used with STM32Cube firmware, when the timebase source is SysTick,
to prevent overwriting SysTick_Handler defined within STM32Cube HAL */
/* #define xPortSysTickHandler SysTick_Handler */
#endif /* FREERTOS_CONFIG_H */
下面将对某些额外需要注意的进行另外注释;
3 应用相关配置
configUSE_PREEMPTION
这个宏定义来选择当前系统任务之间的调度算法;
- 设置为
1
,则表示选择RTOS为抢占式调度; - 设置为
0
,则表示调度算法为时间片调度;
通常这里需要设置为抢占式调度,一般设置为
1
configMAX_PRIORITIES
应用程序任务可用 的优先级数,任意数量的任务可以共享相同的优先级。协同例程(Co-routines
)分别进行优先级排序,可以参考configMAX_CO_ROUTINE_PRIORITIES
;每个可用的优先级都会消耗RTOS内核中的RAM,因此该值不应设置为高于应用程序实际需要的值;
configMINIMAL_STACK_SIZE
空闲任务使用的堆栈大小;通常,不应将此值从演示应用程序随附的FreeRTOSConfig.h文件中为您所使用的端口设置的值中减少。xTaskCreate()
和 xTaskCreateStatic()
函数的堆栈大小参数一样,堆栈大小以字而不是字节,如果放在堆栈上的每个单元都是32位,堆栈大小100则表示400字节,4bytes等于32bit;
configUSE_16_BIT_TICKS
- 设置为
1
:TickType_t被定义(类型定义)为无符号的16位类型; - 设置为
0
:TickType_t被定义(类型定义)为无符号的32位类型;
当系统是8位或16位的结构时,使用这个设置,可以提高能;
configUSE_CO_ROUTINES
- 设置为
1
可在构建中包括协同例程功能; - 设置为
0
可从构建中省略协同例程功能;
要包含协例程,必须在项目中包含
croutine.c
configMAX_CO_ROUTINE_PRIORITIES
应用程序协同例程可用的优先级数。任意数量的协同例程可以共享相同的优先级。任务分别进行优先级排序-请参阅configMAX_PRIORITIES
4 内存管理配置
configSUPPORT_STATIC_ALLOCATION
- 设置为
1
:则可以使用程序中预先分配好的RAM
创建RTOS
对象; - 设置为
0
:只能使用从FreeRTOS
堆分配的RAM
创建RTOS
对象;
如果未定义configSUPPORT_STATIC_ALLOCATION,则默认为0如果将configSUPPORT_STATIC_ALLOCATION设置为1,则应用程序编写器还必须提供两个回调函数:- -
vApplicationGetIdleTaskMemory()
:用于提供RTOS空闲任务的内存;vApplicationGetTimerTaskMemory()
:用于提供RTOS守护程序/计时器服务任务(如果configUSE_TIMERS设置为1)的内存: 。
这个在移植的时候可能也会遇到,具体如下所示;
/* 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;
}
configSUPPORT_DYNAMIC_ALLOCATION
- 设置为
1
:则可以使用从FreeRTOS堆自动分配的RAM创建RTOS对象; - 设置为
0
:则只能使用应用程序编写器提供的RAM创建RTOS对象;
如果未定义configSUPPORT_DYNAMIC_ALLOCATION,则默认为1;
configTOTAL_HEAP_SIZE
设置FreeRTOS
中堆可用的RAM总量;仅当configSUPPORT_DYNAMIC_ALLOCATION
设置为1;且使用FreeRTOS
源代码下载中提供的示例内存分配方案(heap_1.c
,heap_2.c
,heap_3.c
,heap_4.c
,heap_5.c
)时,才会配置这个值;
configAPPLICATION_ALLOCATED_HEAP
- 设置为
0
:FreeRTOS
堆由FreeRTOS
声明,并由链接器放置在内存中; - 设置为
1
:可以使堆改为由用户程序进行设置,允许将堆放置在内存中任意位置;
如果使用
heap_1.c
,heap_2.c
或heap_4.c
,并且configAPPLICATION_ALLOCATED_HEAP
设置为1
,那么应用程序编写器必须提供一个uint8_t数组,其名称和维数如下所示。uint8_t ucHeap [configTOTAL_HEAP_SIZE];
该数组将被作为FreeRTOS的堆来使用。如何将数组放置在特定的内存位置取决于所使用的编译器–请参阅编译器的文档。
5 hook 钩子函数
钩子函数的本质就是回调函数,有下面四个函数;
configUSE_TICK_HOOK
;configUSE_IDLE_HOOK
;configUSE_MALLOC_FAILED_HOOK
;configUSE_DAEMON_TASK_STARTUP_HOOK
;
具体参考hook函数;
configUSE_TICK_HOOK
程序必须为hook函数提供以下原型:void vApplicationTickHook( void );
configUSE_IDLE_HOOK
configUSE_IDLE_HOOK设置为1,才会调用IDLE_HOOK,设置此选项后,应用程序必须为hook函数提供以下原型:void vApplicationIdleHook(void);
configUSE_MALLOC_FAILED_HOOK
程序必须为hook函数提供以下原型:void vApplicationMallocFailedHook( void );
configUSE_DAEMON_TASK_STARTUP_HOOK
RTOS守护程序任务与计时器服务任务相同。有时将其称为守护程序任务,因为该任务现在不仅用于维护计时器,还用于更多任务。将configUSE_DAEMON_TASK_STARTUP_HOOK
设置为1,则在守护程序任务首次开始执行时,将立即调用守护程序任务启动钩子函数。应用程序的初始化代码如果需要放在该钩子函数中,则该功能将很有用,这将允许初始化代码利用RTOS功能。
应用程序编写器必须使用以下名称提供原型的“守护程序任务”启动挂钩函数的实现。void vApplicationDaemonTaskStartupHook( void );
6 功能选配
如果以下函数没有被使用到,已经设置为0
,使其不被包含到应用程序中,从而减少程序的应用空间;
#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
7 总结
如果是学习FreRTOS的初期,只需要了解几个比价关键的配置即可,随着后期对该系统功能了解的深入,在系统的剪裁,RAM和ROM的优化上就可以进一步的了解,另外也可以参考https://www.freertos.org/a00110.html,