tags: Embedded / Freeertos FreeRTOS Task dispatch Source
Freeertos can run multiple tasks, with the task scheduling function of its kernel, this article introduces the basic ideas and partial source code analysis of task scheduling.
/ * Principal function * /
int main()
{
init();// Some initialization
/ * Dead cycle * /
while(1)
{
do_something_1();// Execute some logic
do_something_2();
}// Cyclic execution
}
/ * Interrupt service function * /
IRQ_Handler()
{
set_flag();// short marker operation
}
The idea of single-chip naked machine programming is relatively simple, that is, a dead loop, the program executes the statements in While (1) in turn, cycle reciprocation, when you need to handle some emergency, you can interrupt while (1) Execution.
Although the bare metal programming is simple, it can only perform various bare metal in a loop, and the first function is executed to execute the second function.Just like some people work in turnThe Utilization of the CPU is not high, and parallel logic cannot be processed.
/ * Principal function * /
int main()
{
init();// Some initialization
xTaskCreate();
vTaskStartScheduler(); // Start the scheduler
}
/ * Submission 1 (dead cycle) * /
void task1()
{
while(1)
{
do_something_1();// Perform some logic (such as acquisition sensor information)
vTaskDelay();
}
}
/ * Submission 2 (dead cycle) * /
void task2()
{
while(1)
{
do_something_2();// Execute some logic (such as the motor movement)
vTaskDelay();
}
}
/ * Interrupt service function * /
IRQ_Handler()
{
set_event();// Trigger event, semaphore
}
MCU introduces RTOS, you can separate each function module as separatetaskEach task is a dead cycle.It is better than some people work at the same time.Such a CPU utilization is improved, and some parallel logic can be processed.
Single chip microcomputer has only one CPU (nuclear), how do you let Multiple people work at the same time? In fact, although each sub-task is a dead loop, it is not performed every sub-task, and each sub-task may require delays during execution, or the data arrived, all,When a task is waiting, the CPU can stop this task, then switch to other tasks execution, which looks like many people work at the same time.。
In the bare metal programming, when a slightly complex function is designed, many subunies are designed to implement a holistic function, this is notified in someGlobal variableorGlobal arrayWait for the connection between individual subsidies.
In RTOS, of course, global variables can also be used, but RTOS recommends we use the system to bring it.Inter-task communication mechanism. There are two reasons:
Blocking waiting mechanism is more efficient than polling
Global variable When used as a certain event flag, the task of obtaining the flag needs to polling whether the detection flag changes, which will generate a large amount of invalid judgment, and the blocking waiting mechanism in communication between the tasks, the CPU can turn other things When the logo changes, the blocking is released, and it can perform subsequent processing in time.
Global variables will generate a logical confusion that cannot be re-entered functions
When RTOS is running, the CPU jumps to each task, and if the global variable is not appropriate, it will cause the original design logic to make confusion. Such as a certainLow priorityThe task is accessing a public function and modifies the global variable in the function, and when the function has not been exited,Higher priorityThe task seizes the right to use the CPU, and also modifications to the global variables in the function. At this time, if the low priority task thinks that you have successfully modified your variable, then perform your subsequent logic, Result in logical errors.
Freertos Task Communication Method
signal(Semaphore): For the taskSynchronizeA taskWaiting in a blocking methodAnother taskWaiting anotherTask release semaphore.Mutual exclusion(MUTEX): Used for shared resources between tasksMutually exclusive access, Get the lock before use, release the lock after use.Event logo group(EventGroup): It is also used between tasksSynchronize, Compared to the quantity, the event logo group canWaiting for multipleThe event happened.message queue(Queue): Category ratio of global data, it can send multiple data at a time (generally defined data to be structurally sent), and the size of each data is fixed.Flow buffer(STREAMBUFFER) On the basis of the queue, a more suitable data structure optimized can write any number of bytes at a time, and can read any number of bytes at a time.Message buffer(MessageBuffer) On the basis of the stream buffer, it is further designed for "message", and each message is written to add a byte to indicate the length of the message. Sexually read at least one message, otherwise it will return 0.Task notification(Notify): Unlike the communication mode between the above tasks (use some kindCommunication objectCommunication objects are entities independent of the task, with separate storage space, data transfer and more complex synchronization, mutual exclusive),Notification is to send to a specified task, change certain variables of the task TCB directly。
Create a task → Ready state(Ready):After the task is createdEntering is ready, indicating that the task is ready, can run at any time, just waiting for the scheduler to schedule.Ready state → Run state(Running):When the task is switched, the highest priority task in the ready list is executed.Thus, enter the operation state.Run state → ready state: After having a higher priority task creation or recovery, task scheduling will occur, in the list of ready listThe highest priority task becomes a running state, then the original task of running is transformed by the operation state.In the ready list, the task of waiting for the highest priority will continue to run the original task.Run state → blocking state(Blocked): ** When the task is blocking (hang, delay, reading signal waiting) **, the task is deleted from the ready list, the task status is turned into blocking state, then the task occurs Switch, run the current highest priority task in the Working list.Blocking state → ready state:Blocking tasks are restoredAfter (task recovery, delay time is timeout, reading semaphoids timeout or reading signal amount), the restored task is added to the ready list, which is turned into a statement by the blocked state; if it is restored at this time The priority is higher than the priority of the running task, and the task switch will occur, and the task will be converted again, which is turned into a run state.Ready, obstructive state, running state → hang(SUSPENDED): Tasks can be invoked vtaskspend () ** API functions can hang in any status, and they are not involved in scheduling, unless they are The release state is released.Hanging state → ready state: The only way to recover a suspended state is ** call vtaskresume () or vtaskResumeFromisr () ** API function, if the priority of the recovered task is higher than the priority of running tasks, the task will occur. Switch, turn the task again convert the task status, and turn it into a run state.The above is the state of the task run, it looks a bit complicated, you can understand this:
For the sake of simplicity, you will not consider hanging up, at any time, the CPU can only handle a certain task, then the task is inRun stateFor other tasks, when you want to delay or wait,Blocked state, When you want to execute but because the priority is low, it is inReadily。
Then, how is the above state change?
1. If you want to enter the blocking state, you can run it on the task of the read state.
2. The release of the blocking state is ready. If the task is higher, you can seize the currently running task, so that yourself run, so that the other party is ready.
Have you found that the task of blocking states must be running, and must first enter the ready state, then enter the run state.
The task scheduler provided in Freeertos is based on priorityGemple schedule: In addition to the interrupt processing function, the code for the locking portion of the scheduler and the interrupt code is unpubable, and the other parts of the system can be preemptive.
The scheduler is to use the relevant scheduling algorithm.Decide the task of currently needed. All scheduler has some common features:
Freertos has two main schedules

