Transplantation of FreeRTOS on STM32F407 series development boards

RTOS real-time operating system

FreeRTOS porting

  1. Add two folders to the project folder
    FreeRTOS_CORE and FreeRTOS_PORT

  2. Add list.c, queue.c, tasks.c in Source in FreeRTOS to the FreeRTOS_CORE kernel source folder

  3. Check out the Keil and MemMang in the Source in FreeRTOS and the portable migration folder. , RVDS three folders, and then find the folder corresponding to the chip model in the RVDS folder. For example, for the stm32f4 series, find ARM_CM4F, and find the corresponding file in MemMang , here I need the .c file heap_4.c. Add the files for this step to the FreeRTOS_PORT project directory folder.

  4. Add the header file path to the above file in the project

    • FreeRTOS\include
    • FreeRTOS\RVDS\ARM_CM4F
  5. Add FreeRTOSConfig.h file
    FreeRTOSConfig.h is the configuration file of the FreeRTOS operating system. The FreeRTOS operating system is tailorable. Users can tailor FreeRTOS according to their needs and cut out the FreeRTOS functions that are not needed, thereby saving precious memory resources in the MCU. . So where does the FreeRTOSConfig.h file come from?

Find the routine for the corresponding chip from the official Demo folder

The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly

  1. Then modify the FreeRTOSConfig.h header file for configuration and tailoring
/* Ensure stdint is only used by the compiler, and not the assembler. */
#include <stdio.h>
#include <stdint.h>
extern uint32_t SystemCoreClock;


#define configUSE_PREEMPTION 1 // Whether to use preemptive task calling
#define configUSE_IDLE_HOOK 0 // Whether the idle task callback function is enabled
#define configUSE_TICK_HOOK 0 // Whether the callback function of the beat task is enabled
#define configCPU_CLOCK_HZ ( SystemCoreClock ) // CPU main frequency parameter 168MHZ
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) // OS tick frequency 1ms/beat
#define configMAX_PRIORITIES (32) // Maximum task priority Maximum: 32
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 130 ) // Stack size of idle task Unit: word word
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 75 * 1024 ) ) // Heap size used by FreeRTOS 75KB
#define configMAX_TASK_NAME_LEN (32)
#define configUSE_TRACE_FACILITY 1 // Visual debugging trace
#define configUSE_16_BIT_TICKS 0 //Select 32bit variable to record the number of system beats
#define configIDLE_SHOULD_YIELD 1 // Idle task behavior settings 1 0
#define configUSE_MUTEXES 1 // Use mutex lock
#define configQUEUE_REGISTRY_SIZE 8 //The maximum number of queue signals and semaphores that can be registered and defined
#define configCHECK_FOR_STACK_OVERFLOW 0 // Stack overflow check 0: No overflow check 1 Do overflow check
#define configUSE_RECURSIVE_MUTEXES 1 // Support mutex lock nesting 0: Mutex locks cannot be nested
#define configUSE_MALLOC_FAILED_HOOK 0 // Enable callback for malloc failure
#define configUSE_APPLICATION_TASK_TAG 0
#define configUSE_COUNTING_SEMAPHORES 1 // Enable the use of semaphores 0: Turn off the use of semaphores
#define configGENERATE_RUN_TIME_STATS 0 // Do not enable running time statistics 1: Also enable 2 other macros


/* Definition of software timer function TIM1~TIM14--Hardware timer */
#define configUSE_TIMERS 0 // Enabled
#define configTIMER_TASK_PRIORITY (2)
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )

/* Set the following macro definition to 1 to enable the corresponding function, or set it to 0 to exclude the function. */
#define INCLUDE_vTaskPrioritySet 1 //You can use the task priority modification function
#define INCLUDE_uxTaskPriorityGet 1 // Get the priority of the task
#define INCLUDE_vTaskDelete 1 //Task deletion function
#define INCLUDE_vTaskCleanUpResources 1 // Clear resource function
#define INCLUDE_vTaskSuspend 1 //Task suspension function
#define INCLUDE_vTaskDelayUntil 1 // Task delay function
#define INCLUDE_vTaskDelay 1 // Precise delay function

