mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
docs: provide CN translation for api-reference/system/freertos_idf.rst
This commit is contained in:
parent
a9f7579e0a
commit
e16134570c
@ -1,22 +1,24 @@
|
||||
FreeRTOS (ESP-IDF)
|
||||
==================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
.. ---------------------------------------------------- Overview -------------------------------------------------------
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The original FreeRTOS (hereinafter referred to as Vanilla FreeRTOS) is a small and efficient Real Time Operating System supported on many single-core MCUs and SoCs. However, to support numerous dual core ESP targets (such as the ESP32, ESP32-S3 and ESP32-P4), ESP-IDF provides a dual core SMP (Symmetric Multiprocessing) capable implementation of FreeRTOS, (hereinafter referred to as ESP-IDF FreeRTOS).
|
||||
The original FreeRTOS (hereinafter referred to as Vanilla FreeRTOS) is a compact and efficient real-time operating system supported on many single-core MCUs and SoCs. However, to support numerous dual-core ESP targets, such as ESP32, ESP32-S3, and ESP32-P4, ESP-IDF provides an implementation of FreeRTOS with dual-core symmetric multiprocessing (SMP) capabilities (hereinafter referred to as ESP-IDF FreeRTOS).
|
||||
|
||||
ESP-IDF FreeRTOS is based on Vanilla FreeRTOS v10.4.3, but contains significant modifications to both API and kernel behavior in order to support dual core SMP. This document describes the API and behavioral differences between Vanilla FreeRTOS and ESP-IDF FreeRTOS.
|
||||
ESP-IDF FreeRTOS is based on Vanilla FreeRTOS v10.4.3 but contains significant modifications to both API and kernel behavior in order to support dual-core SMP. This document describes the API and behavioral differences between Vanilla FreeRTOS and ESP-IDF FreeRTOS.
|
||||
|
||||
.. note::
|
||||
|
||||
This document assumes that the reader has a requisite understanding of Vanilla FreeRTOS (its features, behavior, and API usage). Refer to the `Vanilla FreeRTOS documentation <https://www.freertos.org/index.html>`_ for more details.
|
||||
This document assumes that the reader has a requisite understanding of Vanilla FreeRTOS, i.e., its features, behavior, and API usage. Refer to the `Vanilla FreeRTOS documentation <https://www.freertos.org/index.html>`_ for more details.
|
||||
|
||||
.. note::
|
||||
|
||||
ESP-IDF FreeRTOS can be built for a single core by enabling the :ref:`CONFIG_FREERTOS_UNICORE` configuration option. ESP targets that are single core always have the :ref:`CONFIG_FREERTOS_UNICORE` option enabled. However, note that building with :ref:`CONFIG_FREERTOS_UNICORE` enabled does not equate to building with Vanilla FreeRTOS (i.e., some of the behavioral and API changes of ESP-IDF are still present). For more details, see :ref:`freertos-smp-single-core`.
|
||||
ESP-IDF FreeRTOS can be built for a single core by enabling the :ref:`CONFIG_FREERTOS_UNICORE` configuration option. ESP targets that are single core always have the :ref:`CONFIG_FREERTOS_UNICORE` option enabled. However, note that building with :ref:`CONFIG_FREERTOS_UNICORE` enabled does not equate to building with Vanilla FreeRTOS, as some of the behavioral and API changes of ESP-IDF are still present. For more details, see :ref:`freertos-smp-single-core`.
|
||||
|
||||
This document is split into the following parts.
|
||||
|
||||
@ -32,40 +34,40 @@ Symmetric Multiprocessing
|
||||
Basic Concepts
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
SMP (Symmetric Multiprocessing) is a computing architecture where two or more identical CPUs (cores) are connected to a single shared main memory and controlled by a single operating system. In general, an SMP system...
|
||||
Symmetric multiprocessing is a computing architecture where two or more identical CPUs (cores) are connected to a single shared main memory and controlled by a single operating system. In general, an SMP system:
|
||||
|
||||
- has multiple cores running independently. Each core has its own register file, interrupts, and interrupt handling.
|
||||
- presents an identical view of memory to each core. Thus a piece of code that accesses a particular memory address has the same effect regardless of which core it runs on.
|
||||
- presents an identical view of memory to each core. Thus, a piece of code that accesses a particular memory address has the same effect regardless of which core it runs on.
|
||||
|
||||
The main advantages of an SMP system compared to single core or Asymmetric Multiprocessing systems are that...
|
||||
The main advantages of an SMP system compared to single-core or asymmetric multiprocessing systems are that:
|
||||
|
||||
- the presence of multiple CPUs allows for multiple hardware threads, thus increases overall processing throughput.
|
||||
- having symmetric memory means that threads can switch cores during execution. This in general can lead to better CPU utilization.
|
||||
- the presence of multiple CPUs allows for multiple hardware threads, thus increasing overall processing throughput.
|
||||
- having symmetric memory means that threads can switch cores during execution. This, in general, can lead to better CPU utilization.
|
||||
|
||||
Although an SMP system allows threads to switch cores, there are scenarios where a thread must/should only run on a particular core. Therefore, threads in an SMP system also have a core affinity that specifies which particular core the thread is allowed to run on.
|
||||
Although an SMP system allows threads to switch cores, there are scenarios where a thread must or should only run on a particular core. Therefore, threads in an SMP system also have a core affinity that specifies which particular core the thread is allowed to run on.
|
||||
|
||||
- A thread that is pinned to a particular core is only be able to run on that core
|
||||
- A thread that is pinned to a particular core is only able to run on that core.
|
||||
- A thread that is unpinned will be allowed to switch between cores during execution instead of being pinned to a particular core.
|
||||
|
||||
SMP on an ESP Target
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
ESP targets (such as the ESP32, ESP32-S3 and ESP32-P4) are dual core SMP SoCs. These targets have the following hardware features that make them SMP capable:
|
||||
ESP targets such as ESP32, ESP32-S3, and ESP32-P4 are dual-core SMP SoCs. These targets have the following hardware features that make them SMP-capable:
|
||||
|
||||
- Two identical cores known as CPU0 and CPU1. This means that the execution of a piece of code is identical regardless of which core it runs on.
|
||||
- Two identical cores are known as CPU0 and CPU1. This means that the execution of a piece of code is identical regardless of which core it runs on.
|
||||
- Symmetric memory (with some small exceptions).
|
||||
|
||||
- If multiple cores access the same memory address, their access will be serialized at the memory bus level.
|
||||
- If multiple cores access the same memory address simultaneously, their access will be serialized by the memory bus.
|
||||
- True atomic access to the same memory address is achieved via an atomic compare-and-swap instruction provided by the ISA.
|
||||
|
||||
- Cross-core interrupts that allow one CPU to trigger and interrupt on another CPU. This allows cores to signal each other.
|
||||
- Cross-core interrupts that allow one CPU to trigger an interrupt on another CPU. This allows cores to signal each other.
|
||||
|
||||
|
||||
.. only:: not esp32p4
|
||||
|
||||
.. note::
|
||||
|
||||
CPU0 is also known as Protocol CPU or ``PRO_CPU`` and CPU1 is also known as Application CPU or ``APP_CPU``. The ``PRO_CPU`` and ``APP_CPU`` aliases for CPU0 and CPU1 exist in ESP-IDF as they reflect how typical ESP-IDF applications utilize the two CPUs. Typically, the tasks responsible for handling wireless networking (e.g., Wi-Fi or Bluetooth) are pinned to CPU0 (thus the name PRO_CPU), whereas the tasks handling the remainder of the application are pinned to CPU1 (thus the name APP_CPU).
|
||||
CPU0 is also known as Protocol CPU or ``PRO_CPU`` and CPU1 is also known as Application CPU or ``APP_CPU``. The ``PRO_CPU`` and ``APP_CPU`` aliases for CPU0 and CPU1 exist in ESP-IDF as they reflect how typical ESP-IDF applications utilize the two CPUs. Typically, the tasks responsible for handling wireless networking (e.g., Wi-Fi or Bluetooth) are pinned to CPU0, thus the name ``PRO_CPU``; whereas the tasks handling the remainder of the application are pinned to CPU1, thus the name ``APP_CPU``.
|
||||
|
||||
|
||||
.. ------------------------------------------------------ Tasks --------------------------------------------------------
|
||||
@ -78,58 +80,54 @@ Creation
|
||||
|
||||
Vanilla FreeRTOS provides the following functions to create a task:
|
||||
|
||||
- :cpp:func:`xTaskCreate` creates a task. The task's memory is dynamically allocated
|
||||
- :cpp:func:`xTaskCreateStatic` creates a task. The task's memory is statically allocated (i.e., provided by the user)
|
||||
- :cpp:func:`xTaskCreate` creates a task. The task's memory is dynamically allocated.
|
||||
- :cpp:func:`xTaskCreateStatic` creates a task. The task's memory is statically allocated, i.e., provided by the user.
|
||||
|
||||
However, in an SMP system, tasks need to be assigned a particular affinity. Therefore, ESP-IDF provides a ``PinnedToCore`` version of Vanilla FreeRTOS's task creation functions:
|
||||
|
||||
- :cpp:func:`xTaskCreatePinnedToCore` creates a task with a particular core affinity. The task's memory is dynamically allocated.
|
||||
- :cpp:func:`xTaskCreateStaticPinnedToCore` creates a task with a particular core affinity. The task's memory is statically allocated (i.e., provided by the user)
|
||||
- :cpp:func:`xTaskCreateStaticPinnedToCore` creates a task with a particular core affinity. The task's memory is statically allocated, i.e., provided by the user.
|
||||
|
||||
The ``PinnedToCore`` versions of the task creation functions API differ from their vanilla counter parts by having an extra ``xCoreID`` parameter that is used to specify the created task's core affinity. The valid values for core affinity are:
|
||||
The ``PinnedToCore`` versions of the task creation function API differ from their vanilla counterparts by having an extra ``xCoreID`` parameter that is used to specify the created task's core affinity. The valid values for core affinity are:
|
||||
|
||||
- ``0`` which pins the created task to CPU0
|
||||
- ``1`` which pins the created task to CPU1
|
||||
- ``tskNO_AFFINITY`` which allows the task to be run on both CPUs
|
||||
- ``0``, which pins the created task to CPU0
|
||||
- ``1``, which pins the created task to CPU1
|
||||
- ``tskNO_AFFINITY``, which allows the task to be run on both CPUs
|
||||
|
||||
Note that ESP-IDF FreeRTOS still supports the vanilla versions of the task creation functions. However, they have been modified to simply call their ``PinnedToCore`` counterparts with ``tskNO_AFFINITY``.
|
||||
Note that ESP-IDF FreeRTOS still supports the vanilla versions of the task creation functions. However, these standard functions have been modified to essentially invoke their respective ``PinnedToCore`` counterparts while setting the core affinity to ``tskNO_AFFINITY``.
|
||||
|
||||
.. note::
|
||||
|
||||
ESP-IDF FreeRTOS also changes the units of ``ulStackDepth`` in the task creation functions. Task stack sizes in Vanilla FreeRTOS are specified in number of words, whereas in ESP-IDF FreeRTOS, the task stack sizes are specified in bytes.
|
||||
ESP-IDF FreeRTOS also changes the units of ``ulStackDepth`` in the task creation functions. Task stack sizes in Vanilla FreeRTOS are specified in a number of words, whereas in ESP-IDF FreeRTOS, the task stack sizes are specified in bytes.
|
||||
|
||||
Execution
|
||||
^^^^^^^^^
|
||||
|
||||
The anatomy of a task in ESP-IDF FreeRTOS is the same as Vanilla FreeRTOS. More specifically, ESP-IDF FreeRTOS tasks:
|
||||
The anatomy of a task in ESP-IDF FreeRTOS is the same as in Vanilla FreeRTOS. More specifically, ESP-IDF FreeRTOS tasks:
|
||||
|
||||
- Can only be in one of following states: Running, Ready, Blocked, or Suspended.
|
||||
- Task functions are typically implemented as an infinite loop
|
||||
- Task functions should never return
|
||||
- Can only be in one of the following states: Running, Ready, Blocked, or Suspended.
|
||||
- Task functions are typically implemented as an infinite loop.
|
||||
- Task functions should never return.
|
||||
|
||||
Deletion
|
||||
^^^^^^^^
|
||||
|
||||
Task deletion in Vanilla FreeRTOS is called via :cpp:func:`vTaskDelete`. The function allows deletion of another task or the currently running task (if the provided task handle is ``NULL``). The actual freeing of the task's memory is sometimes delegated to the idle task (if the task being deleted is the currently running task).
|
||||
Task deletion in Vanilla FreeRTOS is called via :cpp:func:`vTaskDelete`. The function allows deletion of another task or the currently running task if the provided task handle is ``NULL``. The actual freeing of the task's memory is sometimes delegated to the idle task if the task being deleted is the currently running task.
|
||||
|
||||
ESP-IDF FreeRTOS provides the same :cpp:func:`vTaskDelete` function. However, due to the dual core nature, there are some behavioral differences when calling :cpp:func:`vTaskDelete` in ESP-IDF FreeRTOS:
|
||||
ESP-IDF FreeRTOS provides the same :cpp:func:`vTaskDelete` function. However, due to the dual-core nature, there are some behavioral differences when calling :cpp:func:`vTaskDelete` in ESP-IDF FreeRTOS:
|
||||
|
||||
- When deleting a task that is pinned to the other core, that task's memory is always freed by the idle task of the other core (due to the need to clear FPU registers).
|
||||
- When deleting a task that is currently running on the other core, an yield is triggered on the other core and the task's memory is freed by one of the idle tasks (depending on the task's core affinity)
|
||||
- A deleted task's memory is freed immediately if...
|
||||
- When deleting a task that is currently running on the other core, a yield is triggered on the other core, and the task's memory is freed by one of the idle tasks.
|
||||
- A deleted task's memory is freed immediately if it is not running on either core.
|
||||
|
||||
- The tasks is currently running on this core and is also pinned to this core
|
||||
- The task is not currently running and is not pinned to any core
|
||||
Please avoid deleting a task that is running on another core as it is difficult to determine what the task is performing, which may lead to unpredictable behavior such as:
|
||||
|
||||
Users should avoid calling :cpp:func:`vTaskDelete` on a task that is currently running on the other core. This is due to the fact that it is difficult to know what the task currently running on the other core is executing, thus can lead to unpredictable behavior such as...
|
||||
- Deleting a task that is holding a mutex.
|
||||
- Deleting a task that has yet to free memory it previously allocated.
|
||||
|
||||
- Deleting a task that is holding a mutex
|
||||
- Deleting a task that has yet to free memory it previously allocated
|
||||
Where possible, please design your own application so that when calling :cpp:func:`vTaskDelete`, the deleted task is in a known state. For example:
|
||||
|
||||
Where possible, users should design their application such that :cpp:func:`vTaskDelete` is only ever called on tasks in a known state. For example:
|
||||
|
||||
- Tasks self deleting (via ``vTaskDelete(NULL)``) when their execution is complete and have also cleaned up all resources used within the task.
|
||||
- Tasks placing themselves in the suspend state (via :cpp:func:`vTaskSuspend`) before being deleted by another task.
|
||||
- Tasks self-deleting via ``vTaskDelete(NULL)`` when their execution is complete and have also cleaned up all resources used within the task.
|
||||
- Tasks placing themselves in the suspend state via :cpp:func:`vTaskSuspend` before being deleted by another task.
|
||||
|
||||
|
||||
.. --------------------------------------------------- Scheduling ------------------------------------------------------
|
||||
@ -137,36 +135,36 @@ Where possible, users should design their application such that :cpp:func:`vTask
|
||||
SMP Scheduler
|
||||
-------------
|
||||
|
||||
The Vanilla FreeRTOS scheduler is best described as a **Fixed Priority Preemptive scheduler with Time Slicing** meaning that:
|
||||
The Vanilla FreeRTOS scheduler is best described as a **fixed priority preemptive scheduler with time slicing** meaning that:
|
||||
|
||||
- Each tasks is given a constant priority upon creation. The scheduler executes highest priority ready state task
|
||||
- The scheduler can switch execution to another task without the cooperation of the currently running task
|
||||
- The scheduler periodically switches execution between ready state tasks of the same priority (in a round robin fashion). Time slicing is governed by a tick interrupt.
|
||||
- Each task is given a constant priority upon creation. The scheduler executes the highest priority ready-state task.
|
||||
- The scheduler can switch execution to another task without the cooperation of the currently running task.
|
||||
- The scheduler periodically switches execution between ready-state tasks of the same priority in a round-robin fashion. Time slicing is governed by a tick interrupt.
|
||||
|
||||
The ESP-IDF FreeRTOS scheduler supports the same scheduling features (i.e., Fixed Priority, Preemption, and Time Slicing) albeit with some small behavioral differences.
|
||||
The ESP-IDF FreeRTOS scheduler supports the same scheduling features, i.e., Fixed Priority, Preemption, and Time Slicing, albeit with some small behavioral differences.
|
||||
|
||||
Fixed Priority
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
In Vanilla FreeRTOS, when scheduler selects a new task to run, it always selects the current highest priority ready state task. In ESP-IDF FreeRTOS, each core independently schedules tasks to run. When a particular core selects a task, the core will select the highest priority ready state task that can be run by the core. A task can be run by the core if:
|
||||
In Vanilla FreeRTOS, when the scheduler selects a new task to run, it always selects the current highest priority ready-state task. In ESP-IDF FreeRTOS, each core independently schedules tasks to run. When a particular core selects a task, the core will select the highest priority ready-state task that can be run by the core. A task can be run by the core if:
|
||||
|
||||
- The task has a compatible affinity (i.e., is either pinned to that core or is unpinned)
|
||||
- The task is not currently being run by another core
|
||||
- The task has a compatible affinity, i.e., is either pinned to that core or is unpinned.
|
||||
- The task is not currently being run by another core.
|
||||
|
||||
However, users should not assume that the two highest priority ready state tasks are always run by the scheduler as a task's core affinity must also be accounted for. For example, given the following tasks:
|
||||
However, please do not assume that the two highest priority ready-state tasks are always run by the scheduler, as a task's core affinity must also be accounted for. For example, given the following tasks:
|
||||
|
||||
- Task A of priority 10 pinned to CPU0
|
||||
- Task B of priority 9 pinned to CPU0
|
||||
- Task C of priority 8 pinned to CPU1
|
||||
|
||||
The resulting schedule will have Task A running on CPU0 and Task C running on CPU1. Task B is not run even though it is the second highest priority task.
|
||||
The resulting schedule will have Task A running on CPU0 and Task C running on CPU1. Task B is not run even though it is the second-highest priority task.
|
||||
|
||||
Preemption
|
||||
^^^^^^^^^^
|
||||
|
||||
In Vanilla FreeRTOS, the scheduler can preempt the currently running task if a higher priority task becomes ready to execute. Likewise in ESP-IDF FreeRTOS, each core can be individually preempted by the scheduler if the scheduler determines that a higher priority task can run on that core.
|
||||
In Vanilla FreeRTOS, the scheduler can preempt the currently running task if a higher priority task becomes ready to execute. Likewise in ESP-IDF FreeRTOS, each core can be individually preempted by the scheduler if the scheduler determines that a higher-priority task can run on that core.
|
||||
|
||||
However, there are some instances where a higher priority task that becomes ready can be run on multiple cores. In this case, the scheduler only preempts one core. The scheduler always gives preference to the current core when multiple cores can be preempted. In other words, if the higher priority ready task is unpinned and has a higher priority than the current priority of both cores, the scheduler will always choose to preempt the current core. For example, given the following tasks:
|
||||
However, there are some instances where a higher-priority task that becomes ready can be run on multiple cores. In this case, the scheduler only preempts one core. The scheduler always gives preference to the current core when multiple cores can be preempted. In other words, if the higher priority ready task is unpinned and has a higher priority than the current priority of both cores, the scheduler will always choose to preempt the current core. For example, given the following tasks:
|
||||
|
||||
- Task A of priority 8 currently running on CPU0
|
||||
- Task B of priority 9 currently running on CPU1
|
||||
@ -177,37 +175,40 @@ The resulting schedule will have Task A running on CPU0 and Task C preempting Ta
|
||||
Time Slicing
|
||||
^^^^^^^^^^^^
|
||||
|
||||
The Vanilla FreeRTOS scheduler implements time slicing meaning that if current highest ready priority contains multiple ready tasks, the scheduler will switch between those tasks periodically in a round robin fashion.
|
||||
The Vanilla FreeRTOS scheduler implements time slicing, which means that if the current highest ready priority contains multiple ready tasks, the scheduler will switch between those tasks periodically in a round-robin fashion.
|
||||
|
||||
However, in ESP-IDF FreeRTOS, it is not possible to implement perfect Round Robin time slicing due to the fact that a particular task may not be able to run on a particular core due to the following reasons:
|
||||
|
||||
- The task is pinned to the another core.
|
||||
- The task is pinned to another core.
|
||||
- For unpinned tasks, the task is already being run by another core.
|
||||
|
||||
Therefore, when a core searches the ready state task list for a task to run, the core may need to skip over a few tasks in the same priority list or drop to a lower priority in order to find a ready state task that the core can run.
|
||||
Therefore, when a core searches the ready-state task list for a task to run, the core may need to skip over a few tasks in the same priority list or drop to a lower priority in order to find a ready-state task that the core can run.
|
||||
|
||||
The ESP-IDF FreeRTOS scheduler implements a Best Effort Round Robin time slicing for ready state tasks of the same priority by ensuring that tasks that have been selected to run are placed at the back of the list, thus giving unselected tasks a higher priority on the next scheduling iteration (i.e., the next tick interrupt or yield)
|
||||
The ESP-IDF FreeRTOS scheduler implements a Best Effort Round Robin time slicing for ready-state tasks of the same priority by ensuring that tasks that have been selected to run are placed at the back of the list, thus giving unselected tasks a higher priority on the next scheduling iteration (i.e., the next tick interrupt or yield).
|
||||
|
||||
The following example demonstrates the Best Effort Round Robin time slicing in action. Assume that:
|
||||
|
||||
- There are four ready state tasks of the same priority ``AX, B0, C1, D1`` where:
|
||||
- The priority is the current highest priority with ready state tasks
|
||||
- The first character represents the task's names (i.e., ``A, B, C, D``)
|
||||
- And the second character represents the tasks core pinning (and ``X`` means unpinned)
|
||||
- The task list is always searched from the head
|
||||
- There are four ready-state tasks of the same priority ``AX``, ``B0``, ``C1``, and ``D1`` where:
|
||||
|
||||
- The priority is the current highest priority with ready-state .
|
||||
- The first character represents the task's name, i.e., ``A``, ``B``, ``C``, ``D``.
|
||||
- The second character represents the task's core pinning, and ``X`` means unpinned.
|
||||
|
||||
- The task list is always searched from the head.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1. Starting state. None of the ready state tasks have been selected to run
|
||||
1. Starting state. None of the ready-state tasks have been selected to run.
|
||||
|
||||
Head [ AX , B0 , C1 , D0 ] Tail
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1. Core 0 has tick interrupt and searches for a task to run.
|
||||
Task A is selected and is moved to the back of the list
|
||||
2. Core 0 has a tick interrupt and searches for a task to run.
|
||||
|
||||
Task A is selected and moved to the back of the list.
|
||||
|
||||
Core0--|
|
||||
Head [ AX , B0 , C1 , D0 ] Tail
|
||||
@ -217,9 +218,11 @@ The following example demonstrates the Best Effort Round Robin time slicing in a
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1. Core 1 has a tick interrupt and searches for a task to run.
|
||||
3. Core 1 has a tick interrupt and searches for a task to run.
|
||||
|
||||
Task B cannot be run due to incompatible affinity, so core 1 skips to Task C.
|
||||
Task C is selected and is moved to the back of the list
|
||||
|
||||
Task C is selected and moved to the back of the list.
|
||||
|
||||
Core1-------| 0
|
||||
Head [ B0 , C1 , D0 , AX ] Tail
|
||||
@ -229,8 +232,9 @@ The following example demonstrates the Best Effort Round Robin time slicing in a
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1. Core 0 has another tick interrupt and searches for a task to run.
|
||||
Task B is selected and moved to the back of the list
|
||||
4. Core 0 has another tick interrupt and searches for a task to run.
|
||||
|
||||
Task B is selected and moved to the back of the list.
|
||||
|
||||
|
||||
Core0--| 1
|
||||
@ -241,9 +245,11 @@ The following example demonstrates the Best Effort Round Robin time slicing in a
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1. Core 1 has another tick and searches for a task to run.
|
||||
Task D cannot be run due to incompatible affinity, so core 1 skips to Task A
|
||||
Task A is selected and moved to the back of the list
|
||||
5. Core 1 has another tick and searches for a task to run.
|
||||
|
||||
Task D cannot be run due to incompatible affinity, so core 1 skips to Task A.
|
||||
|
||||
Task A is selected and moved to the back of the list.
|
||||
|
||||
Core1-------| 0
|
||||
Head [ D0 , AX , C1 , B0 ] Tail
|
||||
@ -254,10 +260,10 @@ The following example demonstrates the Best Effort Round Robin time slicing in a
|
||||
|
||||
The implications to users regarding the Best Effort Round Robin time slicing:
|
||||
|
||||
- Users cannot expect multiple ready state tasks of the same priority to run sequentially (as is the case in Vanilla FreeRTOS). As demonstrated in the example above, a core may need to skip over tasks.
|
||||
- Users cannot expect multiple ready-state tasks of the same priority to run sequentially as is the case in Vanilla FreeRTOS. As demonstrated in the example above, a core may need to skip over tasks.
|
||||
- However, given enough ticks, a task will eventually be given some processing time.
|
||||
- If a core cannot find a task runnable task at the highest ready state priority, it will drop to a lower priority to search for tasks.
|
||||
- To achieve ideal round robin time slicing, users should ensure that all tasks of a particular priority are pinned to the same core.
|
||||
- If a core cannot find a task runnable task at the highest ready-state priority, it will drop to a lower priority to search for tasks.
|
||||
- To achieve ideal round-robin time slicing, users should ensure that all tasks of a particular priority are pinned to the same core.
|
||||
|
||||
Tick Interrupts
|
||||
^^^^^^^^^^^^^^^
|
||||
@ -266,17 +272,17 @@ Vanilla FreeRTOS requires that a periodic tick interrupt occurs. The tick interr
|
||||
|
||||
- Incrementing the scheduler's tick count
|
||||
- Unblocking any blocked tasks that have timed out
|
||||
- Checking if time slicing is required (i.e., triggering a context switch)
|
||||
- Checking if time slicing is required, i.e., triggering a context switch
|
||||
- Executing the application tick hook
|
||||
|
||||
In ESP-IDF FreeRTOS, each core receives a periodic interrupt and independently run the tick interrupt. The tick interrupts on each core are of the same period but can be out of phase. However, the tick responsibilities listed above are not run by all cores:
|
||||
In ESP-IDF FreeRTOS, each core receives a periodic interrupt and independently runs the tick interrupt. The tick interrupts on each core are of the same period but can be out of phase. However, the tick responsibilities listed above are not run by all cores:
|
||||
|
||||
- CPU0 executes all of the tick interrupt responsibilities listed above
|
||||
- CPU1 only checks for time slicing and execute the application tick hook
|
||||
- CPU1 only checks for time slicing and executes the application tick hook
|
||||
|
||||
.. note::
|
||||
|
||||
CPU0 is solely responsible for keeping time in ESP-IDF FreeRTOS. Therefore anything that prevents CPU0 from incrementing the tick count (such as suspending the scheduler on CPU0) causes the entire schedulers time keeping to lag behind.
|
||||
CPU0 is solely responsible for keeping time in ESP-IDF FreeRTOS. Therefore, anything that prevents CPU0 from incrementing the tick count, such as suspending the scheduler on CPU0, will cause the entire scheduler's timekeeping to lag behind.
|
||||
|
||||
Idle Tasks
|
||||
^^^^^^^^^^
|
||||
@ -295,22 +301,22 @@ Vanilla FreeRTOS allows the scheduler to be suspended/resumed by calling :cpp:fu
|
||||
|
||||
- Task switching is disabled but interrupts are left enabled.
|
||||
- Calling any blocking/yielding function is forbidden, and time slicing is disabled.
|
||||
- The tick count is frozen (but the tick interrupt still occurs to execute the application tick hook)
|
||||
- The tick count is frozen, but the tick interrupt still occurs to execute the application tick hook.
|
||||
|
||||
On scheduler resumption, :cpp:func:`xTaskResumeAll` catches up all of the lost ticks and unblock any timed out tasks.
|
||||
On scheduler resumption, :cpp:func:`xTaskResumeAll` catches up all of the lost ticks and unblock any timed-out tasks.
|
||||
|
||||
In ESP-IDF FreeRTOS, suspending the scheduler across multiple cores is not possible. Therefore when :cpp:func:`vTaskSuspendAll` is called on a particular core (e.g., core A):
|
||||
|
||||
- Task switching is disabled only on core A but interrupts for core A are left enabled
|
||||
- Task switching is disabled only on core A but interrupts for core A are left enabled.
|
||||
- Calling any blocking/yielding function on core A is forbidden. Time slicing is disabled on core A.
|
||||
- If an interrupt on core A unblocks any tasks, tasks with affinity to core A will go into core A's own pending ready task list. Unpinned tasks or tasks with affinity to other cores can be scheduled on cores with the scheduler running.
|
||||
- In case the scheduler is suspended on all cores, tasks unblocked by an interrupt will go to the pending ready task lists of their pinned cores or to the pending ready list of the core on which the interrupt is called if the tasks are unpinned.
|
||||
- If core A is CPU0, the tick count is frozen and a pended tick count is incremented instead. However, the tick interrupt will still occur in order to execute the application tick hook.
|
||||
- If the scheduler is suspended on all cores, tasks unblocked by an interrupt will be directed to the pending ready task lists of their pinned cores. For unpinned tasks, they will be placed in the pending ready list of the core where the interrupt occurred.
|
||||
- If core A is CPU0, the tick count is frozen, and a pended tick count is incremented instead. However, the tick interrupt will still occur in order to execute the application tick hook.
|
||||
|
||||
When :cpp:func:`xTaskResumeAll` is called on a particular core (e.g., core A):
|
||||
|
||||
- Any tasks added to core A's pending ready task list will be resumed
|
||||
- If core A is CPU0, the pended tick count is unwound to catch up the lost ticks.
|
||||
- Any tasks added to core A's pending ready task list will be resumed.
|
||||
- If core A is CPU0, the pended tick count is unwound to catch up with the lost ticks.
|
||||
|
||||
.. warning::
|
||||
|
||||
@ -321,22 +327,24 @@ Disabling Interrupts
|
||||
|
||||
Vanilla FreeRTOS allows interrupts to be disabled and enabled by calling :c:macro:`taskDISABLE_INTERRUPTS` and :c:macro:`taskENABLE_INTERRUPTS` respectively.
|
||||
|
||||
ESP-IDF FreeRTOS provides the same API, however interrupts are only disabled or enabled on the current core.
|
||||
ESP-IDF FreeRTOS provides the same API, however, interrupts are only disabled or enabled on the current core.
|
||||
|
||||
.. warning::
|
||||
|
||||
Disabling interrupts is a valid method of achieve mutual exclusion in Vanilla FreeRTOS (and single core systems in general). However, in an SMP system, disabling interrupts is **NOT** a valid method ensuring mutual exclusion. Refer to Critical Sections for more details.
|
||||
Disabling interrupts is a valid method of achieving mutual exclusion in Vanilla FreeRTOS (and single-core systems in general). However, in an SMP system, disabling interrupts is **NOT** a valid method ensuring mutual exclusion. Refer to :ref:`critical-sections` for more details.
|
||||
|
||||
|
||||
.. ------------------------------------------------ Critical Sections --------------------------------------------------
|
||||
|
||||
.. _critical-sections:
|
||||
|
||||
Critical Sections
|
||||
-----------------
|
||||
|
||||
API Changes
|
||||
^^^^^^^^^^^
|
||||
|
||||
Vanilla FreeRTOS implements critical sections by disabling interrupts, This prevents preemptive context switches and the servicing of ISRs during a critical section. Thus a task/ISR that enters a critical section is guaranteed to be the sole entity to access a shared resource. Critical sections in Vanilla FreeRTOS have the following API:
|
||||
Vanilla FreeRTOS implements critical sections by disabling interrupts, which prevents preemptive context switches and the servicing of ISRs during a critical section. Thus a task/ISR that enters a critical section is guaranteed to be the sole entity to access a shared resource. Critical sections in Vanilla FreeRTOS have the following API:
|
||||
|
||||
- ``taskENTER_CRITICAL()`` enters a critical section by disabling interrupts
|
||||
- ``taskEXIT_CRITICAL()`` exits a critical section by reenabling interrupts
|
||||
@ -353,11 +361,11 @@ However, in an SMP system, merely disabling interrupts does not constitute a cri
|
||||
|
||||
.. note::
|
||||
|
||||
The critical section API can be called recursively (i.e., nested critical sections). Entering a critical section multiple times recursively is valid so long as the critical section is exited the same number of times it was entered. However, given that critical sections can target different spinlocks, users should take care to avoid dead locking when entering critical sections recursively.
|
||||
The critical section API can be called recursively, i.e., nested critical sections. Entering a critical section multiple times recursively is valid so long as the critical section is exited the same number of times it was entered. However, given that critical sections can target different spinlocks, users should take care to avoid deadlocking when entering critical sections recursively.
|
||||
|
||||
Spinlocks can be allocated statically or dynamically. As such, macros are provided for both static and dynamic initialization of spinlocks, as demonstrated by the following code snippets.
|
||||
|
||||
- Allocating a static spinlock and initializing it using ``portMUX_INITIALIZER_UNLOCKED``
|
||||
- Allocating a static spinlock and initializing it using ``portMUX_INITIALIZER_UNLOCKED``:
|
||||
|
||||
.. code:: c
|
||||
|
||||
@ -371,7 +379,7 @@ Spinlocks can be allocated statically or dynamically. As such, macros are provid
|
||||
taskEXIT_CRITICAL(&my_spinlock);
|
||||
}
|
||||
|
||||
- Allocating a dynamic spinlock and initializing it using ``portMUX_INITIALIZE()``
|
||||
- Allocating a dynamic spinlock and initializing it using ``portMUX_INITIALIZE()``:
|
||||
|
||||
.. code:: c
|
||||
|
||||
@ -391,26 +399,26 @@ Implementation
|
||||
|
||||
In ESP-IDF FreeRTOS, the process of a particular core entering and exiting a critical section is as follows:
|
||||
|
||||
- For ``taskENTER_CRITICAL(&spinlock)`` (or ``taskENTER_CRITICAL_ISR(&spinlock)``)
|
||||
- For ``taskENTER_CRITICAL(&spinlock)`` or ``taskENTER_CRITICAL_ISR(&spinlock)``
|
||||
|
||||
#. The core disables its interrupts (or interrupt nesting) up to ``configMAX_SYSCALL_INTERRUPT_PRIORITY``
|
||||
#. The core disables its interrupts or interrupt nesting up to ``configMAX_SYSCALL_INTERRUPT_PRIORITY``.
|
||||
#. The core then spins on the spinlock using an atomic compare-and-set instruction until it acquires the lock. A lock is acquired when the core is able to set the lock's owner value to the core's ID.
|
||||
#. Once the spinlock is acquired, the function returns. The remainder of the critical section runs with interrupts (or interrupt nesting) disabled.
|
||||
#. Once the spinlock is acquired, the function returns. The remainder of the critical section runs with interrupts or interrupt nesting disabled.
|
||||
|
||||
- For ``taskEXIT_CRITICAL(&spinlock)`` (or ``taskEXIT_CRITICAL_ISR(&spinlock)``)
|
||||
- For ``taskEXIT_CRITICAL(&spinlock)`` or ``taskEXIT_CRITICAL_ISR(&spinlock)``
|
||||
|
||||
#. The core releases the spinlock by clearing the spinlock's owner value
|
||||
#. The core re-enables interrupts (or interrupt nesting)
|
||||
#. The core releases the spinlock by clearing the spinlock's owner value.
|
||||
#. The core re-enables interrupts or interrupt nesting.
|
||||
|
||||
Restrictions and Considerations
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Given that interrupts (or interrupt nesting) are disabled during a critical section, there are multiple restrictions regarding what can be done within a critical sections. During a critical section, users should keep the following restrictions and considerations in mind:
|
||||
Given that interrupts (or interrupt nesting) are disabled during a critical section, there are multiple restrictions regarding what can be done within critical sections. During a critical section, users should keep the following restrictions and considerations in mind:
|
||||
|
||||
- Critical sections should be as kept as short as possible
|
||||
- Critical sections should be kept as short as possible
|
||||
|
||||
- The longer the critical section lasts, the longer a pending interrupt can be delayed.
|
||||
- A typical critical section should only access a few data structures and/or hardware registers
|
||||
- A typical critical section should only access a few data structures and/or hardware registers.
|
||||
- If possible, defer as much processing and/or event handling to the outside of critical sections.
|
||||
|
||||
- FreeRTOS API should not be called from within a critical section
|
||||
@ -429,17 +437,17 @@ Misc
|
||||
|
||||
Usually, when a context switch occurs:
|
||||
|
||||
- the current state of a CPU's registers are saved to the stack of task being switch out
|
||||
- the previously saved state of the CPU's registers are loaded from the stack of the task being switched in
|
||||
- the current state of a CPU's registers is saved to the stack of the task being switched out
|
||||
- the previously saved state of the CPU's registers is loaded from the stack of the task being switched in
|
||||
|
||||
However, ESP-IDF FreeRTOS implements Lazy Context Switching for the FPU (Floating Point Unit) registers of a CPU. In other words, when a context switch occurs on a particular core (e.g., CPU0), the state of the core's FPU registers are not immediately saved to the stack of the task getting switched out (e.g., Task A). The FPU's registers are left untouched until:
|
||||
However, ESP-IDF FreeRTOS implements Lazy Context Switching for the Floating Point Unit (FPU) registers of a CPU. In other words, when a context switch occurs on a particular core (e.g., CPU0), the state of the core's FPU registers is not immediately saved to the stack of the task getting switched out (e.g., Task A). The FPU registers are left untouched until:
|
||||
|
||||
- A different task (e.g., Task B) runs on the same core and uses the FPU. This will trigger an exception that saves the FPU registers to Task A's stack.
|
||||
- Task A get's scheduled to the same core and continues execution. Saving and restoring the FPU's registers is not necessary in this case.
|
||||
- A different task (e.g., Task B) runs on the same core and uses FPU. This will trigger an exception that saves the FPU registers to Task A's stack.
|
||||
- Task A gets scheduled to the same core and continues execution. Saving and restoring the FPU registers is not necessary in this case.
|
||||
|
||||
However, given that tasks can be unpinned thus can be scheduled on different cores (e.g., Task A switches to CPU1), it is unfeasible to copy and restore the FPU's registers across cores. Therefore, when a task utilizes the FPU (by using a ``float`` type in its call flow), ESP-IDF FreeRTOS will automatically pin the task to the current core it is running on. This ensures that all tasks that uses the FPU are always pinned to a particular core.
|
||||
However, given that tasks can be unpinned and thus can be scheduled on different cores (e.g., Task A switches to CPU1), it is unfeasible to copy and restore the FPU registers across cores. Therefore, when a task utilizes FPU by using a ``float`` type in its call flow, ESP-IDF FreeRTOS will automatically pin the task to the current core it is running on. This ensures that all tasks that use FPU are always pinned to a particular core.
|
||||
|
||||
Furthermore, ESP-IDF FreeRTOS by default does not support the usage of the FPU within an interrupt context given that the FPU's register state is tied to a particular task.
|
||||
Furthermore, ESP-IDF FreeRTOS by default does not support the usage of FPU within an interrupt context given that the FPU register state is tied to a particular task.
|
||||
|
||||
.. only: esp32
|
||||
|
||||
@ -449,7 +457,7 @@ Misc
|
||||
|
||||
.. note::
|
||||
|
||||
ESP targets that contain an FPU do not support hardware acceleration for double precision floating point arithmetic (``double``). Instead ``double`` is implemented via software hence the behavioral restrictions regarding the ``float`` type do not apply to ``double``. Note that due to the lack of hardware acceleration, ``double`` operations may consume significantly more CPU time in comparison to ``float``.
|
||||
ESP targets that contain an FPU do not support hardware acceleration for double precision floating point arithmetic (``double``). Instead, ``double`` is implemented via software, hence the behavioral restrictions regarding the ``float`` type do not apply to ``double``. Note that due to the lack of hardware acceleration, ``double`` operations may consume significantly more CPU time in comparison to ``float``.
|
||||
|
||||
|
||||
.. -------------------------------------------------- Single Core -----------------------------------------------------
|
||||
@ -459,20 +467,20 @@ Misc
|
||||
ESP-IDF FreeRTOS Single Core
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Although ESP-IDF FreeRTOS is an SMP scheduler, some ESP targets are single core (such as the ESP32-S2 and ESP32-C3). When building ESP-IDF applications for these targets, ESP-IDF FreeRTOS is still used but the number of cores will be set to ``1`` (i.e., the :ref:`CONFIG_FREERTOS_UNICORE` will always be enabled for single core targets).
|
||||
Although ESP-IDF FreeRTOS is an SMP scheduler, some ESP targets are single-core (such as ESP32-S2 and ESP32-C3). When building ESP-IDF applications for these targets, ESP-IDF FreeRTOS is still used but the number of cores will be set to ``1`` (i.e., the :ref:`CONFIG_FREERTOS_UNICORE` will always be enabled for single-core targets).
|
||||
|
||||
For multicore targets (such as the ESP32 and ESP32-S3), :ref:`CONFIG_FREERTOS_UNICORE` can also be set. This results in ESP-IDF FreeRTOS only running on CPU0, and all other cores will be inactive.
|
||||
For multicore targets (such as ESP32 and ESP32-S3), :ref:`CONFIG_FREERTOS_UNICORE` can also be set. This results in ESP-IDF FreeRTOS only running on CPU0, and all other cores will be inactive.
|
||||
|
||||
.. note::
|
||||
|
||||
Users should bear in mind that enabling :ref:`CONFIG_FREERTOS_UNICORE` **is NOT equivalent to running Vanilla FreeRTOS**. The additional API of ESP-IDF FreeRTOS can still be called, and the behavior changes of ESP-IDF FreeRTOS incurs a small amount of overhead even when compiled for only a single core.
|
||||
Users should bear in mind that enabling :ref:`CONFIG_FREERTOS_UNICORE` **is NOT equivalent to running Vanilla FreeRTOS**. The additional APIs of ESP-IDF FreeRTOS can still be called, and the behavior changes of ESP-IDF FreeRTOS incur a small amount of overhead even when compiled for only a single core.
|
||||
|
||||
.. ------------------------------------------------- API References ----------------------------------------------------
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
This section contains documentation of FreeRTOS types, functions, and macros. It is automatically generated from FreeRTOS header files.
|
||||
This section introduces FreeRTOS types, functions, and macros. It is automatically generated from FreeRTOS header files.
|
||||
|
||||
Task API
|
||||
^^^^^^^^
|
||||
|
@ -1 +1,518 @@
|
||||
.. include:: ../../../en/api-reference/system/freertos_idf.rst
|
||||
FreeRTOS (ESP-IDF)
|
||||
==================
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
.. ---------------------------------------------------- Overview -------------------------------------------------------
|
||||
|
||||
概述
|
||||
----
|
||||
|
||||
原始 FreeRTOS(下文称 Vanilla FreeRTOS)是一款小巧高效的实时操作系统,适用于许多单核 MCU 和 SoC。但为了支持双核 ESP 芯片,如 ESP32、ESP32-S3、ESP32-P4,ESP-IDF 提供了支持双核对称多处理 (SMP) 的 FreeRTOS 实现(下文称 ESP-IDF FreeRTOS)。
|
||||
|
||||
ESP-IDF FreeRTOS 以 Vanilla FreeRTOS v10.4.3 为基础,为支持 SMP,在 API 和内核行为上做了较大改动。本文档描述了 Vanilla FreeRTOS 与 ESP-IDF FreeRTOS 之间的 API 和内核行为差异。
|
||||
|
||||
.. note::
|
||||
|
||||
本文档假定读者已具备 Vanilla FreeRTOS 的必要背景知识,即了解其特性、行为和 API 用法。如需了解背景知识,请参阅 `Vanilla FreeRTOS 文档 <https://www.freertos.org/index.html>`_。
|
||||
|
||||
.. note::
|
||||
|
||||
启用 :ref:`CONFIG_FREERTOS_UNICORE` 配置选项可以为单核芯片构建 ESP-IDF FreeRTOS,单核 ESP 芯片会默认启用 :ref:`CONFIG_FREERTOS_UNICORE` 配置选项。但请注意,启用 :ref:`CONFIG_FREERTOS_UNICORE` 并不等同于构建 Vanilla FreeRTOS,ESP-IDF FreeRTOS 的某些内核行为和 API 仍有所不同。更多详细信息,请参阅 :ref:`freertos-smp-single-core`。
|
||||
|
||||
本文档包含以下小节。
|
||||
|
||||
.. contents:: 目录
|
||||
:depth: 2
|
||||
|
||||
|
||||
.. -------------------------------------------- Symmetric Multiprocessing ----------------------------------------------
|
||||
|
||||
对称多处理
|
||||
----------
|
||||
|
||||
基本概念
|
||||
^^^^^^^^
|
||||
|
||||
对称多处理是一种计算架构,其中,两个及以上相同的 CPU 核连接到单个共享的主内存,并由单个操作系统控制。SMP 系统通常具有以下特点:
|
||||
|
||||
- 多个核独立运行。每个核都有自己的寄存器文件、中断和中断处理。
|
||||
- 对每个核呈现相同的内存视图。因此,无论在哪个核上运行,访问特定内存地址的代码都会产生相同的效果。
|
||||
|
||||
与单核或非对称多处理系统相比,SMP 系统的主要优势在于:
|
||||
|
||||
- 存在多个 CPU,支持多个硬件线程,从而提高整体处理吞吐量。
|
||||
- 对称内存支持线程在执行期间切换核,从而提高 CPU 利用率。
|
||||
|
||||
尽管 SMP 系统支持线程切换核,但在某些情况下,线程必须或应该仅在特定核上运行。因此,在 SMP 系统中,线程也具备核亲和性,指定线程在哪个特定核上运行。
|
||||
|
||||
- 固定到特定核的线程只能在该核上运行。
|
||||
- 未固定于特定核的线程支持在执行期间切换核。
|
||||
|
||||
ESP 芯片上的 SMP
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
ESP32、ESP32-S3、ESP32-P4 等 ESP 芯片是双核 SMP SoC,具有以下硬件特性以支持 SMP:
|
||||
|
||||
- 具有两个完全相同的核,分别称为 CPU0 和 CPU1。代码段无论在哪个核上运行,都有相同的执行效果。
|
||||
- 具有对称内存(除了少数例外情况)。
|
||||
|
||||
- 如果多个核同时访问相同的内存地址,它们的访问会被内存总线串行化。
|
||||
- 通过 ISA 提供的原子比较和交换指令,可以实现对同一内存地址的真正原子访问。
|
||||
|
||||
- 跨核中断支持由一个 CPU 触发另一个 CPU 上的中断,这使得核间可以互相发送信号。
|
||||
|
||||
|
||||
.. only:: not esp32p4
|
||||
|
||||
.. note::
|
||||
|
||||
CPU0 又称协议 CPU 或 ``PRO_CPU``,CPU1 又称应用 CPU 或 ``APP_CPU``。在 ESP-IDF 中,CPU0 和 CPU1 的别名 ``PRO_CPU`` 和 ``APP_CPU`` 反映了典型 ESP-IDF 应用程序使用这两个 CPU 的方式。负责处理无线网络(如 Wi-Fi 或蓝牙)的任务通常会固定在 CPU0 上,因此称 CPU0 为 ``PRO_CPU``;而处理应用程序的其余部分任务会被固定在 CPU1 上,因此称 CPU1 为 ``APP_CPU``。
|
||||
|
||||
|
||||
.. ------------------------------------------------------ Tasks --------------------------------------------------------
|
||||
|
||||
任务
|
||||
----
|
||||
|
||||
创建任务
|
||||
^^^^^^^^
|
||||
|
||||
Vanilla FreeRTOS 提供以下用于创建任务的函数:
|
||||
|
||||
- 使用 :cpp:func:`xTaskCreate` 创建任务时,任务内存动态分配。
|
||||
- 使用 :cpp:func:`xTaskCreateStatic` 创建任务时,任务内存静态分配,即由用户提供。
|
||||
|
||||
然而,在 SMP 系统中,任务需要分配到特定核。因此,ESP-IDF 提供了 Vanilla FreeRTOS 任务创建函数的 ``PinnedToCore`` 版本:
|
||||
|
||||
- 使用 :cpp:func:`xTaskCreatePinnedToCore` 可以创建具有特定核亲和性的任务,任务内存动态分配。
|
||||
- 使用 :cpp:func:`xTaskCreateStaticPinnedToCore` 可以创建具有特定核亲和性的任务,任务内存静态分配,即由用户提供。
|
||||
|
||||
不同于普通的任务创建函数 API,``PinnedToCore`` 版本的任务创建函数 API 有额外的 ``xCoreID`` 参数,用于指定所创建任务的核亲和性。核亲和性的有效值包括:
|
||||
|
||||
- ``0``:将创建的任务固定到 CPU0
|
||||
- ``1``:将创建的任务固定到 CPU1
|
||||
- ``tskNO_AFFINITY``:支持任务在两个 CPU 上运行
|
||||
|
||||
注意,ESP-IDF FreeRTOS 仍支持普通的任务创建函数,但这些标准函数已经过调整,会内部调用其 ``PinnedToCore`` 版本,同时将核亲和性设置为 ``tskNO_AFFINITY``。
|
||||
|
||||
.. note::
|
||||
|
||||
ESP-IDF FreeRTOS 还更改了任务创建函数中的 ``ulStackDepth`` 参数。在 Vanilla FreeRTOS 中,任务堆栈的大小以字为单位指定,而在 ESP-IDF FreeRTOS 中,任务堆栈的大小以字节为单位指定。
|
||||
|
||||
执行任务
|
||||
^^^^^^^^
|
||||
|
||||
ESP-IDF FreeRTOS 中任务的结构与 Vanilla FreeRTOS 相同。具体而言,ESP-IDF FreeRTOS 任务:
|
||||
|
||||
- 只能处于以下任一状态:运行中、就绪、阻塞或挂起。
|
||||
- 任务函数通常为无限循环。
|
||||
- 任务函数不应返回。
|
||||
|
||||
删除任务
|
||||
^^^^^^^^
|
||||
|
||||
调用 :cpp:func:`vTaskDelete` 可以在 Vanilla FreeRTOS 中删除任务。该函数可用于删除其他任务,若任务句柄为 ``NULL`` 则删除当前运行任务。如果删除的任务是当前正在运行的任务时,任务的内存释放有时会委托给空闲任务执行。
|
||||
|
||||
ESP-IDF FreeRTOS 提供了同样的 :cpp:func:`vTaskDelete` 函数。然而,ESP-IDF FreeRTOS 是一个双核系统,因此调用 :cpp:func:`vTaskDelete` 时,行为上会与 Vanilla FreeRTOS 有以下差异:
|
||||
|
||||
- 删除另一个核上运行的任务时,会在另一个核上触发一次让步,任务内存由其中一个空闲任务释放。
|
||||
- 如果删除的任务没有在任一核上运行,则会立即释放其内存。
|
||||
|
||||
请避免删除正在另一个核上运行的任务,否则由于无法确定该任务正在执行的操作,可能会导致难以预料的行为,例如:
|
||||
|
||||
- 删除持有互斥锁的任务。
|
||||
- 删除尚未释放其先前分配的内存的任务。
|
||||
|
||||
请尽可能自己设计应用程序,确保在调用 :cpp:func:`vTaskDelete` 时,删除的任务处于已知状态。例如:
|
||||
|
||||
- 当任务完成执行操作并清理了任务内使用的所有资源时,任务调用 ``vTaskDelete(NULL)`` 自行删除。
|
||||
- 在被另一个任务删除前,任务调用 :cpp:func:`vTaskSuspend` 将自己置于挂起状态。
|
||||
|
||||
|
||||
.. --------------------------------------------------- Scheduling ------------------------------------------------------
|
||||
|
||||
SMP 调度器
|
||||
----------
|
||||
|
||||
对 Vanilla FreeRTOS 调度器最确切的描述是 **具有时间分片和固定优先级的抢占式调度器**,这意味着:
|
||||
|
||||
- 每个任务在创建时都赋予了固定优先级,调度器会执行具有最高优先级且处于就绪状态的任务。
|
||||
- 调度器可以在不需要当前运行任务的协作下切换执行到另一个任务。
|
||||
- 调度器会采用轮转方式,定期在具有相同优先级的就绪状态任务间切换执行,时间分片由时钟中断控制。
|
||||
|
||||
ESP-IDF FreeRTOS 调度器支持相同的调度特性,即固定优先级、抢占和时间分片,但也存在细微的行为差异。
|
||||
|
||||
固定优先级
|
||||
^^^^^^^^^^
|
||||
|
||||
在 Vanilla FreeRTOS 中,当调度器选择要运行的新任务时,往往会选择当前优先级最高的就绪任务。而在 ESP-IDF FreeRTOS 中,每个核都独立地调度要运行的任务。当特定核选择一个任务时,该核会选择优先级最高且可以在该核上运行的就绪状态任务。满足以下条件时,任务可以在核上运行:
|
||||
|
||||
- 任务亲和性兼容,即已固定或未固定到当前核上。
|
||||
- 该任务当前没有在其他核上运行。
|
||||
|
||||
但是,两个具有最高优先级的就绪任务不一定始终由调度器运行,因为还需考虑到任务的核亲和性。例如,给定以下任务:
|
||||
|
||||
- 优先级为 10 的任务 A,固定到 CPU0
|
||||
- 优先级为 9 的任务 B,固定到 CPU0
|
||||
- 优先级为 8 的任务 C,固定到 CPU1
|
||||
|
||||
经过调度后,任务 A 将在 CPU0 上运行,任务 C 将在 CPU1 上运行。即使任务 B 是第二优先级任务,也不会被执行。
|
||||
|
||||
抢占
|
||||
^^^^
|
||||
|
||||
在 Vanilla FreeRTOS 中,如果优先级更高的任务已准备好执行,调度器可以抢占当前正在运行的任务。同样,在 ESP-IDF FreeRTOS 任务中,如果调度器确定一个优先级更高的任务可以在某个核上运行,那么调度器可以单独抢占各个核。
|
||||
|
||||
但在某些情况下,一个优先级更高的就绪任务可以在多个核上运行。此时,调度器只会抢占一个核。即便当前有多个核可以抢占,调度器总是优先选择当前核。换句话说,如果优先级更高的就绪任务未固定,并且其优先级高于两个核的当前优先级,调度器将始终选择抢占当前核。例如,给定以下任务:
|
||||
|
||||
- 优先级为 8 的任务 A 当前在 CPU0 上运行
|
||||
- 优先级为 9 的任务 B 当前在 CPU1 上运行
|
||||
- 优先级为 10 的任务 C 未固定,并由任务 B 解除了阻塞
|
||||
|
||||
经过调度后,任务 A 将在 CPU0 上运行,任务 C 将抢占任务 B,因为调度器总是优先选择当前核。
|
||||
|
||||
时间分片
|
||||
^^^^^^^^
|
||||
|
||||
Vanilla FreeRTOS 实现了时间分片,这意味着如果当前优先级最高的就绪任务包含多个就绪任务,调度器会在这些任务间轮转定期切换。
|
||||
|
||||
然而,在 ESP-IDF FreeRTOS 中,由于以下原因,特定任务可能无法在特定核上运行,因此无法实现完美的轮转时间分片:
|
||||
|
||||
- 任务固定到了另一个核。
|
||||
- 任务未固定,但已经由其他核运行。
|
||||
|
||||
因此,当核在所有就绪状态任务中搜索寻找要运行的任务时,可能需要跳过同一优先级列表中的一些任务,或者降低优先级,以找到可以运行的就绪状态任务。
|
||||
|
||||
ESP-IDF FreeRTOS 调度器会确保已选择运行的任务置于列表末尾,为同一优先级的就绪状态任务实现最佳轮转时间分片。这样,在下一次调度迭代(即,下一个滴答中断或让步)中,未经选择的任务优先级会更高。
|
||||
|
||||
以下示例展示了最佳轮转时间分片的实操。假设:
|
||||
|
||||
- 有四个相同优先级的就绪状态任务 ``AX``、``B0``、``C1`` 和 ``D1``,其中:
|
||||
|
||||
- 优先级是当前具有就绪状态任务的最高优先级。
|
||||
- 第一个字符代表任务名称,即 ``A``、``B``、``C``、``D``。
|
||||
- 第二个字符表示任务核的固定情况,``X`` 表示未固定。
|
||||
|
||||
- 任务列表始终从头开始搜索
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
1. 起始状态,尚未选择要运行的就绪状态任务。
|
||||
|
||||
Head [ AX , B0 , C1 , D0 ] Tail
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
2. 核 0 有一个滴答中断,搜索要运行的任务。
|
||||
|
||||
选择任务 A,并将其移至列表末尾。
|
||||
|
||||
Core0--|
|
||||
Head [ AX , B0 , C1 , D0 ] Tail
|
||||
|
||||
0
|
||||
Head [ B0 , C1 , D0 , AX ] Tail
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
3. 核 1 有一个滴答中断,搜索要运行的任务。
|
||||
|
||||
由于亲和性不兼容,任务 B 无法运行,因此核 1 跳到任务 C。
|
||||
|
||||
选择任务 C,并将其移至列表末尾。
|
||||
|
||||
Core1-------| 0
|
||||
Head [ B0 , C1 , D0 , AX ] Tail
|
||||
|
||||
0 1
|
||||
Head [ B0 , D0 , AX , C1 ] Tail
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
4. 核 0 有另一个滴答中断,搜索要运行的任务。
|
||||
|
||||
选择任务 B,并将其移至列表末尾。
|
||||
|
||||
|
||||
Core0--| 1
|
||||
Head [ B0 , D0 , AX , C1 ] Tail
|
||||
|
||||
1 0
|
||||
Head [ D0 , AX , C1 , B0 ] Tail
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
5. 核 1 有另一个滴答中断,搜索要运行的任务。
|
||||
|
||||
由于亲和性不兼容,任务 D 无法运行,因此核 1 跳到任务 A
|
||||
|
||||
选择任务 A,并将其移至列表末尾
|
||||
|
||||
Core1-------| 0
|
||||
Head [ D0 , AX , C1 , B0 ] Tail
|
||||
|
||||
0 1
|
||||
Head [ D0 , C1 , B0 , AX ] Tail
|
||||
|
||||
|
||||
在使用最佳轮转时间分片时需注意:
|
||||
|
||||
- 相同优先级的多个就绪状态任务不一定可以像在 Vanilla FreeRTOS 中一样按顺序运行。如以上示例所示,核可能会跳过任务。
|
||||
- 然而,经过足够的滴答次数,任务最终将获得一些处理时间。
|
||||
- 如果核找不到优先级最高的可运行就绪任务,它将降低优先级来搜索任务。
|
||||
- 为了实现理想的轮转时间分片,应确保特定优先级的所有任务都固定到同一个核上。
|
||||
|
||||
时钟中断
|
||||
^^^^^^^^
|
||||
|
||||
Vanilla FreeRTOS 要求定期发生滴答中断,滴答中断有以下作用:
|
||||
|
||||
- 增加调度器的滴答计数
|
||||
- 为超时的阻塞任务解除阻塞
|
||||
- 检查是否需要进行时间分片,即触发上下文切换
|
||||
- 执行应用程序滴答函数
|
||||
|
||||
在 ESP-IDF FreeRTOS 中,每个核都会接收到定期中断,并独立运行滴答中断。每个核上的滴答中断周期相同,但可能不同步。然而,上述滴答中断任务不会由所有核同时执行,具体而言:
|
||||
|
||||
- CPU0 执行上述所有滴答中断任务
|
||||
- CPU1 仅检查是否需要时间分片并执行应用程序滴答函数
|
||||
|
||||
.. note::
|
||||
|
||||
在 ESP-IDF FreeRTOS 中,CPU0 是负责时间计数的唯一核。因此,任何阻止 CPU0 增加滴答计数的情况,例如暂停 CPU0 上的调度器,都会导致整个调度器的时间计数滞后。
|
||||
|
||||
空闲任务
|
||||
^^^^^^^^
|
||||
|
||||
启动调度器时,Vanilla FreeRTOS 会隐式创建一个优先级为 0 的空闲任务。当没有其他任务准备运行时,空闲任务运行并有以下作用:
|
||||
|
||||
- 释放已删除任务的内存
|
||||
- 执行应用程序的空闲函数
|
||||
|
||||
而 ESP-IDF FreeRTOS 为每个核单独创建了一个固定的空闲任务。每个核上的空闲任务起到与其 Vanilla FreeRTOS 对应任务相同的作用。
|
||||
|
||||
调度器挂起
|
||||
^^^^^^^^^^
|
||||
|
||||
Vanilla FreeRTOS 支持调用 :cpp:func:`vTaskSuspendAll` 挂起调度器,调用 :cpp:func:`xTaskResumeAll` 恢复调度器。调度器挂起时:
|
||||
|
||||
- 禁用任务切换,但仍启用中断。
|
||||
- 禁止调用任何阻塞或让出函数,禁用时间分片。
|
||||
- 时钟计数冻结,但仍会发生时钟中断以执行应用程序时钟函数。
|
||||
|
||||
调度器恢复时,:cpp:func:`xTaskResumeAll` 会补上所有丢失的时钟计数,并解除超时任务的阻塞。
|
||||
|
||||
在 ESP-IDF FreeRTOS 中,无法在多个核上同时挂起调度器。因此,在特定核上(如核 A)调用 :cpp:func:`vTaskSuspendAll` 时:
|
||||
|
||||
- 只在核 A 上禁用任务切换,但仍启用核 A 的中断。
|
||||
- 禁止在核 A 上调用任何阻塞或让出函数,在核 A 上禁用时间分片。
|
||||
- 核 A 上的中断解除任务阻塞时,对核 A 亲和的任务会进入核 A 的待执行任务列表。未固定的任务或对其他核亲和的任务可以在运行调度器的核上调度。
|
||||
- 所有核上的调度器均挂起时,由中断解除阻塞的任务将进入它们固定到的核的待执行任务列表。如果任务未固定,则进入调用中断的核的待执行任务列表。
|
||||
- 如果核 A 是 CPU0,则时钟计数将被冻结,挂起的时钟计数将递增,但仍会发生时钟中断以执行应用程序时钟函数。
|
||||
|
||||
在特定核(如核 A)上调用 :cpp:func:`xTaskResumeAll` 时:
|
||||
|
||||
- 任何添加到核 A 的待执行任务列表中的任务将恢复执行。
|
||||
- 如果核 A 是 CPU0,则挂起的时钟计数将回退,补上丢失的时钟计数。
|
||||
|
||||
.. warning::
|
||||
|
||||
ESP-IDF FreeRTOS 上的调度器挂起仅暂停特定核上的调度,因此调度器挂起 **不能** 确保访问共享数据时任务互斥。如果需要互斥,请使用适当的锁定机制,如互斥锁或自旋锁。
|
||||
|
||||
禁用中断
|
||||
^^^^^^^^
|
||||
|
||||
Vanilla FreeRTOS 支持调用 :c:macro:`taskDISABLE_INTERRUPTS` 禁用中断,调用 :c:macro:`taskENABLE_INTERRUPTS` 启用中断。
|
||||
|
||||
ESP-IDF FreeRTOS 提供相同的 API,但只能在当前核上启用或禁用中断。
|
||||
|
||||
.. warning::
|
||||
|
||||
在 Vanilla FreeRTOS 和一般的单核系统中,禁用中断是实现互斥的有效方法。但在 SMP 系统中,禁用中断 **不能** 确保实现互斥。详情请参阅 :ref:`critical-sections`。
|
||||
|
||||
|
||||
.. ------------------------------------------------ Critical Sections --------------------------------------------------
|
||||
|
||||
.. _critical-sections:
|
||||
|
||||
临界区
|
||||
------
|
||||
|
||||
API 变更
|
||||
^^^^^^^^
|
||||
|
||||
Vanilla FreeRTOS 通过禁用中断实现临界区 (Critical Section),以防止在临界区内发生抢占式上下文切换和中断服务,确保进入临界区的任务或 ISR 是访问共享资源的唯一实体。Vanilla FreeRTOS 中的临界区提供以下 API:
|
||||
|
||||
- ``taskENTER_CRITICAL()`` 通过禁用中断进入临界区
|
||||
- ``taskEXIT_CRITICAL()`` 通过重新启用中断退出临界区
|
||||
- ``taskENTER_CRITICAL_FROM_ISR()`` 通过禁用中断嵌套从 ISR 进入临界区
|
||||
- ``taskEXIT_CRITICAL_FROM_ISR()`` 通过重新启用中断嵌套从 ISR 退出临界区
|
||||
|
||||
然而,在 SMP 系统中,仅禁用中断并不能构成临界区,因为存在其他核意味着共享资源仍可以同时访问。因此,ESP-IDF FreeRTOS 中的临界区是使用自旋锁实现的。为适应自旋锁,ESP-IDF FreeRTOS 中的临界区 API 包含一个额外的自旋锁参数,具体如下:
|
||||
|
||||
- 自旋锁为 ``portMUX_TYPE`` (**请勿与 FreeRTOS 互斥混淆**)
|
||||
- ``taskENTER_CRITICAL(&spinlock)`` 从任务上下文进入临界区
|
||||
- ``taskEXIT_CRITICAL(&spinlock)`` 从任务上下文退出临界区
|
||||
- ``taskENTER_CRITICAL_ISR(&spinlock)`` 从中断上下文进入临界区
|
||||
- ``taskEXIT_CRITICAL_ISR(&spinlock)`` 从中断上下文退出临界区
|
||||
|
||||
.. note::
|
||||
|
||||
临界区 API 可以递归调用,即可以嵌套使用临界区。只要退出临界区的次数与进入的次数相同,多次递归进入临界区就是有效的。但是,由于临界区可以针对不同的自旋锁,因此在递归进入临界区时,应注意避免死锁。
|
||||
|
||||
自旋锁可以静态或动态分配。因此,提供了静态和动态初始化自旋锁的宏,如以下代码片段所示。
|
||||
|
||||
- 静态分配自旋锁并使用 ``portMUX_INITIALIZER_UNLOCKED`` 初始化:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// 静态分配并初始化自旋锁
|
||||
static portMUX_TYPE my_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
void some_function(void)
|
||||
{
|
||||
taskENTER_CRITICAL(&my_spinlock);
|
||||
// 此时已处于临界区
|
||||
taskEXIT_CRITICAL(&my_spinlock);
|
||||
}
|
||||
|
||||
- 静态分配自旋锁并使用 ``portMUX_INITIALIZE()`` 初始化:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// 动态分配自旋锁
|
||||
portMUX_TYPE *my_spinlock = malloc(sizeof(portMUX_TYPE));
|
||||
// 动态初始化自旋锁
|
||||
portMUX_INITIALIZE(my_spinlock);
|
||||
|
||||
...
|
||||
|
||||
taskENTER_CRITICAL(my_spinlock);
|
||||
// 访问资源
|
||||
taskEXIT_CRITICAL(my_spinlock);
|
||||
|
||||
实现
|
||||
^^^^
|
||||
|
||||
ESP-IDF FreeRTOS 中,特定核进入和退出临界区的过程如下:
|
||||
|
||||
- 对于 ``taskENTER_CRITICAL(&spinlock)`` 或 ``taskENTER_CRITICAL_ISR(&spinlock)``
|
||||
|
||||
#. 核禁用其中断或中断嵌套,直到达到 ``configMAX_SYSCALL_INTERRUPT_PRIORITY``。
|
||||
#. 接着,核使用原子比较和设置指令在自旋锁上自旋,直到获取锁。当核能够将锁的所有者值设置为核的 ID 时,就获得了锁。
|
||||
#. 一旦获取了自旋锁,函数返回。剩余的临界区部分将在禁用中断或中断嵌套的情况下运行。
|
||||
|
||||
- 对于 ``taskEXIT_CRITICAL(&spinlock)`` 或 ``taskEXIT_CRITICAL_ISR(&spinlock)``
|
||||
|
||||
#. 核通过清除自旋锁的所有者值释放自旋锁。
|
||||
#. 核重新启用中断或中断嵌套。
|
||||
|
||||
限制与注意事项
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
由于在临界区内禁用了中断或中断嵌套,产生了多个关于在临界区内可执行操作的限制,请牢记以下操作限制和注意事项:
|
||||
|
||||
- 临界区应尽可能短
|
||||
|
||||
- 临界区持续时间越长,越可能延迟挂起中断的执行。
|
||||
- 临界区通常应仅涉及少量数据结构和/或硬件寄存器。
|
||||
- 如果可以,尽可能将执行操作和/或事件处理程序推迟到临界区之外。
|
||||
|
||||
- 不应在临界区内调用 FreeRTOS API
|
||||
- 不应在临界区内调用任何阻塞或让出函数
|
||||
|
||||
|
||||
.. ------------------------------------------------------ Misc ---------------------------------------------------------
|
||||
|
||||
其他事项
|
||||
--------
|
||||
|
||||
.. only:: SOC_CPU_HAS_FPU
|
||||
|
||||
使用浮点
|
||||
^^^^^^^^
|
||||
|
||||
通常情况下,当发生上下文切换时:
|
||||
|
||||
- CPU 寄存器的当前状态保存到要切出的任务栈中
|
||||
- CPU 寄存器的先前保存状态从要切入的任务栈中加载
|
||||
|
||||
然而,ESP-IDF FreeRTOS 为 CPU 的浮点单元 (FPU) 寄存器实现了延迟上下文切换。换句话说,当在特定核上(如 CPU0)发生上下文切换时,核的 FPU 寄存器状态不会立即保存到要被切出的任务的堆栈中(如任务 A)。FPU 的寄存器在发生以下情况前将保持不变:
|
||||
|
||||
- 另一个任务(如任务 B)在同一核上运行并使用 FPU,这将触发异常,将 FPU 寄存器保存到任务 A 的堆栈中。
|
||||
- 任务 A 重新调度到同一核并继续执行。在这种情况下,不需要保存和恢复 FPU 的寄存器。
|
||||
|
||||
然而,由于任务并未固定到某一核上,可以随意调度(如任务 A 切换到 CPU1),因此很难实现跨核复制和恢复 FPU 寄存器状态。因此,当任务在其执行流程中用 ``float`` 类型使用 FPU 时,ESP-IDF FreeRTOS 会自动将任务固定到当前正在运行的核上,确保所有使用 FPU 的任务始终在特定核上运行。
|
||||
|
||||
此外,请注意,由于 FPU 寄存器状态与特定任务相关联,ESP-IDF FreeRTOS 默认不支持在中断上下文中使用 FPU。
|
||||
|
||||
.. only: esp32
|
||||
|
||||
.. note::
|
||||
|
||||
如需在 ISR 例程中使用 ``float`` 类型,请参考配置选项:ref:`CONFIG_FREERTOS_FPU_IN_ISR`。
|
||||
|
||||
.. note::
|
||||
|
||||
具有 FPU 的 ESP 芯片不支持双精度浮点运算 ``double`` 的硬件加速。``double`` 通过软件实现,因此比起 ``float`` 类型,``double`` 操作可能消耗更多 CPU 时间。
|
||||
|
||||
|
||||
.. -------------------------------------------------- Single Core -----------------------------------------------------
|
||||
|
||||
.. _freertos-smp-single-core:
|
||||
|
||||
单核 ESP-IDF FreeRTOS
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
尽管 ESP-IDF FreeRTOS 为 SMP 调度器,仍需要考虑单核 ESP 芯片,如 ESP32-S2 和 ESP32-C3。为这些芯片构建 ESP-IDF 应用程序时,同样使用 ESP-IDF FreeRTOS,但核的数量将设置为 ``1``,即 :ref:`CONFIG_FREERTOS_UNICORE` 将始终对单核芯片启用。
|
||||
|
||||
对于 ESP32 和 ESP32-S3 等多核芯片也可以设置 :ref:`CONFIG_FREERTOS_UNICORE`,启用该选项后 ESP-IDF FreeRTOS 仅在 CPU0 上运行,其他核都将处于非活动状态。
|
||||
|
||||
.. note::
|
||||
|
||||
请注意,开启 :ref:`CONFIG_FREERTOS_UNICORE` **不等于运行 Vanilla FreeRTOS**,此时仍然可以调用 ESP-IDF FreeRTOS 的附加 API,且即使为单核编译,ESP-IDF FreeRTOS 的行为变化仍会导致一定的性能开销。
|
||||
|
||||
.. ------------------------------------------------- API References ----------------------------------------------------
|
||||
|
||||
API 参考
|
||||
--------
|
||||
|
||||
本节介绍了 FreeRTOS 类型、函数和宏,均从 FreeRTOS 头文件自动生成。
|
||||
|
||||
任务 API
|
||||
^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/task.inc
|
||||
|
||||
队列 API
|
||||
^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/queue.inc
|
||||
|
||||
信号量 API
|
||||
^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/semphr.inc
|
||||
|
||||
定时器 API
|
||||
^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/timers.inc
|
||||
|
||||
事件组 API
|
||||
^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/event_groups.inc
|
||||
|
||||
流缓冲区 API
|
||||
^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/stream_buffer.inc
|
||||
|
||||
消息缓冲区 API
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/message_buffer.inc
|
||||
|
Loading…
x
Reference in New Issue
Block a user