Create 3 tasks Task1, Task2, and Task3, the priority is 1, 2, 3, which is the highest priority of Task3.
At first task Task1 is running state (occupied CPU), during the operation of Task2, the task task2 seizes the implementation of Task1 under the action of the preemptive scheduler.
and so:Task2 enters the running state by the read state, Task1 enters the ready state by the running state。
Task Task2 is running, since Task3 is in the case, the task Task3 seizes the execution of Task2 under the action of the seizure scheduler.
and so:Task3 enters the running state by the read state, TASK2 is entered by the running state。
The blocking API function is called during the task task operation, such as vtaskdelay, task task 3 is suspended, enters the pending state, finding the next highest priority task to execute under the scope of scheduler is Task2, so:Task Task2 is returned to the running state。
Task task2 is running, because the block3 blocking time ends, Task3 is again ready, under the role of the seizure schedulerTask Task3 once again seizes the execution of Task2. Task3 enters the running state, Task2 enters the read state。

Note: The above is exemplified by 5 Tick's time slice, and the time tablets of Freeertos can only be 1 Tick.
English is REAL TIME OPERATING SYSTEM, the real-time operating system,real timeRefers to the external event or data generation, it can be received and processed at a sufficient speed, and the results of its processing can control the production process or make a quick response to the processing system, and control all real-time tasks. Coordinate the operating system that runs uniformly.
RTOS is generally used in relatively low-speed MCUs, such as motion control classes, buttons, key inputs, etc., which require real-time processing, generally requested MS, or even US-Level Response.
English is Time Sharing Operating System, i.e., the time-only operating system,MinuteIt means that the system processor time and memory space are subject to a certain time interval (that is, what we saidTime film) Use the procedure for each thread to switch.
TSOS is generally used in relatively high speed CPUs, such as multi-user desktop systems, servers, etc. System (Windows, Linux).
RTOS has high priority tasksseizeFunction, as well as the same priorityTime filmRotation scheduling, thus can respond to events (ie, good real-time), and TSOS is a fixed time slice transfer adjustment. When there is an event transmission, it can only be used after the current time film is executed. A time film, so it may not be able to respond to certain emergency events in time.
Freertos scheduling each task, first requiring a way to access and control each task, the task control block can implement this function, it is a structure, record the stack pointer of the task, the current state, task priority Wait.
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; / * Stack Top * /
ListItem_t xStateListItem; / * List of tag task status (Ready, Blocked, Suspended) * /
ListItem_t xEventListItem; / * Event list of tasks * /
UBaseType_t uxPriority; / * Task priority * /
StackType_t *pxStack; / * Task Stack Start Address * /
char pcTaskName[ configMAX_TASK_NAME_LEN ]; / * Descriptive name to the task is easy to debug * /
#if(... part)
...Omission
#endif
} tskTCB;
typedef tskTCB TCB_t;
When a task needs to be delayed, call vtaskdelay (), then the task entersBlocked stateAt this time, the scheduler will start executing from the ready-priority ready task from the ready list.
What is the specific execution of the vtaskdelay ()?
#if ( INCLUDE_vTaskDelay == 1 )
void vTaskDelay( const TickType_t xTicksToDelay )
{
BaseType_t xAlreadyYielded = pdFALSE;
/ * DELAY time is 0 forced task switching * /
if( xTicksToDelay > ( TickType_t ) 0U )
{
configASSERT( uxSchedulerSuspended == 0 );
/ * Stop Task Scheduling * /
vTaskSuspenvdAll();
{
traceTASK_DELAY();
/ * The task deleted from the event list when the scheduler is suspended, and is not placed in the ready list or from the block list before the scheduler is restored.
This task cannot appear in the list of events because it is the task currently being executed. * /
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
}
xAlreadyYielded = xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/ * If XtaskResumeAll does not perform task switching, force task switch * /
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* INCLUDE_vTaskDelay */
The delay parameters of Vtaskdelay are in Tick, ie, vtaskdelay (1) delayed 1ms.
When the delay parameter is not 0, the delay function is called normally, first stop the task scheduling, add the current task to the delay list, and then restore the task scheduling.
When the delay parameter is 0, it will force task switching (PORTYIELD_WITHIN_API) (Question: If the priority of the current task is the highest, although forced switching, but due to the highest priority of the task, it does not switch to other tasks • If it is really forced to switch to another task, then this highest priority task will then grab the use of CPUs?).
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely )
{
TickType_t xTimeToWake;
const TickType_t xConstTickCount = xTickCount;
#if( INCLUDE_xTaskAbortDelay == 1 )
// ... part
#endif
/ * Remove it from the ready list before adding the task to the blocking list
if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/ * The current task must be in the ready list, so there is no need to detect, and Port Reset Macro can be called immediately * /
portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
#if ( INCLUDE_vTaskSuspend == 1 )
{
if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
{
/ * To ensure that the timer event wakes up, add tasks to the hang list instead of the delay list. It will have no regular blocking * /
vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
{
/ * If the event does not occur, the calculation task should be awakened. This may overflow, but this is irrelevant, the kernel will manage it correctly * /
xTimeToWake = xConstTickCount + xTicksToWait;
/ * The list item will be inserted in the wake-up order * /
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
if( xTimeToWake < xConstTickCount )
{
/ * Wake-up time has overflow, put this item in the overflow list * /
vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
{
/ * Wake-up time has not overflow, so the current blocking list is used. * /
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
/ * If the task that enters the blocking state is placed at the top of the blocking task list, then xnexttaskunblocktime also needs to update * /
if( xTimeToWake < xNextTaskUnblockTime )
{
xNextTaskUnblockTime = xTimeToWake;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
}
#else /* INCLUDE_vTaskSuspend */
// ... part
#endif /* INCLUDE_vTaskSuspend */
}
#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) // PENDSV's hanging position (28th)
#ifndef portYIELD_WITHIN_API
#define portYIELD_WITHIN_API portYIELD
#endif
/* Scheduler utilities. */
#define portYIELD() \
{ \
/* Set a PendSV to request a context switch. */ \
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
\
/ * Trigger PENDSV, generate context switching * / \
__dsb( portSY_FULL_READ_WRITE ); \
__isb( portSY_FULL_READ_WRITE ); \
}
The PORTYIELD () task handover function is mainly to execute the PENDSV interrupt service function when there is no other interrupt runtime, which implements task switching in this interrupt function.
The PENDSV interrupt service function is a compilation code, which may not be easy to understand, the function needs to be understood first:
External variables PxCurrentTCB is a task control block that is currently running
When entering the PENDSV interrupt service function, the operating environment of the last task is: XPSR, PC (Task Inlet Address), R14, R12, R3, R2, R1, R0 (Task), the value of these CPU registers will be automatically Save to the stack of the task, the remaining R4 ~ R11 needs to be saved manually.

In general, this function implementation 3 segment function: Save, below, the middle calls a C function vtaskswitchContext, used to find tasks to run.
__asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting;
extern pxCurrentTCB;
extern vTaskSwitchContext;
PRESERVE8
mrs r0, psp / * Save the current task PSP address to R0 * /
isb
ldr r3, =pxCurrentTCB / * Get the PXCurrentTCBCONST pointer address * /
ldr r2, [r3] / * R2 is given the current TCB address * /
/* Is the task using the FPU context? If so, push high vfp registers. */
tst r14, #0x10
it eq
vstmdbeq r0!, {s16-s31}
/* Save the core registers. */
stmdb r0!, {r4-r11, r14}
/* Save the new top of stack into the first member of the TCB. */
str r0, [r2]
stmdb sp!, {r3}
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0 / * Shield interruption * /
dsb
isb
bl vTaskSwitchContext / * Call vtaskswitchContext * /
mov r0, #0
msr basepri, r0 / * Open interrupt * /
ldmia sp!, {r3}
/* The first item in pxCurrentTCB is the task top of stack. */
ldr r1, [r3] / * R3 is still the PXCurrentTCBCONST pointer address, R1 becomes a new TCB address * /
ldr r0, [r1] / * R0 value becomes a stack address of the new TCB (the PSP value of the last scheduled last schedule) * /
/* Pop the core registers. */
ldmia r0!, {r4-r11, r14}
/* Is the task using the FPU context? If so, pop the high vfp registers
too. */
tst r14, #0x10
it eq
vldmiaeq r0!, {s16-s31}
msr psp, r0
isb
#ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata */
#if WORKAROUND_PMU_CM001 == 1
push { r14 }
pop { pc }
nop
#endif
#endif
bx r14
}
This function is actually a task run for the highest priority.
void vTaskSwitchContext( void )
{
if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
{
/* The scheduler is currently suspended - do not allow a context
switch. */
xYieldPending = pdTRUE;
}
else
{
xYieldPending = pdFALSE;
traceTASK_SWITCHED_OUT();
#if ( configGENERATE_RUN_TIME_STATS == 1 )
// ... part
#endif /* configGENERATE_RUN_TIME_STATS */
/ * Check if the stack overflow * /
taskCHECK_FOR_STACK_OVERFLOW();
/ * Select a maximum priority task running * /
taskSELECT_HIGHEST_PRIORITY_TASK();
traceTASK_SWITCHED_IN();
#if ( configUSE_NEWLIB_REENTRANT == 1 )
// ... part
#endif /* configUSE_NEWLIB_REENTRANT */
}
}
The function of selecting a top priority task is as follows:
The essence of the task is the control block (TCB) for obtaining the task
define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
UBaseType_t uxTopPriority; \
\
/ * Find the highest leisure level * / \
portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \
configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \
/ * Get the TCB of the highest priority ready task, update to PxCurrentTCB * / \
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
} /* taskSELECT_HIGHEST_PRIORITY_TASK() */
The function of the TCB for obtaining the highest-priority ready task is implemented as follows:
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
{ \
List_t * const pxConstList = ( pxList ); \
/* Increment the index to the next item and return the item, ensuring */ \
/* we don't return the marker used at the end of the list. */ \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
{ \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
} \
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
}
The suspend dispatcher can prevent context switching, but can be interrupted. If the interrupt request context is switched when the scheduler is suspended, the request will remain suspended and the request is only executed when the scheduler (not hang) is restarted.
This function is to lock the scheduler. Each time the function is called, the variable uxscheduverSpended is self-adding, and the depth of nested nested in nested calls is used.
PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) pdFALSE;
void vTaskSuspendAll( void )
{
++uxSchedulerSuspended;
}
Each time the vtaskspendAll () function will add the UXSCHEDUSUSPENDED variable, then the corresponding xtaskresumeAll () is definitely reduced by the variable.
If the recovery scheduler causes context to switch, return pdtrue, otherwise returns PDFALSE
BaseType_t xTaskResumeAll( void )
{
TCB_t *pxTCB = NULL;
BaseType_t xAlreadyYielded = pdFALSE;
/ * If UXSCHEDULERSUSPENDED is 0, this function does not match the call to vtaskspendall () * /
configASSERT( uxSchedulerSuspended );
/ * When the scheduler is suspended, the ISR may cause the task to be removed from the event list. If this is this,
Then the deleted task will be added to the XpendingReadylist. Once the scheduler is recovered,
You can safely move all pending ready tasks from this list to their corresponding list * /
taskENTER_CRITICAL();
{
--uxSchedulerSuspended;
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
{
/ * Move anything ready to move to the ready list to the appropriate list * /
while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
{
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
prvAddTaskToReadyList( pxTCB );
/ * If the priority of the mission is higher than the current task, you need to switch between tasks * /
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
xYieldPending = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
if( pxTCB != NULL )
{
/ * When the scheduler is suspended, a task is released, which may prevent recalculation of the next release time,
In this case, it is now recalculated. This is very important for low power consumption without Tickless.
It prevents unnecessary exit from low power status * /
prvResetNextTaskUnblockTime();
}
/ * If you have any ticks when the scheduler is suspended, then you should now process them.
This ensures that the tick count does not slide, and any delayed task can restore the correct time * /
{
UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */
if( uxPendedCounts > ( UBaseType_t ) 0U )
{
do
{
/ * Find the task to be switched, if there is a task switch * /
if( xTaskIncrementTick() != pdFALSE )
{
xYieldPending = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
--uxPendedCounts;
} while( uxPendedCounts > ( UBaseType_t ) 0U );
uxPendedTicks = 0;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
if( xYieldPending != pdFALSE )
{
#if( configUSE_PREEMPTION != 0 )
{
xAlreadyYielded = pdTRUE;
}
#endif
/ * If you need task to switch, call the taskyield_if_using_preeemption () function initiate a task switch * /
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
return xAlreadyYielded;
}
Disclaimer: This column refers to the FreeRTOS tutorials of Wei Dongshan, Wildfire, Punctual Atom, and other bloggers. If there is any infringement, please let me know and delete the post immediately ...
content First, task scheduling Second, task switching Third, About Pendsv First, task scheduling After the task function is created, you need to call the function ...
This chapter is based on FreeRTOS's startup task scheduler source code analysis. Later, I will upload other source code analysis process and understanding of FreeRTOS. First, I will understand the tas...
Here is a summary of some things notified by the learning task What can task notifications do Analog Event Flag Group Simulates binary semaphores and counting semaphores, but it is not suitable for th...
A multitasking system can implement concurrent execution of multiple tasks. If it is a single-core processor, the CPU executes task A for a period of time, task B for a period of time, and task C for ...
scheduler The scheduler is to use the related scheduling algorithm to select tasks and safely switch the code that the task runs. Basic functions: (1) The scheduler can distinguish between ready tasks...
1. Preface This issue of the article explains the source code of FreeRTOS real-time operating system, mainly source code analysis, and there is very little practical operation. The previous section ta...
task The task is usually a function similar to the following code. Task status Running: Tasks occupy the CPU, and only one task is running on a single-core processor at any time. Ready: The task is re...
The FreeRTOS task scheduling is implemented by S32K144. First, create the project, and then do code analysis. Select file->new->S32DS Application Project Fill in the project name, select S32K144...
The tasks of FreeRTOS have the following states: run Running Ready Ready block Blocked Hang Suspended The states other than the operating state are collectively referred to as the non-operating state....