1 系統(tǒng)的剪裁
嵌入式系統(tǒng)通常都支持用戶自定義進(jìn)行剪裁,比較常見的Linux
系統(tǒng)通過Kbuild
在構(gòu)建系統(tǒng)的時(shí)候進(jìn)行剪裁,同樣,國產(chǎn)的RT-Thread
也支持Kbuild
,可以很方便地通過menuconfig
進(jìn)行內(nèi)核的剪裁,具體如下所示;
另外也支持通過對config
文件進(jìn)行配置,Linux的內(nèi)核構(gòu)建過程可以參考文章《探索Linux內(nèi)核:Kconfig / kbuild的秘密》;
FreeRTOS
支持使用FreeRTOSConfig.h
配置文件進(jìn)行定制,下面有幾點(diǎn)需要注意的地方;
- 每個FreeRTOS應(yīng)用程序的預(yù)處理器必須包含頭文件
FreeRTOSConfig.h
; FreeRTOSConfig.h
是根據(jù)用戶需求而進(jìn)行定制產(chǎn)生的文件,它應(yīng)位于應(yīng)用程序的目錄中,而不是RTOS
內(nèi)核代碼的目錄;FreeRTOS
源碼中的每一個demo
程序都有屬于自己的FreeRTOSConfig.h
文件,如果一下配置選項(xiàng)被省略,那這些設(shè)置為默認(rèn)值;- 用戶通過修改
FreeRTOSConfig.h
頭文件中的宏定義,從而刪減系統(tǒng)的模塊,設(shè)置任務(wù)??臻g大小,設(shè)置系統(tǒng)分配的堆大小等等,下面配合源碼進(jìn)行詳細(xì)解析。
2 FreeRTOSConfig.h
這里先找到文件FreeRTOSConfig.h
,這個FreeRTOS
工程是通過CubeIDE
進(jìn)行快速整合的,具體可以參考《【FreeRTOS學(xué)習(xí)01】CubeIDE快速整合FreeRTOS創(chuàng)建第一個任務(wù)》;工程結(jié)構(gòu)如下圖所示;
本文的FreeRTOSConfig.h
頭文件是根據(jù)集成在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 ) //應(yīng)用程序任務(wù)可用的優(yōu)先級數(shù)
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
#define configTOTAL_HEAP_SIZE ((size_t)15360)
#define configMAX_TASK_NAME_LEN ( 16 ) //創(chuàng)建任務(wù)時(shí),任務(wù)的名稱允許的最大長度
#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 */
下面將對某些額外需要注意的進(jìn)行另外注釋;
3 應(yīng)用相關(guān)配置
configUSE_PREEMPTION
這個宏定義來選擇當(dāng)前系統(tǒng)任務(wù)之間的調(diào)度算法;
- 設(shè)置為
1
,則表示選擇RTOS為搶占式調(diào)度; - 設(shè)置為
0
,則表示調(diào)度算法為時(shí)間片調(diào)度;
通常這里需要設(shè)置為搶占式調(diào)度,一般設(shè)置為
1
configMAX_PRIORITIES
應(yīng)用程序任務(wù)可用 的優(yōu)先級數(shù),任意數(shù)量的任務(wù)可以共享相同的優(yōu)先級。協(xié)同例程(Co-routines
)分別進(jìn)行優(yōu)先級排序,可以參考configMAX_CO_ROUTINE_PRIORITIES
;每個可用的優(yōu)先級都會消耗RTOS內(nèi)核中的RAM,因此該值不應(yīng)設(shè)置為高于應(yīng)用程序?qū)嶋H需要的值;
configMINIMAL_STACK_SIZE
空閑任務(wù)使用的堆棧大??;通常,不應(yīng)將此值從演示應(yīng)用程序隨附的FreeRTOSConfig.h文件中為您所使用的端口設(shè)置的值中減少。xTaskCreate()
和 xTaskCreateStatic()
函數(shù)的堆棧大小參數(shù)一樣,堆棧大小以字而不是字節(jié),如果放在堆棧上的每個單元都是32位,堆棧大小100則表示??400字節(jié),4bytes等于32bit;
configUSE_16_BIT_TICKS
- 設(shè)置為
1
:TickType_t被定義(類型定義)為無符號的16位類型; - 設(shè)置為
0
:TickType_t被定義(類型定義)為無符號的32位類型;
當(dāng)系統(tǒng)是8位或16位的結(jié)構(gòu)時(shí),使用這個設(shè)置,可以提高能;
configUSE_CO_ROUTINES
- 設(shè)置為
1
可在構(gòu)建中包括協(xié)同例程功能; - 設(shè)置為
0
可從構(gòu)建中省略協(xié)同例程功能;
要包含協(xié)例程,必須在項(xiàng)目中包含
croutine.c
configMAX_CO_ROUTINE_PRIORITIES
應(yīng)用程序協(xié)同例程可用的優(yōu)先級數(shù)。任意數(shù)量的協(xié)同例程可以共享相同的優(yōu)先級。任務(wù)分別進(jìn)行優(yōu)先級排序-請參閱configMAX_PRIORITIES
4 內(nèi)存管理配置
configSUPPORT_STATIC_ALLOCATION
- 設(shè)置為
1
:則可以使用程序中預(yù)先分配好的RAM
創(chuàng)建RTOS
對象; - 設(shè)置為
0
:只能使用從FreeRTOS
堆分配的RAM
創(chuàng)建RTOS
對象;
如果未定義configSUPPORT_STATIC_ALLOCATION,則默認(rèn)為0如果將configSUPPORT_STATIC_ALLOCATION設(shè)置為1,則應(yīng)用程序編寫器還必須提供兩個回調(diào)函數(shù):- -
vApplicationGetIdleTaskMemory()
:用于提供RTOS空閑任務(wù)的內(nèi)存;vApplicationGetTimerTaskMemory()
:用于提供RTOS守護(hù)程序/計(jì)時(shí)器服務(wù)任務(wù)(如果configUSE_TIMERS設(shè)置為1)的內(nèi)存: 。
這個在移植的時(shí)候可能也會遇到,具體如下所示;
/* 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
- 設(shè)置為
1
:則可以使用從FreeRTOS堆自動分配的RAM創(chuàng)建RTOS對象; - 設(shè)置為
0
:則只能使用應(yīng)用程序編寫器提供的RAM創(chuàng)建RTOS對象;
如果未定義configSUPPORT_DYNAMIC_ALLOCATION,則默認(rèn)為1;
configTOTAL_HEAP_SIZE
設(shè)置FreeRTOS
中堆可用的RAM總量;僅當(dāng)configSUPPORT_DYNAMIC_ALLOCATION
設(shè)置為1;且使用FreeRTOS
源代碼下載中提供的示例內(nèi)存分配方案(heap_1.c
,heap_2.c
,heap_3.c
,heap_4.c
,heap_5.c
)時(shí),才會配置這個值;
configAPPLICATION_ALLOCATED_HEAP
- 設(shè)置為
0
:FreeRTOS
堆由FreeRTOS
聲明,并由鏈接器放置在內(nèi)存中; - 設(shè)置為
1
:可以使堆改為由用戶程序進(jìn)行設(shè)置,允許將堆放置在內(nèi)存中任意位置;
如果使用
heap_1.c
,heap_2.c
或heap_4.c
,并且configAPPLICATION_ALLOCATED_HEAP
設(shè)置為1
,那么應(yīng)用程序編寫器必須提供一個uint8_t數(shù)組,其名稱和維數(shù)如下所示。uint8_t ucHeap [configTOTAL_HEAP_SIZE];
該數(shù)組將被作為FreeRTOS的堆來使用。如何將數(shù)組放置在特定的內(nèi)存位置取決于所使用的編譯器–請參閱編譯器的文檔。
5 hook 鉤子函數(shù)
鉤子函數(shù)的本質(zhì)就是回調(diào)函數(shù),有下面四個函數(shù);
configUSE_TICK_HOOK
;configUSE_IDLE_HOOK
;configUSE_MALLOC_FAILED_HOOK
;configUSE_DAEMON_TASK_STARTUP_HOOK
;
具體參考hook函數(shù);
configUSE_TICK_HOOK
程序必須為hook函數(shù)提供以下原型:void vApplicationTickHook( void );
configUSE_IDLE_HOOK
configUSE_IDLE_HOOK設(shè)置為1,才會調(diào)用IDLE_HOOK,設(shè)置此選項(xiàng)后,應(yīng)用程序必須為hook函數(shù)提供以下原型:void vApplicationIdleHook(void);
configUSE_MALLOC_FAILED_HOOK
程序必須為hook函數(shù)提供以下原型:void vApplicationMallocFailedHook( void );
configUSE_DAEMON_TASK_STARTUP_HOOK
RTOS守護(hù)程序任務(wù)與計(jì)時(shí)器服務(wù)任務(wù)相同。有時(shí)將其稱為守護(hù)程序任務(wù),因?yàn)樵撊蝿?wù)現(xiàn)在不僅用于維護(hù)計(jì)時(shí)器,還用于更多任務(wù)。將configUSE_DAEMON_TASK_STARTUP_HOOK
設(shè)置為1,則在守護(hù)程序任務(wù)首次開始執(zhí)行時(shí),將立即調(diào)用守護(hù)程序任務(wù)啟動鉤子函數(shù)。應(yīng)用程序的初始化代碼如果需要放在該鉤子函數(shù)中,則該功能將很有用,這將允許初始化代碼利用RTOS功能。
應(yīng)用程序編寫器必須使用以下名稱提供原型的“守護(hù)程序任務(wù)”啟動掛鉤函數(shù)的實(shí)現(xiàn)。void vApplicationDaemonTaskStartupHook( void );
6 功能選配
如果以下函數(shù)沒有被使用到,已經(jīng)設(shè)置為0
,使其不被包含到應(yīng)用程序中,從而減少程序的應(yīng)用空間;
#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 總結(jié)
如果是學(xué)習(xí)FreRTOS的初期,只需要了解幾個比價(jià)關(guān)鍵的配置即可,隨著后期對該系統(tǒng)功能了解的深入,在系統(tǒng)的剪裁,RAM和ROM的優(yōu)化上就可以進(jìn)一步的了解,另外也可以參考https://www.freertos.org/a00110.html,