esp-idf/docs/zh_CN/api-reference/system/freertos_idf.rst

500 lines
24 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

FreeRTOS (IDF)
==============
:link_to_translation:`en:[English]`
本文档介绍了 ESP-IDF 框架内的 FreeRTOS 双核 SMP 实现,包含以下小节:
.. contents:: 目录
:depth: 2
.. ---------------------------------------------------- Overview -------------------------------------------------------
概述
----
原始 FreeRTOS下文称 Vanilla FreeRTOS是一款小巧高效的实时操作系统适用于许多单核 MCU 和 SoC。但为了支持双核 ESP 芯片,如 ESP32、ESP32-S3、ESP32-P4ESP-IDF 特别提供了支持双核对称多处理 (SMP) 的 FreeRTOS 实现(下文称 IDF FreeRTOS
IDF FreeRTOS 源代码基于 Vanilla FreeRTOS v10.5.1,但内核行为和 API 都有重大修改,以支持双核 SMP。不过用户也可以启用 :ref:`CONFIG_FREERTOS_UNICORE` 选项,将 IDF FreeRTOS 配置为支持单核,详情请参阅 :ref:`freertos-idf-single-core`
.. note::
本文档假定读者已具备 Vanilla FreeRTOS 的必要背景知识,即了解其特性、行为和 API 用法。如需了解背景知识,请参阅 `Vanilla FreeRTOS 文档 <https://www.freertos.org/index.html>`_
.. -------------------------------------------- Symmetric Multiprocessing ----------------------------------------------
对称多处理
----------
基本概念
^^^^^^^^
对称多处理是一种计算架构,其中,两个及以上相同的 CPU 核连接到单个共享的主内存并由单个操作系统控制。SMP 系统通常具有以下特点:
- 多个核独立运行。每个核都有自己的寄存器文件、中断和中断处理。
- 对每个核呈现相同的内存视图。因此,无论在哪个核上运行,访问特定内存地址的代码都会产生相同的效果。
与单核或非对称多处理系统相比SMP 系统的主要优势在于:
- 存在多个核,支持多个硬件线程,从而提高整体处理吞吐量。
- 对称内存支持线程在执行期间切换核,从而提高 CPU 利用率。
尽管 SMP 系统支持线程切换核,但在某些情况下,线程必须或应该仅在特定核上运行。因此,在 SMP 系统中,线程也具备核亲和性,指定线程在哪个特定核上运行。
- 分配给特定核的线程只能在该核上运行。
- 未分配给特定核的线程支持在执行期间切换核。
ESP 芯片上的 SMP
^^^^^^^^^^^^^^^^
ESP32、ESP32-S3、ESP32-P4 等 ESP 芯片是双核 SMP SoC具有以下硬件特性以支持 SMP
- 具有两个完全相同的核,分别称为核 0 和核 1。代码段无论在哪个核上运行都有相同的执行效果。
- 具有对称内存(除了少数例外情况)。
- 如果多个核同时访问相同的内存地址,它们的访问会被内存总线串行化。
- 通过 ISA 提供的原子比较和交换指令,可以实现对同一内存地址的真正原子访问。
- 跨核中断支持由一个核触发另一个核上的中断,这使得核间可以互相发送信号,如请求在另一个核上进行上下文切换。
.. note::
在 ESP-IDF 中,核 0 和核 1 有时分别又被称为 ``PRO_CPU````APP_CPU``。别名 ``PRO_CPU````APP_CPU`` 反映了典型 ESP-IDF 应用程序使用这两个 CPU 的方式。负责处理 Wi-Fi 或蓝牙等协议相关处理程序的任务通常会分配给核 0因此称核 0 为 ``PRO_CPU``;而处理应用程序其余部分的任务会分配给核 1因此称核 1 为 ``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``:将创建的任务分配给核 0
- ``1``:将创建的任务分配给核 1
- ``tskNO_AFFINITY``:支持任务在两个核上运行
注意IDF FreeRTOS 仍支持普通的任务创建函数,但这些标准函数已经过调整,会内部调用其 ``...PinnedToCore()`` 版本,同时将核亲和性设置为 ``tskNO_AFFINITY``
.. note::
IDF FreeRTOS 还更改了任务创建函数中的 ``ulStackDepth`` 参数。在 Vanilla FreeRTOS 中,任务堆栈的大小以字为单位指定,而在 IDF FreeRTOS 中,任务堆栈的大小以字节为单位指定。
执行任务
^^^^^^^^
IDF FreeRTOS 中任务的结构与 Vanilla FreeRTOS 相同。具体而言IDF FreeRTOS 任务:
- 只能处于以下任一状态:运行中、就绪、阻塞或挂起。
- 任务函数通常为无限循环。
- 任务函数不应返回。
删除任务
^^^^^^^^
调用 :cpp:func:`vTaskDelete` 可以在 Vanilla FreeRTOS 中删除任务。该函数可用于删除其他任务,若任务句柄为 ``NULL`` 则删除当前运行任务。如果删除的任务是当前正在运行的任务时,任务的内存释放有时会委托给空闲任务执行。
IDF FreeRTOS 提供了同样的 :cpp:func:`vTaskDelete` 函数。然而IDF FreeRTOS 是一个双核系统,因此调用 :cpp:func:`vTaskDelete` 时,行为上会与 Vanilla FreeRTOS 有以下差异:
- 删除另一个核上运行的任务时,会在另一个核上触发一次让步,任务内存由其中一个空闲任务释放。
- 如果删除的任务没有在任一核上运行,则会立即释放其内存。
请避免删除正在另一个核上运行的任务,否则由于无法确定该任务正在执行的操作,可能会导致难以预料的行为,例如:
- 删除持有互斥锁的任务。
- 删除尚未释放其先前分配的内存的任务。
请尽可能自己设计应用程序,确保在调用 :cpp:func:`vTaskDelete` 时,删除的任务处于已知状态。例如:
- 当任务完成执行操作并清理了任务内使用的所有资源时,任务调用 ``vTaskDelete(NULL)`` 自行删除。
- 在被另一个任务删除前,任务调用 :cpp:func:`vTaskSuspend` 将自己置于挂起状态。
.. --------------------------------------------------- Scheduling ------------------------------------------------------
SMP 调度器
----------
对 Vanilla FreeRTOS 调度器最确切的描述是 **具有时间分片和固定优先级的抢占式调度器**,这意味着:
- 每个任务在创建时都赋予了固定优先级,调度器会执行具有最高优先级且处于就绪状态的任务。
- 调度器可以在不需要当前运行任务的协作下切换执行到另一个任务。
- 调度器会采用轮转方式,定期在具有相同优先级的就绪状态任务间切换执行,时间分片由时钟中断控制。
IDF FreeRTOS 调度器支持相同的调度特性,即固定优先级、抢占和时间分片,但也存在细微的行为差异。
固定优先级
^^^^^^^^^^
在 Vanilla FreeRTOS 中,当调度器选择要运行的新任务时,往往会选择当前优先级最高的就绪任务。而在 IDF FreeRTOS 中,每个核都独立地调度要运行的任务。当特定核选择一个任务时,该核会选择优先级最高且可以在该核上运行的就绪状态任务。满足以下条件时,任务可以在核上运行:
- 任务亲和性兼容,即已分配或未分配给当前核。
- 该任务当前没有在其他核上运行。
但是,两个具有最高优先级的就绪任务不一定始终由调度器运行,因为还需考虑到任务的核亲和性。例如,给定以下任务:
- 优先级为 10 的任务 A分配给核 0
- 优先级为 9 的任务 B分配给核 0
- 优先级为 8 的任务 C分配给核 1
经过调度后,任务 A 将在核 0 上运行,任务 C 将在核 1 上运行。即使任务 B 是第二优先级任务,也不会被执行。
抢占
^^^^
在 Vanilla FreeRTOS 中,如果优先级更高的任务已准备好执行,调度器可以抢占当前正在运行的任务。同样,在 IDF FreeRTOS 任务中,如果调度器确定一个优先级更高的任务可以在某个核上运行,那么调度器可以单独抢占各个核。
但在某些情况下,一个优先级更高的就绪任务可以在多个核上运行。此时,调度器只会抢占一个核。即便当前有多个核可以抢占,调度器总是优先选择当前核。换句话说,如果优先级更高的就绪任务未分配,并且其优先级高于两个核的当前优先级,调度器将始终选择抢占当前核。例如,给定以下任务:
- 优先级为 8 的任务 A 当前在核 0 上运行
- 优先级为 9 的任务 B 当前在核 1 上运行
- 优先级为 10 的任务 C 未分配,并由任务 B 解除了阻塞
经过调度后,任务 A 将在核 0 上运行,任务 C 将抢占任务 B因为调度器总是优先选择当前核。
时间分片
^^^^^^^^
Vanilla FreeRTOS 实现了时间分片,这意味着如果当前优先级最高的就绪任务包含多个就绪任务,调度器会在这些任务间轮转定期切换。
然而,在 IDF FreeRTOS 中,由于以下原因,特定任务可能无法在特定核上运行,因此无法实现完美的轮转时间分片:
- 任务分配给了另一个核。
- 任务未分配,但已经由其他核运行。
因此,当核在所有就绪状态任务中搜索寻找要运行的任务时,可能需要跳过同一优先级列表中的一些任务,或者降低优先级,以找到可以运行的就绪状态任务。
IDF FreeRTOS 调度器会确保已选择运行的任务置于列表末尾,为同一优先级的就绪状态任务实现最佳轮转时间分片。这样,在下一次调度迭代(即,下一个滴答中断或让步)中,未经选择的任务优先级会更高。
以下示例展示了最佳轮转时间分片的实操。假设:
- 有四个相同优先级的就绪状态任务 ``AX````B0````C1````D1``,其中:
- 优先级是当前具有就绪状态任务的最高优先级。
- 第一个字符代表任务名称,即 ``A````B````C````D``
- 第二个字符表示任务核的分配情况,``X`` 表示未分配。
- 任务列表始终从头开始搜索
1. 起始状态,尚未选择要运行的就绪状态任务。
.. code-block:: none
Head [ AX , B0 , C1 , D0 ] Tail
2. 核 0 有一个滴答中断,搜索要运行的任务。选择任务 A并将其移至列表末尾。
.. code-block:: none
Core 0 ─┐
Head [ AX , B0 , C1 , D0 ] Tail
[0]
Head [ B0 , C1 , D0 , AX ] Tail
3. 核 1 有一个滴答中断,搜索要运行的任务。由于亲和性不兼容,任务 B 无法运行,因此核 1 跳到任务 C。选择任务 C并将其移至列表末尾。
.. code-block:: none
Core 1 ──────┐
▼ [0]
Head [ B0 , C1 , D0 , AX ] Tail
[0] [1]
Head [ B0 , D0 , AX , C1 ] Tail
4. 核 0 有另一个滴答中断,搜索要运行的任务。选择任务 B并将其移至列表末尾。
.. code-block:: none
Core 0 ─┐
▼ [1]
Head [ B0 , D0 , AX , C1 ] Tail
[1] [0]
Head [ D0 , AX , C1 , B0 ] Tail
5. 核 1 有另一个滴答中断,搜索要运行的任务。由于亲和性不兼容,任务 D 无法运行,因此核 1 跳到任务 A。选择任务 A并将其移至列表末尾。
.. code-block:: none
Core 1 ──────┐
▼ [0]
Head [ D0 , AX , C1 , B0 ] Tail
[0] [1]
Head [ D0 , C1 , B0 , AX ] Tail
在使用最佳轮转时间分片时需注意:
- 相同优先级的多个就绪状态任务不一定可以像在 Vanilla FreeRTOS 中一样按顺序运行。如以上示例所示,核可能会跳过任务。
- 然而,经过足够的滴答次数,任务最终将获得一些处理时间。
- 如果核找不到优先级最高的可运行就绪任务,它将降低优先级来搜索任务。
- 为了实现理想的轮转时间分片,应确保特定优先级的所有任务都分配给同一个核。
时钟中断
^^^^^^^^
Vanilla FreeRTOS 要求定期发生滴答中断,滴答中断有以下作用:
- 增加调度器的滴答计数
- 为超时的阻塞任务解除阻塞
- 检查是否需要进行时间分片,即触发上下文切换
- 执行应用程序滴答函数
在 IDF FreeRTOS 中,每个核都会接收到定期中断,并独立运行滴答中断。每个核上的滴答中断周期相同,但可能不同步。然而,上述滴答中断任务不会由所有核同时执行,具体而言:
- 核 0 执行上述所有滴答中断任务
- 核 1 仅检查是否需要时间分片并执行应用程序滴答函数
.. note::
在 IDF FreeRTOS 中,核 0 是负责时间计数的唯一核。因此,任何阻止核 0 增加滴答计数的情况,例如暂停核 0 上的调度器,都会导致整个调度器的时间计数滞后。
空闲任务
^^^^^^^^
启动调度器时Vanilla FreeRTOS 会隐式创建一个优先级为 0 的空闲任务。当没有其他任务准备运行时,空闲任务运行并有以下作用:
- 释放已删除任务的内存
- 执行应用程序的空闲函数
而 IDF FreeRTOS 为每个核单独创建了一个固定的空闲任务。每个核上的空闲任务起到与其 Vanilla FreeRTOS 对应任务相同的作用。
调度器挂起
^^^^^^^^^^
Vanilla FreeRTOS 支持调用 :cpp:func:`vTaskSuspendAll` 挂起调度器,调用 :cpp:func:`xTaskResumeAll` 恢复调度器。调度器挂起时:
- 禁用任务切换,但仍启用中断。
- 禁止调用任何阻塞或让出函数,禁用时间分片。
- 时钟计数冻结,但仍会发生时钟中断以执行应用程序时钟函数。
调度器恢复时,:cpp:func:`xTaskResumeAll` 会补上所有丢失的时钟计数,并解除超时任务的阻塞。
在 IDF FreeRTOS 中,无法在多个核上同时挂起调度器。因此,在特定核上(如核 A调用 :cpp:func:`vTaskSuspendAll` 时:
- 只在核 A 上禁用任务切换,但仍启用核 A 的中断。
- 禁止在核 A 上调用任何阻塞或让出函数,在核 A 上禁用时间分片。
- 核 A 上的中断解除任务阻塞时,对核 A 亲和的任务会进入核 A 的待执行任务列表。未分配的任务或对其他核亲和的任务可以在运行调度器的核上调度。
- 所有核上的调度器均挂起时,由中断解除阻塞的任务将进入它们分配的核的待执行任务列表。如果任务未分配,则进入调用中断的核的待执行任务列表。
- 如果核 A 是核 0则时钟计数将被冻结挂起的时钟计数将递增但仍会发生时钟中断以执行应用程序时钟函数。
在特定核(如核 A上调用 :cpp:func:`xTaskResumeAll` 时:
- 任何添加到核 A 的待执行任务列表中的任务将恢复执行。
- 如果核 A 是核 0则挂起的时钟计数将回退补上丢失的时钟计数。
.. warning::
IDF FreeRTOS 上的调度器挂起仅暂停特定核上的调度,因此调度器挂起 **不能** 确保访问共享数据时任务互斥。如果需要互斥,请使用适当的锁定机制,如互斥锁或自旋锁。
.. ------------------------------------------------ Critical Sections --------------------------------------------------
临界区
------
禁用中断
^^^^^^^^
Vanilla FreeRTOS 支持通过调用 :c:macro:`taskDISABLE_INTERRUPTS`:c:macro:`taskENABLE_INTERRUPTS` 分别禁用和启用中断。IDF FreeRTOS 提供了相同的 API但中断只能在当前核上禁用或启用。
在 Vanilla FreeRTOS 以及其他普通单核系统中,禁用中断可以有效实现互斥,**但在 SMP 系统中,禁用中断并不能确保实现互斥**,而应使用有自旋锁的临界区以实现互斥。
API 变更
^^^^^^^^
Vanilla FreeRTOS 通过禁用中断实现临界区 (Critical Section),以防止在临界区内发生抢占式上下文切换和中断服务,确保进入临界区的任务或 ISR 是访问共享资源的唯一实体。Vanilla FreeRTOS 中的临界区提供以下 API
- ``taskENTER_CRITICAL()`` 通过禁用中断进入临界区
- ``taskEXIT_CRITICAL()`` 通过重新启用中断退出临界区
- ``taskENTER_CRITICAL_FROM_ISR()`` 通过禁用中断嵌套从 ISR 进入临界区
- ``taskEXIT_CRITICAL_FROM_ISR()`` 通过重新启用中断嵌套从 ISR 退出临界区
然而,在 SMP 系统中仅禁用中断并不能构成临界区因为存在其他核意味着共享资源仍可以同时访问。因此IDF FreeRTOS 中的临界区是使用自旋锁实现的。为适应自旋锁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);
实现
^^^^
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
使用浮点
^^^^^^^^
通常情况下,当发生上下文切换时:
- 核寄存器的当前状态保存到要切出的任务栈中
- 核寄存器的先前保存状态从要切入的任务栈中加载
然而IDF FreeRTOS 为核的浮点运算单元 (FPU) 寄存器实现了延迟上下文切换。换句话说,当在特定核上(如核 0发生上下文切换时核的 FPU 寄存器状态不会立即保存到要被切出的任务的堆栈中(如任务 A。FPU 的寄存器在发生以下情况前将保持不变:
- 另一个任务(如任务 B在同一核上运行并使用 FPU这将触发异常将 FPU 寄存器保存到任务 A 的堆栈中。
- 任务 A 重新调度到同一核并继续执行。在这种情况下,不需要保存和恢复 FPU 的寄存器。
然而,由于任务并未分配给某一核,可以随意调度(如任务 A 切换到核 1因此很难实现跨核复制和恢复 FPU 寄存器状态。因此,当任务在其执行流程中用 ``float`` 类型使用 FPU 时IDF FreeRTOS 会自动将任务分配给当前正在运行的核,确保所有使用 FPU 的任务始终在特定核上运行。
此外,请注意,由于 FPU 寄存器状态与特定任务相关联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-idf-single-core:
单核模式
^^^^^^^^
尽管 IDF FreeRTOS 是为双核 SMP 专门设计的,但也可通过启用 :ref:`CONFIG_FREERTOS_UNICORE` 选项,将 IDF FreeRTOS 配置为支持单核。
对于 ESP32-S2 和 ESP32-C3 等单核芯片,:ref:`CONFIG_FREERTOS_UNICORE` 选项始终启用。对于 ESP32 和 ESP32-S3 等多核芯片也可以设置 :ref:`CONFIG_FREERTOS_UNICORE`,对于多核目标(如 ESP32 和 ESP32-S3也可以设置 :ref:`CONFIG_FREERTOS_UNICORE`,但启用该选项后应用仅在核 0 上运行。
在单核模式下IDF FreeRTOS 与 Vanilla FreeRTOS 完全相同,因此无需考虑前文提到的对内核行为的 SMP 更改。因此,在单核模式下构建 IDF FreeRTOS 具有以下特点:
- 内核在临界区内执行的所有操作都是确定的(即在临界区内不走链表)。
- 恢复了 Vanilla FreeRTOS 调度算法(包括完美的轮转时间分片)。
- 移除了所有单核构建中的 SMP 专用数据。
在单核模式下仍可调用 SMP API这些 API 仍然保持公开,以便为单核和多核构建源代码,而无需调用不同的 API 集。不过SMP API 在单核模式下不会展示任何 SMP 行为,因此实际上等同于其对应的单核模式 API。例如
- 任何 ``...ForCore(..., BaseType_t xCoreID)`` SMP API 将只接受 ``0````xCoreID`` 的有效值。
- ``...PinnedToCore()`` 任务创建 API 将直接忽略 ``xCoreID`` 核亲和参数。
- 临界区 API 仍需要自旋锁参数,但不会使用自旋锁,临界区将恢复为仅用于禁用/启用中断。
.. ------------------------------------------------- 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