Porting Modifications
In the FreeRTOS article on system porting[1], we copied the FreeRTOSConfig.h
file from the path FreeRTOSv9.0.0\FreeRTOS\Demo\CORTEX_STM32F103_Keil
to our user-modifiable App
folder and imported the project. However, the content of the FreeRTOSConfig.h
file is not what we really want, and we need to make changes. The source file content is as follows:
Although it can satisfy running FreeRTOS, often with the addition of features, we need more support from FreeRTOS. This only shows a few cut features; to make the most of resources and avoid needing to add more later, we directly import the features provided by FreeRTOS and modify them to the following content:
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/* Here is a good place to include header files that are required across
your application. */
#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
#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
#define configSTACK_DEPTH_TYPE uint16_t
#define configMESSAGE_BUFFER_LENGTH_TYPE size_t
/* Memory allocation related definitions. */
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configTOTAL_HEAP_SIZE 10240
#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 */
Of course, this is just the official provided and relatively complete configuration template; the specific functionality to implement still requires us to modify the corresponding macros.
Detailed Explanation of Configuration Items
「1、configUSE_PREEMPTION」
Set 1
: RTOS uses a preemptive scheduler, meaning when a higher priority task appears while a process is in kernel space, if the current kernel allows preemption, the current task can be suspended to execute the higher priority process;
Set 0
: RTOS uses a cooperative scheduler (time slicing), where the cooperative operating system switches to the next task after the task actively releases the CPU, and the timing of task switching is entirely determined by the currently running task, so a high-priority process cannot interrupt a low-priority process running in the kernel to preempt CPU execution.
「2、configUSE_PORT_OPTIMISED_TASK_SELECTION」
Some FreeRTOS hardware ports have two methods for selecting the next task to execute: a generic method and a method specific to that hardware port.
Generic method:
-
Used when configUSE_PORT_OPTIMISED_TASK_SELECTION
is set to0
(explicitly) or the hardware-specific method is not implemented (implicitly). -
Can be used with all FreeRTOS hardware ports. -
Written entirely in C, making it less efficient than using the hardware-specific method. -
Does not limit the maximum number of available priorities.
Hardware-specific method:
-
Not applicable to all hardware ports. -
Used when configUSE_PORT_OPTIMISED_TASK_SELECTION
is set to1
. -
Relies on one or more architecture-specific assembly instructions (usually counting leading zeros [CLZ] or equivalent instructions), so it can only be used with architectures specifically written for it. -
More efficient than the generic method. -
Typically, the maximum number of available priorities is limited to 32.
「3、configUSE_TICKLESS_IDLE」
Set 1
: Enables low power tickless mode;
Set 0
: Keeps the system tick interrupt running continuously.
In low power tickless mode, the CPU is usually set to enter low power mode using the idle hook function to achieve power saving.
「4、configUSE_IDLE_HOOK」
Set 1
: Uses the idle hook (similar to a callback function);
Set 0
: Ignores the idle hook.
The idle task hook is a function implemented by the user, and FreeRTOS specifies the name and parameters of the function: void vApplicationIdleHook(void)
, which will be called in each idle task cycle.
「5、configUSE_MALLOC_FAILED_HOOK」
Each time a task, queue, or semaphore is created, the kernel uses a call to pvPortMalloc()
to allocate memory from the heap. The FreeRTOS official download package includes five heap memory allocation schemes, which are implemented in the source files heap_1.c
, heap_2.c
, heap_3.c
, heap_4.c
, and heap_5.c
; configUSE_MALLOC_FAILED_HOOK
is only meaningful when using one of these five heap memory allocation schemes.
If a hook function for malloc failure is defined and properly configured, this function will be called when pvPortMalloc()
returns NULL; note: NULL is only returned when FreeRTOS finds insufficient heap memory in response to memory allocation requests.
Set 1
: The application must define a hook function for malloc failure;
Set 0
: Ignores the malloc failure hook function, even if this hook function is defined.
The malloc failure hook function must have the following name and prototype:
void vApplicationMallocFailedHook( void );
「6、configUSE_DAEMON_TASK_STARTUP_HOOK」
Set 1
: The application must define a hook function with the exact name and prototype;
Set 0
: Ignores this hook function.
The name and prototype are as follows:
void vApplicationDaemonTaskStartupHook( void );
The hook function will be called precisely once when the RTOS daemon task (also known as the timer service task[2]) is first executed. Any initialization code needed for applications running RTOS can be placed in the hook function.
「7、configUSE_TICK_HOOK」
Set 1
: Uses the tick hook;
Set 0
: Ignores the tick hook.
The tick hook is a function implemented by the user, and FreeRTOS specifies the name and parameters of the function: void vApplicationTickHook(void)
, which can be called periodically during the tick interrupt.
「8、configCPU_CLOCK_HZ」
Input the frequency in Hz, which will be used for the internal clock driving the peripheral for the system tick interrupt, usually the same as the clock driving the internal CPU clock; this value is configured to properly configure the system tick interrupt cycle.
「9、configTICK_RATE_HZ」
Configures the frequency of the RTOS tick interrupt; used to measure time, that is, the number of interrupts per second, each interrupt will schedule tasks in RTOS.
A higher tick frequency means higher resolution can be measured at the same time; however, a higher tick frequency also means that the RTOS kernel will use more CPU time, thus reducing efficiency. The RTOS demonstration examples generally use a tick frequency of 1000Hz, which is much higher than what is actually used (there is no need for such a high system tick interrupt frequency during actual use).
Multiple tasks can share the same priority. The RTOS scheduler will share processor time among tasks of the same priority by switching tasks upon each RTOS system tick interrupt; therefore, a higher tick frequency will reduce the “time slice” allocated to each task.
「10、configMAX_PRIORITIES」
Configures the number of priorities available for application tasks; any number of tasks can share the same priority. The priority of co-routines is separate, see configMAX_CO_ROUTINE_PRIORITIES
for details.
Each available priority consumes RAM in the RTOS kernel, so this value should not be set higher than what the application actually needs.
Each task will be assigned a priority, with the priority value between 0 ~ (configMAX_PRIORITIES – 1). A lower priority number indicates a lower priority task, and the idle task has a priority of 0 (tskIDLE_PRIORITY), thus it is the lowest priority task.
「11、configMINIMAL_STACK_SIZE」
The stack size used by the idle task. Typically, this value should not be less than the value defined in the corresponding processor demonstration routine file FreeRTOSConfig.h
.
Just like the stack size parameters in xTaskCreate()[3] and xTaskCreateStatic()[4] functions, the stack size is in words rather than bytes; for example, if the stack size is 100 on a 32-bit processor, it indicates a space size of 400 bytes.
「12、configMAX_TASK_NAME_LEN」
The maximum allowed length of the string describing task information when creating a task. The length includes the \0
string terminator.
「13、configUSE_TRACE_FACILITY」
Set 1
: Indicates that visual trace debugging is activated, which will activate some additional structure members and functions;
Set 0
: Ignores.
「14、configUSE_STATS_FORMATTING_FUNCTIONS」
When both configUSE_TRACE_FACILITY
and configUSE_STATS_FORMATTING_FUNCTIONS
are set to 1
, then the build includes the compiled vTaskList()
and vTaskGetRunTimeStats()
functions;
Otherwise, if either of these macros is 0
, these two functions will be omitted from the build and not compiled; they are typically used only during debugging to observe various tasks, often used together with macro 13.
「15、configUSE_16_BIT_TICKS」
Time is measured in “ticks”, which is the number of times the tick interrupt has executed since the RTOS kernel started; the tick count is stored in a variable of type TickType_t
.
Set 1
: TickType_t
is defined as an unsigned 16-bit type;
Set 0
: TickType_t
is defined as an unsigned 32-bit type.
Using a 16-bit type will greatly improve performance on 8-bit and 16-bit processor systems but will limit the maximum specified time period to 65535 “ticks”. Therefore, assuming a tick frequency of 250Hz, the maximum delay or block time for tasks using a 16-bit counter will be 262 seconds, while using a 32-bit counter will be 17179869 seconds.
「16、configIDLE_SHOULD_YIELD」
This parameter controls the behavior of tasks at idle priority. It only takes effect when:
-
A preemptive scheduler is being used. -
The application creates tasks that run at idle priority.
If configUSE_TIME_SLICING
is set to 1
(or not defined), then tasks with the same priority will share time slices. If there are tasks with the same priority, and its priority is higher than the idle priority, and no task is being preempted, it can be assumed that these tasks with the same priority will allocate equal processing time;
However, when tasks share idle priority, the situation is slightly different. If configIDLE_SHOULD_YIELD
is set to 1
, then whenever any user task with the same idle priority becomes ready, the idle task will immediately yield the CPU to allow the user task to run, ensuring the fastest response to user tasks. However, this behavior may have adverse effects (depending on the application’s needs) as follows:
The image above describes the execution pattern of four tasks at idle priority. Tasks A
, B
, and C
are application tasks; task I
is the idle task. At times T0, T1, …, T6, context switching occurs regularly; during the idle task’s execution, when task A
becomes ready, the idle task immediately yields the CPU to start executing task A
, but at this moment the idle task I
has already consumed some time of the current time slice, resulting in the idle task I
and task A
sharing the same time slice. Therefore, application tasks B
and C
get more processing time than application task A
.
This situation can be avoided in the following ways:
-
If appropriate, use the idle hook function instead of a separate task at idle priority. -
Create all application tasks with a priority higher than idle priority. -
Set configIDLE_SHOULD_YIELD
to0
.
Setting configIDLE_SHOULD_YIELD
to 0
can prevent the idle task from consuming processing time until its time slice ends. This ensures equal processing time allocation for all tasks at idle priority (if no task is being preempted), but at the cost of allocating a larger proportion of the total processing time to the idle task.
「17、configUSE_TASK_NOTIFICATIONS」
Set 1
(or not defined): Enables task notification functionality; relevant API functions will be included in the compilation;
Set 0
: Directly disables task notification functionality and its associated APIs from the program.
This feature is enabled by default. When enabled, each task adds 8 bytes of RAM.
「18、configUSE_MUTEXES」
Set 1
: Uses mutexes;
Set 0
: Does not use mutexes.
Readers should understand the difference between mutexes and binary semaphores in FreeRTOS.
「19、configUSE_RECURSIVE_MUTEXES」
Set 1
: Uses recursive mutexes;
Set 0
: Does not use recursive mutexes.
「20、configUSE_COUNTING_SEMAPHORES」
Set 1
: Uses counting semaphores;
Set 0
: Does not use counting semaphores.
「21、configUSE_ALTERNATIVE_API」
Set 1
: Uses “alternative” queue functions;
Set 0
: Does not use “alternative” queue functions.
The “alternative” API queue functions are described in detail in the queue.h
header file; 「the “alternative” API is deprecated and should not be used in new designs!」
「22、configCHECK_FOR_STACK_OVERFLOW」
For detailed descriptions of stack usage and stack overflow checking methods, please refer to the link Stack Usage and Stack Overflow Checking[5], which will not be detailed here.
「23、configQUEUE_REGISTRY_SIZE」
This definition sets the number of semaphores and message queues that can be registered.
The queue registry serves two purposes, both associated with RTOS kernel debugging:
-
It allows associating text names with queues for easy identification in debugging GUI. -
It contains the information needed for the debugger to find each registered queue and semaphore.
Besides kernel debugging, the queue registry serves no other purpose; only registered queues and semaphores can be viewed using the RTOS kernel debugger. For more information, see vQueueAddToRegistry()[6] and vQueueUnregisterQueue()[7] API reference documentation.
「24、configUSE_QUEUE_SETS」
Set 1
: Enables queue set functionality (can block, suspend to multiple queues and semaphores);
Set 0
: Ignores queue set functionality.
「25、configUSE_TIME_SLICING」
Set 1
(or not defined): By default, FreeRTOS uses a time-slice based preemptive scheduler, meaning the RTOS scheduler will always run the highest priority