/* 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 /* 15 priority levels */
#endif

/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf

/* 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. */
#define configASSERT( x ) if( ( x ) == 0 ) {<!-- --> taskDISABLE_INTERRUPTS(); for( ;; ); }

/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler

FreeRTOSConfig parameter official explanation document

config’ parameter

configUSE_PREEMPTION

Set to 1 to use the preemptive RTOS scheduler; set to 0 to use the cooperative RTOS scheduler.

configUSE_PORT_OPTIMISED_TASK_SELECTION

Some FreeRTOS ports have two methods of selecting the next task to execute, a generic method and a port-specific method.

General method:

  • Used when configUSE_PORT_OPTIMISED_TASK_SELECTION is set to 0, or when porting-specific methods are not implemented.

  • Available for all FreeRTOS ports.

  • Written entirely in C, less efficient than port-specific methods.

  • There is no limit to the maximum number of available priorities.

Port specific methods:

  • Some transplants are not available.

  • Used when configUSE_PORT_OPTIMISED_TASK_SELECTION is set to 1.

  • Relies on one or more architecture-specific assembly instructions (usually the Count Leading Zeros [CLZ] instruction or equivalent) and therefore only works on the architecture for which it was written.

  • More efficient than generic methods.

  • The maximum number of available priority levels is usually limited to 32.

configUSE_TICKLESS_IDLE

Set configUSE_TickLESS_IDLE to 1 to use low-power tickless mode, or to 0 to keep tick interrupts running at all times. Not all FreeRTOS ports implement low-power tickless mode.

configUSE_IDLE_HOOK

Set it to 1 to use idle hooks, or to 0 to ignore idle hooks.

configUSE_MALLOC_FAILED_HOOK

When creating a task, queue, or semaphore, the kernel calls pvPortMalloc() to allocate memory from the heap. The official FreeRTOS download includes 4 examples of memory allocation schemes. These solutions are implemented in the heap_1.c, heap_2.c, heap_3.c, heap_4.c and heap_5.c source files respectively. configUSE_MALLOC_FAILED_HOOK is only applicable when these 3 solutions are used. .

The malloc() failure hook is a hook (or callback) function that will be called if pvPortMalloc() returns NULL if it has been defined and configured. It will return NULL only if there is not enough memory left in the FreeRTOS heap to allocate the requested memory.

If configUSE_MALLOC_FAILED_HOOK is set to 1, the application must define a malloc() failure hook function. If configUSE_MALLOC_FAILED_HOOK is set to 0, then the malloc() failure hook function will not be called, even if the hook function is defined. The malloc() failure hook function must have a name and prototype as shown below.

void vApplicationMallocFailedHook( void );
configUSE_DAEMON_TASK_STARTUP_HOOK

If both configUSE_TIMERS and configUSE_DAEMON_TASK_STARTUP_HOOK are set to 1, then the application must define a hook function, and it must have the same name and prototype as the following function. When the RTOS daemon task (also known as timer service task) is executed for the first time, the hook function will be called only once. Any application initialization code that requires the RTOS to run can be placed in a hook function.

void void vApplicationDaemonTaskStartupHook( void );
configUSE_SB_COMPLETED_CALLBACK

