FreeRTOS task scheduling and task switching | FreeRTOS eight

Table of contents

illustrate:

1. Task Scheduler

1.1. Start the task scheduler function

1.2. Task scheduler implementation steps

1.3. xPortStartScheduler() function

2. Start a task

2.1. prvStartFirstTask() function

2.2. vPortSVCHandler() function

3. Task switching

3.1. Task switching triggering path

3.2. PendSV interrupt triggers task switching steps


illustrate:

About the content:

1) The following contents are mostly conceptual understanding and step analysis

2) There is no personal sample code yet. The official sample code of FreeRTOS is used.

3) If you want to transplant the code for testing, please look elsewhere. There is no personal sample code for testing in the following content.

About others:

1) Operating system: win 10

2) Platform: keil 5 mdk

3) Language: c language

4) Board: STM32 series transplanted to FreeRTOS

1. Task Scheduler

1.1. Start the task scheduler function

Name: vTaskStartScheduler(), function: start the task scheduler. When the task scheduler is started, FreeRTOS will start scheduling tasks.

1.2. Task scheduler implementation steps

1) Create an idle task: prvldelTask

As shown in Figure 1 below:

 figure 1

2) Create a software timer task: xTimerCreateTimerTask (optional, it will not be created if the macro definition is not configured)

As shown in Figure 2 below:

 figure 2

3) Turn off interrupts (will be turned on when starting the first task)

As shown in Figure 3 below:

image 3

4) Initialize global variables

As shown in Figure 4 below, because the task scheduler has just been started and there are no tasks running at this time, the waiting time is set to the maximum value of 8 F, as shown in Figure 5 below:

 Figure 4

 Figure 5

5) Initialize the time base timer of the task running time statistics function

As shown in Figure 6 below, the function is not actually implemented, but an interface is defined:

Figure 6

6) Call the function xPortStartScheduler to complete the task scheduler

As shown in Figure 7 below:

Figure 7

1.3. xPortStartScheduler() function

Function: Complete the configuration part related to the hardware framework in the startup task scheduler, and start the first task.

1) Detect whether the user’s interrupt-related configuration in the FreeRTOSConfig.h file is incorrect

As shown in Figure 8 below, there is a lot of content, and some parts are intercepted:

Figure 8

2) Configure the interrupt priority of PendSV and SysTick to the lowest priority

As shown in Figure 9 below, the priority is assigned to the register:

Figure 9

3) Call the vPortSetupTimerinterrupt() function to configure SysTick

Figure 10 below:

 Figure 10

4) Initialize the critical section nesting counter to 0

As shown in Figure 11 below:

Figure 11

5) Call the function prvStartFirstTask() to start the first task

As shown in Figure 12 below:

 Figure 12

2. Start a task

How to start a task?

        Find the highest priority task and put the register value of the highest priority task into the CPU register, which is equivalent to starting the highest priority task.

        For example: the first task to be started is A. The register value of task A is saved in the applied task stack when it is created. Take out the register value of task A stack and put it in the CPU register.

Notice:

1) When an interrupt occurs, the hardware automatically saves and restores PSR, PC (R15, program calculator PC), LR (R14, connection register), R12, R3-R0, while R4-R11 needs to be saved and restored manually. R12 (MSP main stack heap pointer-->interrupt, PSP process stack heap pointer)

2) After entering the interrupt, the hardware will force the use of the MSP pointer. At this time, the value of LR (R14, connection register) will be automatically updated to the special EXC_RETURN

2.1. prvStartFirstTask() function

Function: Initialize the environment before starting the first task, mainly to reset the MSP pointer and enable global interrupts (turn on PendSV)

What is MSP pointer?

        During the running process of the program, a certain amount of stack space is needed to save local variables and other information. When information is saved to the stack, the MCU will automatically update the SP pointer, and the ARM Cortex-M core provides two stack spaces.

MSP (main stack pointer): used by the OS kernel, exception service routines, and application code that requires all privileged access;

PSP (Process Stack Pointer): used for regular application code (when not in an exception service routine);

In FreeRTOS, MSP (main stack) is used for interrupts, and PSP (process stack) is used outside of interrupts.

1) Reset MSP initial value

As shown in Figure 13 below:

 Figure 13

2) Enable interrupts (turn on interrupts)

As shown in Figure 14 below:

 Figure 14

3) Trigger the SVC interrupt (get the current highest priority task control block pxCurrentTCP; pop the register value of the task into the CPU register; set the PSP; return to r14 and execute the first task)

As shown in Figure 15 below:

 Figure 15

2.2. vPortSVCHandler() function

1) Obtain the task stack address of the highest priority ready task through pxCurrentTCP. The highest priority ready task is the task that the system will run.

As shown in Figure 16 below:

Figure 16

2) Use the top pointer of the task stack to pop the contents of the task stack into the CPU register. The contents of the task stack have been initialized when the task function is called, and the PSP value is set.

Figure 17 below:

Figure 17

3) Set the control interrupt register value to write 0 to enable interrupts

As shown in Figure 18 below:

 Figure 18

4) R14 records the exception return value EXC_RETURN in the ISR

The legal value of the exception return value EXC_RETURN is as shown in Figure 19:

 

Figure 19

3. Task switching

What is the essence of task switching?

        Switching of CPU registers (switching to the register value of that task means running that task).

       

3.1. Task switching triggering path

The equivalent way to trigger the PnedSV interrupt is as follows:

1) Tick timer triggers

2) Trigger by calling FreeRTOS API function, for example: portYIELD()

Essentially, the PendSV interrupt is started by writing 1 to suspend PendSV to bit28 of the interrupt control and status register ICSR.

3.2. PendSV interrupt triggers task switching steps

1) The current PSP is the stack pointer of the running task. Read the current PSP process pointer and store it in r0.

2) Push the stack (also called saving the scene, starting from r0 and going down)

As shown in Figure 20 below:

 Figure 20

3) Obtain the current highest priority task control block

4) Pop the stack (also called restoring the scene, giving the register value to the CPU register)

As shown in Figure 21 below:

 Figure 21

5) Update the switched task stack pointer to PSP (that is, give r0 to PSP)

6) bx r14 executes the update task function

3.3. Task switching implementation function

Name: __asm ​​void xPortPendSVHandler(void), function: to implement task switching

The implementation steps are as follows:

1) Manual 8-byte alignment, give PSP, the value of r0

As shown in Figure 22 below:

 Figure 22

2) Get the current highest priority task

As shown in Figure 23 below:

 Figure 23

3) Get the highest priority of the current task

As shown in Figure 24 below:

  Figure 24

4) Turn on interrupt

As shown in Figure 25 below:

5) Push on the stack

As shown in Figure 26 below:

 Figure 26

6) Give the top of the stack to PSP

As shown in Figure 27 below:

 Figure 27

Guess you like

Origin blog.csdn.net/qq_57663276/article/details/128882596