Setting configUSE_SB_COMPLETED_CALLBACK() requires setting up xStreamBufferCreateWithCallback() and xStreamBufferCreateStaticWithCallback() (and their message buffer equivalents) in the build. Stream buffers and message buffers created using these functions can have their own unique send completion callbacks and receive completion callbacks, which are passed through xStreamBufferCreate() and xStreamBufferCreateStatic () (and Both stream buffers and message buffers created share the callbacks defined by the sbSEND_COMPLETED() and sbRECEVE_COMPLETED() macros. configUSE_SB_COMPLETED_CALLBACK Defaults to 0 for backward compatibility.

configUSE_ticking_HOOK

Set it to 1 to use the tick hook, or to 0 to ignore the tick hook.

configCPU_CLOCK_HZ

The input drives the execution frequency (in Hertz) of the internal clock used by the peripheral that generates tick interrupts. The internal clock is also usually the clock that drives the internal CPU. This value is required to correctly configure the timer peripheral.

configSYStic_CLOCK_HZ

Optional parameter for ARM Cortex-M ports only.

ARM Cortex-M ports generate RTOS tick interrupts from the Cortex-M Sys tick timer by default. Most Cortex-M MCUs run the SysTick timer at the same frequency as the MCU itself, and if so, configSYSTICK_CLOCK_HZ is not needed and should remain undefined. If the frequency of the SysTick timer is different from the MCU core, then configCPU_CLOCK_HZ should be set to the normal MCU clock frequency and configSYSTICK_CLOCK_HZ to the SysTick clock frequency.

configTICK_RATE_HZ

RTOS ticks Frequency of interrupts.

Tick interrupts are used to measure time. Therefore, the higher the tick frequency, the higher the resolution of time measurement. However, a high tick frequency also means that the RTOS kernel will use more CPU time and therefore be less efficient. The tick frequency used by the RTOS demo application is 1000 Hz. This frequency is used to test the RTOS kernel and is higher than normal frequency requirements.

Multiple tasks can share the same priority. The RTOS scheduler will switch tasks with the same priority during each RTOS tick, allowing them to share processor time. Therefore, a high tick frequency also reduces the “time slice” allocated to each task.

configMAX_PRIORITIES

The number of priorities available for application tasks. Any number of tasks can share the same priority. Coroutines are assigned priorities individually, see configMAX_CO_ROUTINE_PRIORITIES.

Each available priority consumes some RAM from the RTOS kernel, so this value should not be set higher than the number of priorities your application actually needs.

If configUSE_PORT_OPTIMISED_TASK_SELECTION is set to 1, then the maximum allowed value will be limited.

configMINIMAL_STACK_SIZE

The stack size used by idle tasks. Typically, this value should not be less than the value set in FreeRTOSConfig.h, the header file located in the ported demo application you are using.

This stack size is similar to the stack size parameter of the xTaskCreate() and xTaskCreateStatic() functions, specified in words rather than bytes. If each element on the stack is 32 bits, then a stack of size 100 elements contains 400 bytes (32 bits per stack element, consuming 4 bytes).

configMAX_TASK_NAME_LEN

When creating a task, the maximum allowed length of a task’s descriptive name. The length is specified in characters, including the NULL terminating byte.

configUSE_TRACE_FACILITY

Set this to 1 if you want to include additional structure members and functions to assist with visualization and tracing.

configUSE_STATS_FORMATTING_FUNCTIONS

Set configUSE_TRACE_FACILITY and configUSE_STATS_FORMATTING_FUNCTIONS to 1 to include features in the vTaskList() and vTaskGetRunTimeStats() functions. Setting either to 0 will ignore vTaskList() and vTaskGetRunTimeStates() at build time.

configUSE_16_BIT_ticks

Time is measured in ticks’, which is the number of tick interrupts that have been executed since the RTOS kernel started. The number of ticks is stored in a variable of type TickType_t.

Defining configUSE_16_BIT_ticks to 1 defines tickType_t as a (typedef) unsigned 16-bit type. Defining configUSE_16_BIT_ticks to 0 defines tickType_t as a (typedef) unsigned 32-bit type.

Using 16-bit types on both 8-bit and 16-bit architectures will greatly improve performance, but will limit the maximum specifyable time period to 65535 ‘ticks’. Therefore, assuming a tick frequency of 250 Hz, the maximum time a task can be delayed or blocked is 262 seconds when using a 16-bit counter, and 17179869 seconds when using a 32-bit counter.

Added configTICK_TYPE_WIDTH_IN_BITS to support tick counts greater than 32 bits. New designs should use configTICK_TYPE_WIDTH_IN_BITS instead of configUSE_16_BIT_TICKS.

configTICK_TYPE_WIDTH_IN_BITS

Time is measured in ticks’, which is the number of tick interrupts that have been executed since the RTOS kernel started. The number of ticks is stored in a variable of type TickType_t.

configTICK_TYPE_WIDTH_IN_BITS controls the type (and bit width) of TickType_t:

  • Defining configTICK_TYPE_WIDTH_IN_BITS as TICK_TYPE_WIDTH_16_BITS causes TickType_t to be typedef’ed as an unsigned 16-bit type.
  • Defining configTICK_TYPE_WIDTH_IN_BITS as TICK_TYPE_WIDTH_32_BITS causes TickType_t to be typedef’ed as an unsigned 32-bit type.
  • Defining configTICK_TYPE_WIDTH_IN_BITS as TICK_TYPE_WIDTH_64_BITS causes TickType_t to be typedef’ed as an unsigned 64-bit type.

Using 16-bit types greatly improves performance on 8- and 16-bit architectures, but limits the maximum period that can be specified to 65535 “ticks”. Likewise, using 32-bit types improves performance on 32-bit architectures, but limits the maximum period that can be specified to 4294967295 “ticks.”

configIDLE_SHOULD_YIELD

This parameter controls the behavior of idle priority tasks. It only works under the following circumstances:

  • Use a preemptive scheduler.
  • The application creates tasks that run at idle priority.

If configUSE_TIME_SLICING is set to 1 (or undefined), tasks with the same priority will use time slicing. If no tasks are preempted, then it can be assumed that each task of a given priority will be allocated the same processing time – which is indeed the case if the priority is higher than idle priority.

If tasks are all idle priority, their behavior may be slightly different. If configIDLE_SHOULD_YIELD is set to 1, the idle task will be suspended immediately if other idle priority tasks are ready to run. This ensures that idle tasks spend minimal time when there are application tasks to schedule. However, this behavior can have undesirable effects as follows (depending on your application requirements):


The figure above shows the execution pattern of four idle priority tasks. Tasks A, B, and C are application tasks. Task I is the idle task. The context switches periodically at times T0, T1, …, T6. When the idle task is suspended, task A starts executing, but part of the current time slice has been consumed by the idle task. This results in Task I and Task A actually sharing the same time slice. Therefore, application tasks B and C get more processing time than application task A.

This situation can be avoided by:

  • If appropriate, use idle hooks instead of separate idle priority tasks.
  • Create all application tasks with a higher priority than idle.
  • Set configIDLE_SHOULD_YIELD to 0.

Setting configIDLE_SHOULD_YIELD to 0 prevents idle tasks from yielding processing time until their time slice ends. This ensures that all idle priority tasks are allocated the same processing time (if no tasks are preempted), but this comes at the expense of a higher proportion of processing time allocated to idle tasks.

FreeRTOS Kernel Developer Documentation

  • Task
  • Queue, mutex lock, semaphore…
  • Direct task notification
  • Stream buffer & message buffer
  • Software timer
  • event group (or “flag”)
  • Source code organization
  • FreeRTOSConfig.h
  • Static memory vs dynamic memory
  • Heap memory management
  • stack overflow protection
  • Create a new project
  • Coroutines (deprecated)

Task

Applications can be designed using only tasks, only coroutines, or a combination of both. However, tasks and coroutines use different API functions, so data cannot be passed through queues (or semaphores) from tasks to coroutines or vice versa.

Coroutines are really only used on very small processors with large RAM constraints.

Features of “Task”

In short: Real-time applications using RTOS can be built as a set of independent tasks. Each task executes in its own context and does not depend on other tasks within the system or the RTOS scheduler itself. Only one task can be executed in the application at any point in time, and the real-time RTOS scheduler is responsible for deciding which task to execute. Therefore, the RTOS scheduler can repeatedly start and stop each task (call the task in or out) while the application is executing. Because tasks are unaware of RTOS scheduler activity, the real-time RTOS scheduler is responsible for ensuring that the processor context (register values, stack contents, etc.) when the task is called in is exactly the same as when the task was called out. To achieve this, each task is assigned its own stack. When a task is called out, the execution context is saved to that task’s stack so that its execution context can be accurately restored when the same task is called in later.

Task summary
easy to use.
No usage restrictions.
Support full preemptive mechanism.
Completely in order of priority.

Each task keeps its own stack, improving RAM usage.
If a preemptive mechanism is used, reentrancy must be carefully considered.

Task status

Tasks can exist in the following states:

  • run

When a task is actually executed, it is said to be in the running state. The task is currently using the processor. If the processor running the RTOS has only one core, only one task can be running at any given time.

  • Ready

Ready tasks are those that can be executed (they are not blocked or suspended), but are not currently executing because a different task of equal or higher priority is already running.

  • block

A task is considered blocked if it is currently waiting for time or an external event. For example, if a task calls vTaskDelay(), it will be blocked (put in a blocked state) until the delay ends – a time event. Tasks can also block waiting for queue, semaphore, event group, notification, or semaphore events. Tasks in a blocked state usually have a “timeout” period. After the timeout, the task will be timed out and unblocked, even if the event the task is waiting for does not occur.

Tasks in the “blocked” state do not use any processing time and cannot be selected to enter the running state.

  • hang

Like tasks in the “blocked” state, tasks in the “suspended” state cannot be selected to enter the running state, but tasks in the suspended state do not time out. Instead, a task enters or exits the suspended state only when explicit commands are called through the vTaskSuspend() and xTaskResume() APIs, respectively.

Task priority

Each task is assigned a priority from 0 to ( configMAX_PRIORITIES – 1 ), where configMAX_PRIORITIES is defined in FreeRTOSConfig.h.

If the port being used implements a port-optimized task selection mechanism that uses a “leading zero count” class directive (for task selection within a single directive) and configUSE_PORT_OPTIMISED_TASK_SELECTION is set to 1 in FreeRTOSConfig.h, then configMAX_PRIORITIES must not be greater than 32. In all other cases, configMAX_PRIORITIES can take any value within a reasonable range, but for reasons of RAM usage efficiency should be kept to the minimum value actually needed.

Low priority numbers represent low priority tasks. Idle tasks have priority zero (tskIDLE_PRIORITY).

The FreeRTOS scheduler ensures that tasks in the Ready or Running state will always get processor (CPU) time before lower priority tasks that are also in the Ready state. In other words, a task placed in the running state is always the highest priority task that can be run.

Any number of tasks can share the same priority. If configUSE_TIME_SLICING is undefined, or if configUSE_TIME_SLICING is set to 1, ready tasks of the same priority will share available processing time using a time-sliced round-robin scheduling scheme.

FreeRTOS Scheduling

(Single Core, AMP and SMP)

This page briefly introduces the FreeRTOS scheduling algorithm, mainly for single-core, asymmetric multi-core (AMP), and symmetric multi-core (SMP) RTOS configuration. The scheduling algorithm is the software program that determines which RTOS task should be running. Only one task per processor core can be running at any given time. In AMP, each processor core runs its own instance of FreeRTOS. In SMP, there is a FreeRTOS instance that can schedule RTOS tasks across multiple cores.

Default RTOS scheduling policy (single core)

FreeRTOS uses a fixed-priority preemptive scheduling strategy by default, and performs time slice round-robin scheduling for tasks of the same priority:

  • “Fixed priority” means that the scheduler does not permanently change a task’s priority, although it may temporarily increase a task’s priority due to priority inheritance.

  • “Preemptive scheduling” means that the scheduler always runs the highest priority RTOS task that is available, regardless of when the task is available to run. For example, if an interrupt service routine (ISR) changes the highest-priority runnable task, the scheduler stops the currently running lower-priority task and starts the higher-priority task-even if this occurs within the same time slice. At this point, the high-priority task is said to “preempt” the low-priority task.

  • “Polling scheduling” means that tasks with the same priority enter the running state in turn.

  • “Time slice” means that the scheduler will switch between tasks of the same priority on each tick interrupt. The time between tick interrupts constitutes a time slice. The tick interrupt is a periodic interrupt used by RTOS to measure time.

Use a prioritized preemptive scheduler to avoid task starvation

The consequence of always running the highest-priority runnable task is that a high-priority task that never enters the “blocked” or “suspended” state will permanently starve all low-priority tasks of any execution duration. This is one reason why it’s usually best to create event-driven tasks. For example, if a high-priority task is waiting for an event, it should not be in a loop (polling) for that event, because if it is polling, it will always run and never enter “blocking” or “hang”. “start” status. Otherwise, the task should enter the “blocked” state waiting for the event. Events can be sent to tasks using one of the many FreeRTOS inter-task communication and synchronization primitives. After receiving the event, the task with higher priority will automatically release the “blocked” state. When a high-priority task is in the “blocked” state, a low-priority task will run.

Configure RTOS scheduling policy

The following FreeRTOSConfig.h settings change the default scheduling behavior:

  • configUSE_PREEMPTION

If configUSE_PREEMPTION is set to 0, “preemption” is turned off, only when the running task enters the “blocked” or “suspended” state, or the running task calls taskYIELD(), or the interrupt service routine (ISR) manually requests a context switch. , a context switch will occur.

  • configUSE_TIME_SLICING

If configUSE_TIME_SLICING is set to 0, it means time slicing is turned off, so the scheduler does not switch between tasks of equal priority on every tick interrupt.

FreeRTOS AMP scheduling strategy

Asymmetric multiprocessing (AMP) with FreeRTOS is when each core of a multicore device runs its own instance of FreeRTOS. These cores do not all need to have the same architecture, but some memory will need to be shared if communication between FreeRTOS instances is required.

Each core runs its own instance of FreeRTOS, so the scheduling algorithm on any given core is exactly the same as the single-core system scheduling algorithm above. You can use stream buffers or message buffers as inter-core communication primitives, so that a task on one core can enter a “blocked” state waiting for data or events from another core.

FreeRTOS SMP scheduling strategy

Symmetric multiprocessing (SMP) with FreeRTOS means that a FreeRTOS instance can schedule RTOS tasks across multiple processor cores. Since there is only one instance of FreeRTOS running and only one port of FreeRTOS can be used at a time, each core must have the same processor architecture and share the same memory space.

The FreeRTOS SMP scheduling policy uses the same algorithm as the single-core scheduling policy, but unlike the single-core and AMP scenarios, SMP causes multiple tasks to be running at any given time (one running on each core). Task). This means that the assumption that low-priority tasks will only run when there is a lack of high-priority tasks to run is no longer true. To understand why, consider how the SMP scheduler selects tasks to run on a dual-core microcontroller if only one high-priority task and two medium-priority tasks are initially in the “ready” state. The scheduler needs to select two tasks, one for each core. First, a high priority task is the highest priority task that can be run, so it is selected for use on the first core. This leaves the two medium priority tasks as the highest priority tasks that can be run, so they will be used on the second core. The result is that high- and medium-priority tasks run simultaneously.

Configure SMP RTOS scheduling policy

The following configuration options help move code written for a single-core or AMP RTOS configuration into an SMP RTOS configuration that relies on the assumption that if there is a high-priority task that can be run, then Low priority tasks will not run.

  • configRUN_MULTIPLE_PRIORITIES

If you set configRUN_MULTIPLE_PRIORITIES to 0 in FreeRTOSConfig.h, the scheduler will only run multiple tasks with the same priority at the same time. This may fix code written based on the assumption of “only run one task at a time”, but it will prevent some of the benefits of SMP configuration.

  • configUSE_CORE_AFFINITY

If configUSE_CORE_AFFINITY is set to 1 in FreeRTOSConfig.h, the vTaskCoreAffinitySet() API function can be used to define which cores a task can and cannot run on. Using this method, application writers can prevent two tasks from being executed simultaneously, assuming the order in which they are executed.