mirror of
https://github.com/espressif/esp-idf.git
synced 2025-11-01 23:51:49 +01:00
docs: update format issues for EN and CN files under api-reference/system and...
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
控制台终端
|
||||
==========
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
ESP-IDF 提供了 ``console`` 组件,它包含了开发基于串口的交互式控制终端所需要的所有模块,主要支持以下功能:
|
||||
@@ -12,7 +13,7 @@ ESP-IDF 提供了 ``console`` 组件,它包含了开发基于串口的交互
|
||||
|
||||
.. note::
|
||||
|
||||
这些功能模块可以一起使用也可以独立使用,例如仅使用行编辑和命令注册的功能,然后使用 ``getopt`` 函数或者自定义的函数来实现参数解析,而不是直接使用 `argtable3 <https://www.argtable.org/>`_ 库。同样地,还可以使用更简单的命令输入方法(比如 ``fgets`` 函数)和其他用于命令分割和参数解析的方法。
|
||||
这些功能模块可以一起使用,也可以独立使用,例如仅使用行编辑和命令注册的功能,然后使用 ``getopt`` 函数或者自定义的函数来实现参数解析,而不是直接使用 `argtable3 <https://www.argtable.org/>`_ 库。同样地,还可以使用更简单的命令输入方法(比如 ``fgets`` 函数)和其他用于命令分割和参数解析的方法。
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -25,7 +26,7 @@ ESP-IDF 提供了 ``console`` 组件,它包含了开发基于串口的交互
|
||||
|
||||
.. note::
|
||||
|
||||
此功能依赖于终端应用程序对 ANSI 转义符的支持。因此,显示原始 UART 数据的串口监视器不能与行编辑库一同使用。如果运行 :example:`system/console` 示例程序的时候看到的输出结果是 ``[6n`` 或者类似的转义字符而不是命令行提示符 ``esp> `` 时,就表明当前的串口监视器不支持 ANSI 转义字符。已知可用的串口监视程序有 GNU screen、minicom 和 esp-idf-monitor(可以通过在项目目录下执行 ``idf.py monitor`` 来调用)。
|
||||
此功能依赖于终端应用程序对 ANSI 转义符的支持。因此,显示原始 UART 数据的串口监视器不能与行编辑库一同使用。如果运行 :example:`system/console` 示例程序的时候看到的输出结果是 ``[6n`` 或者类似的转义字符而不是命令行提示符 ``esp>`` 时,就表明当前的串口监视器不支持 ANSI 转义字符。已知可用的串口监视程序有 GNU screen、minicom 和 esp-idf-monitor(可以通过在项目目录下执行 ``idf.py monitor`` 来调用)。
|
||||
|
||||
前往这里可以查看 `linenoise <https://github.com/antirez/linenoise>`_ 库提供的所有函数的描述。
|
||||
|
||||
@@ -34,19 +35,19 @@ ESP-IDF 提供了 ``console`` 组件,它包含了开发基于串口的交互
|
||||
|
||||
Linenoise 库不需要显式地初始化,但是在调用行编辑函数之前,可能需要对某些配置的默认值稍作修改。
|
||||
|
||||
:cpp:func:`linenoiseClearScreen`
|
||||
- :cpp:func:`linenoiseClearScreen`
|
||||
|
||||
使用转义字符清除终端屏幕,并将光标定位在左上角。
|
||||
|
||||
:cpp:func:`linenoiseSetMultiLine`
|
||||
- :cpp:func:`linenoiseSetMultiLine`
|
||||
|
||||
在单行和多行编辑模式之间进行切换。单行模式下,如果命令的长度超过终端的宽度,会在行内滚动命令文本以显示文本的结尾,在这种情况下,文本的开头部分会被隐藏。单行模式在每次按下按键时发送给屏幕刷新的数据比较少,与多行模式相比更不容易发生故障。另一方面,在单行模式下编辑命令和复制命令将变得更加困难。默认情况下开启的是单行模式。
|
||||
|
||||
:cpp:func:`linenoiseAllowEmpty`
|
||||
- :cpp:func:`linenoiseAllowEmpty`
|
||||
|
||||
设置 linenoise 库收到空行的解析行为,设置为 ``true`` 时返回长度为零的字符串 (``""``) ,设置为 ``false`` 时返回 ``NULL``。默认情况下,将返回长度为零的字符串。
|
||||
|
||||
:cpp:func:`linenoiseSetMaxLineLen`
|
||||
- :cpp:func:`linenoiseSetMaxLineLen`
|
||||
|
||||
设置 linenoise 库中每行的最大长度,默认长度为 4096 字节,可以通过更新该默认值来优化 RAM 内存的使用。
|
||||
|
||||
@@ -54,11 +55,11 @@ Linenoise 库不需要显式地初始化,但是在调用行编辑函数之前
|
||||
主循环
|
||||
^^^^^^
|
||||
|
||||
:cpp:func:`linenoise`
|
||||
- :cpp:func:`linenoise`
|
||||
|
||||
在大多数情况下,控制台应用程序都会具有相同的工作形式——在某个循环中不断读取输入的内容,然后解析再处理。 :cpp:func:`linenoise` 是专门用来获取用户按键输入的函数,当回车键被按下后会便返回完整的一行内容。因此可以用它来完成前面循环中的“读取”任务。
|
||||
在大多数情况下,控制台应用程序都会具有相同的工作形式——在某个循环中不断读取输入的内容,然后解析再处理。:cpp:func:`linenoise` 是专门用来获取用户按键输入的函数,当回车键被按下后会便返回完整的一行内容。因此可以用它来完成前面循环中的“读取”任务。
|
||||
|
||||
:cpp:func:`linenoiseFree`
|
||||
- :cpp:func:`linenoiseFree`
|
||||
|
||||
必须调用此函数才能释放从 :cpp:func:`linenoise` 函数获取的命令行缓冲区。
|
||||
|
||||
@@ -66,21 +67,21 @@ Linenoise 库不需要显式地初始化,但是在调用行编辑函数之前
|
||||
提示和补全
|
||||
^^^^^^^^^^
|
||||
|
||||
:cpp:func:`linenoiseSetCompletionCallback`
|
||||
- :cpp:func:`linenoiseSetCompletionCallback`
|
||||
|
||||
当用户按下制表键时, linenoise 会调用 **补全回调函数** ,该回调函数会检查当前已经输入的内容,然后调用 :cpp:func:`linenoiseAddCompletion` 函数来提供所有可能的补全后的命令列表。启用补全功能,需要事先调用 :cpp:func:`linenoiseSetCompletionCallback` 函数来注册补全回调函数。
|
||||
|
||||
``console`` 组件提供了一个现成的函数来为注册的命令提供补全功能 :cpp:func:`esp_console_get_completion` (见下文)。
|
||||
|
||||
:cpp:func:`linenoiseAddCompletion`
|
||||
- :cpp:func:`linenoiseAddCompletion`
|
||||
|
||||
补全回调函数会通过调用此函数来通知 linenoise 库当前键入命令所有可能的补全结果。
|
||||
|
||||
:cpp:func:`linenoiseSetHintsCallback`
|
||||
- :cpp:func:`linenoiseSetHintsCallback`
|
||||
|
||||
每当用户的输入改变时, linenoise 就会调用此回调函数,检查到目前为止输入的命令行内容,然后提供带有提示信息的字符串(例如命令参数列表),然后会在同一行上用不同的颜色显示出该文本。
|
||||
|
||||
:cpp:func:`linenoiseSetFreeHintsCallback`
|
||||
- :cpp:func:`linenoiseSetFreeHintsCallback`
|
||||
|
||||
如果 **提示回调函数** 返回的提示字符串是动态分配的或者需要以其它方式回收,就需要使用 :cpp:func:`linenoiseSetFreeHintsCallback` 注册具体的清理函数。
|
||||
|
||||
@@ -88,23 +89,23 @@ Linenoise 库不需要显式地初始化,但是在调用行编辑函数之前
|
||||
历史记录
|
||||
^^^^^^^^
|
||||
|
||||
:cpp:func:`linenoiseHistorySetMaxLen`
|
||||
- :cpp:func:`linenoiseHistorySetMaxLen`
|
||||
|
||||
该函数设置要保留在内存中的最近输入的命令的数量。用户通过使用向上/向下箭头来导航历史记录。
|
||||
|
||||
:cpp:func:`linenoiseHistoryAdd`
|
||||
- :cpp:func:`linenoiseHistoryAdd`
|
||||
|
||||
Linenoise 不会自动向历史记录中添加命令,应用程序需要调用此函数来将命令字符串添加到历史记录中。
|
||||
|
||||
:cpp:func:`linenoiseHistorySave`
|
||||
- :cpp:func:`linenoiseHistorySave`
|
||||
|
||||
该函数将命令的历史记录从 RAM 中保存为文本文件,例如保存到 SD 卡或者 Flash 的文件系统中。
|
||||
该函数将命令的历史记录从 RAM 中保存为文本文件,例如保存到 SD 卡或者 flash 的文件系统中。
|
||||
|
||||
:cpp:func:`linenoiseHistoryLoad`
|
||||
- :cpp:func:`linenoiseHistoryLoad`
|
||||
|
||||
与 ``linenoiseHistorySave`` 相对应,从文件中加载历史记录。
|
||||
|
||||
:cpp:func:`linenoiseHistoryFree`
|
||||
- :cpp:func:`linenoiseHistoryFree`
|
||||
|
||||
释放用于存储命令历史记录的内存。当使用完 linenoise 库后需要调用此函数。
|
||||
|
||||
@@ -123,9 +124,9 @@ Linenoise 库不需要显式地初始化,但是在调用行编辑函数之前
|
||||
|
||||
示例:
|
||||
|
||||
- ``abc def 1 20 .3`` ⟶ [ ``abc``, ``def``, ``1``, ``20``, ``.3`` ]
|
||||
- ``abc "123 456" def`` ⟶ [ ``abc``, ``123 456``, ``def`` ]
|
||||
- ```a\ b\\c\"`` ⟶ [ ``a b\c"`` ]
|
||||
- ``abc def 1 20 .3`` > [ ``abc``, ``def``, ``1``, ``20``, ``.3`` ]
|
||||
- ``abc "123 456" def`` > [ ``abc``, ``123 456``, ``def`` ]
|
||||
- ```a\ b\\c\"`` > [ ``a b\c"`` ]
|
||||
|
||||
|
||||
参数解析
|
||||
@@ -150,19 +151,19 @@ Linenoise 库不需要显式地初始化,但是在调用行编辑函数之前
|
||||
|
||||
命令注册模块还提供了其它函数:
|
||||
|
||||
:cpp:func:`esp_console_run`
|
||||
- :cpp:func:`esp_console_run`
|
||||
|
||||
该函数接受命令行字符串,使用 :cpp:func:`esp_console_split_argv` 函数将其拆分为 argc/argv 形式的参数列表,在已经注册的组件列表中查找命令,如果找到,则执行其对应的处理程序。
|
||||
|
||||
:cpp:func:`esp_console_register_help_command`
|
||||
- :cpp:func:`esp_console_register_help_command`
|
||||
|
||||
将 ``help`` 命令添加到已注册命令列表中,此命令将会以列表的方式打印所有注册的命令及其参数和帮助文本。
|
||||
|
||||
:cpp:func:`esp_console_get_completion`
|
||||
- :cpp:func:`esp_console_get_completion`
|
||||
|
||||
与 linenoise 库中的 :cpp:func:`linenoiseSetCompletionCallback` 一同使用的回调函数,根据已经注册的命令列表为 linenoise 提供补全功能。
|
||||
|
||||
:cpp:func:`esp_console_get_hint`
|
||||
- :cpp:func:`esp_console_get_hint`
|
||||
|
||||
与 linenoise 库中 :cpp:func:`linenoiseSetHintsCallback` 一同使用的回调函数,为 linenoise 提供已经注册的命令的参数提示功能。
|
||||
|
||||
@@ -183,9 +184,9 @@ Linenoise 库不需要显式地初始化,但是在调用行编辑函数之前
|
||||
应用程序示例
|
||||
------------
|
||||
|
||||
:example:`system/console` 目录下提供了 ``console`` 组件的示例应用程序,展示了具体的使用方法。该示例介绍了如何初始化 UART 和 VFS 的功能,设置 linenoise 库,从 UART 中读取命令并加以处理,然后将历史命令存储到 Flash 中。更多信息,请参阅示例代码目录中的 README.md 文件。
|
||||
:example:`system/console` 目录下提供了 ``console`` 组件的示例应用程序,展示了具体的使用方法。该示例介绍了如何初始化 UART 和 VFS 的功能,设置 linenoise 库,从 UART 中读取命令并加以处理,然后将历史命令存储到 flash 中。更多信息,请参阅示例代码目录中的 README.md 文件。
|
||||
|
||||
此外,ESP-IDF 还提供了众多基于 `console` 组件的示例程序,它们可以辅助应用程序的开发。例如,:example:`peripherals/i2c/i2c_tools`,:example:`wifi/iperf` 等等。
|
||||
此外,ESP-IDF 还提供了众多基于 ``console`` 组件的示例程序,它们可以辅助应用程序的开发。例如,:example:`peripherals/i2c/i2c_tools`,:example:`wifi/iperf` 等等。
|
||||
|
||||
|
||||
API 参考
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
|
||||
通过重复调用 :cpp:func:`esp_event_handler_register_with`,可以将单个处理程序独立注册到多个事件中,且每次调用均可指定处理程序应执行的具体事件根基和事件 ID。
|
||||
|
||||
然而,在某些情况下,您可能希望处理程序在以下情况时执行:
|
||||
然而,在某些情况下,你可能希望处理程序在以下情况时执行:
|
||||
|
||||
(1) 所有发布到循环的事件
|
||||
(2) 特定基本标识符的所有事件
|
||||
@@ -182,7 +182,7 @@
|
||||
处理程序自行注销
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
通常情况下,由事件循环运行的事件处理程序 *不允许在该事件循环上执行任何注册/注销活动* ,但允许处理程序自行注销。例如,可以执行以下操作:
|
||||
通常情况下,由事件循环运行的事件处理程序 **不允许在该事件循环上执行任何注册/注销活动**,但允许处理程序自行注销。例如,可以执行以下操作:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
ETM 事件
|
||||
---------
|
||||
|
||||
``esp_timer`` 的构建基于 *systimer* 硬件定时器,能够产生报警事件并与 :doc:`ETM </api-reference/peripherals/etm>` 模块交互。调用函数 :cpp:func:`esp_timer_new_etm_alarm_event` 以获取相应的 ETM 事件句柄。
|
||||
``esp_timer`` 的构建基于 **systimer** 硬件定时器,能够产生报警事件并与 :doc:`ETM </api-reference/peripherals/etm>` 模块交互。调用函数 :cpp:func:`esp_timer_new_etm_alarm_event` 以获取相应的 ETM 事件句柄。
|
||||
|
||||
如需了解如何将 ETM 事件连接到相应 ETM 通道,请参阅 :doc:`ETM </api-reference/peripherals/etm>`。
|
||||
|
||||
|
||||
@@ -349,7 +349,7 @@ ESP-IDF 集成了用于请求 :ref:`堆内存信息 <heap-information>`、:ref:`
|
||||
|
||||
1. 构建程序并将其下载到目标设备,详情请参阅 :ref:`第五步:开始使用 ESP-IDF 吧 <get-started-build>`。
|
||||
|
||||
2. 运行 OpenOCD (请参阅 :doc:`JTAG 调试 </api-guides/jtag-debugging/index>`)。
|
||||
2. 运行 OpenOCD(请参阅 :doc:`JTAG 调试 </api-guides/jtag-debugging/index>`)。
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
@@ -51,9 +51,13 @@ DRAM
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. note:: 有关 DRAM 使用限制的详细信息,请参阅 :ref:`dram` 。
|
||||
.. note::
|
||||
|
||||
.. note:: 运行时可用的 DRAM 堆空间可能少于编译时计算的大小,因为启动时会在运行 FreeRTOS 调度程序之前从堆中分配部分内存,包括初始 FreeRTOS 任务的栈内存。
|
||||
有关 DRAM 使用限制的详细信息,请参阅 :ref:`dram` 。
|
||||
|
||||
.. note::
|
||||
|
||||
运行时可用的 DRAM 堆空间可能少于编译时计算的大小,因为启动时会在运行 FreeRTOS 调度程序之前从堆中分配部分内存,包括初始 FreeRTOS 任务的栈内存。
|
||||
|
||||
IRAM
|
||||
^^^^
|
||||
@@ -105,7 +109,7 @@ DMA 存储器
|
||||
32 位可访问内存
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
如果某个内存结构体仅以 32 位为单位寻址,例如一个整数或指针数组,则可以使用 ``MALLOC_CAP_32BIT`` 标志分配。通过这一方式,分配器能够在无法调用 malloc() 的情况下提供 IRAM 内存,从而充分利用 {IDF_TARGET_NAME} 中的所有可用内存。
|
||||
如果某个内存结构体仅以 32 位为单位寻址,例如一个整数或指针数组,则可以使用 ``MALLOC_CAP_32BIT`` 标志分配。通过这一方式,分配器能够在无法调用 ``malloc()`` 的情况下提供 IRAM 内存,从而充分利用 {IDF_TARGET_NAME} 中的所有可用内存。
|
||||
|
||||
.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA and SOC_CPU_HAS_FPU
|
||||
|
||||
@@ -150,7 +154,9 @@ DMA 存储器
|
||||
* :cpp:func:`heap_caps_aligned_alloc`
|
||||
* :cpp:func:`heap_caps_aligned_free`
|
||||
|
||||
请注意,不建议使用此种方法。
|
||||
.. note::
|
||||
|
||||
不建议使用此种方法。
|
||||
|
||||
堆跟踪及调试
|
||||
------------------------
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
杂项系统 API
|
||||
=========================
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
{IDF_TARGET_BASE_MAC_BLOCK: default="BLK1", esp32="BLK0"}
|
||||
@@ -36,7 +37,7 @@ MAC 地址
|
||||
|
||||
要获取特定接口(如 Wi-Fi、蓝牙、以太网)的 MAC 地址,请调用函数 :cpp:func:`esp_read_mac`。
|
||||
|
||||
在 ESP-IDF 中,各个网络接口的 MAC 地址是根据单个 *基准 MAC 地址 (Base MAC address)* 计算出来的。默认情况下使用乐鑫指定的基准 MAC 地址,该基准地址在产品生产过程中已预烧录至 {IDF_TARGET_NAME} eFuse。
|
||||
在 ESP-IDF 中,各个网络接口的 MAC 地址是根据单个 **基准 MAC 地址 (Base MAC address)** 计算出来的。默认情况下使用乐鑫指定的基准 MAC 地址,该基准地址在产品生产过程中已预烧录至 {IDF_TARGET_NAME} eFuse。
|
||||
|
||||
.. only:: not esp32s2
|
||||
|
||||
@@ -45,7 +46,7 @@ MAC 地址
|
||||
:header-rows: 1
|
||||
|
||||
* - 接口
|
||||
- MAC 地址(默认 4 个全局地址)
|
||||
- MAC 地址(默认 4 个全局地址)
|
||||
- MAC 地址(2 个全局地址)
|
||||
* - Wi-Fi Station
|
||||
- base_mac
|
||||
@@ -71,7 +72,7 @@ MAC 地址
|
||||
:header-rows: 1
|
||||
|
||||
* - 接口
|
||||
- MAC 地址(默认 2 个全局地址)
|
||||
- MAC 地址(默认 2 个全局地址)
|
||||
- MAC 地址(1 个全局地址)
|
||||
* - Wi-Fi Station
|
||||
- base_mac
|
||||
@@ -89,7 +90,9 @@ MAC 地址
|
||||
|
||||
.. only:: not SOC_EMAC_SUPPORTED
|
||||
|
||||
.. note:: {IDF_TARGET_NAME} 内部未集成以太网 MAC 地址,但仍可以计算得出该地址。不过,以太网 MAC 地址只能与外部以太网接口(如 SPI 以太网设备)一起使用,具体请参阅 :doc:`/api-reference/network/esp_eth`。
|
||||
.. note::
|
||||
|
||||
{IDF_TARGET_NAME} 内部未集成以太网 MAC 地址,但仍可以计算得出该地址。不过,以太网 MAC 地址只能与外部以太网接口(如 SPI 以太网设备)一起使用,具体请参阅 :doc:`/api-reference/network/esp_eth`。
|
||||
|
||||
自定义接口 MAC
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
空中升级 (OTA)
|
||||
==============
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
OTA 流程概览
|
||||
@@ -7,7 +8,7 @@ OTA 流程概览
|
||||
|
||||
OTA 升级机制可以让设备在固件正常运行时根据接收数据(如通过 Wi-Fi 或蓝牙)进行自我更新。
|
||||
|
||||
要运行 OTA 机制,需配置设备的 :doc:`分区表 <../../api-guides/partition-tables>`,该分区表至少包括两个 OTA 应用程序分区(即 `ota_0` 和 `ota_1`)和一个 OTA 数据分区。
|
||||
要运行 OTA 机制,需配置设备的 :doc:`分区表 <../../api-guides/partition-tables>`,该分区表至少包括两个 OTA 应用程序分区(即 ``ota_0`` 和 ``ota_1``)和一个 OTA 数据分区。
|
||||
|
||||
OTA 功能启动后,向当前未用于启动的 OTA 应用分区写入新的应用固件镜像。镜像验证后,OTA 数据分区更新,指定在下一次启动时使用该镜像。
|
||||
|
||||
@@ -35,7 +36,9 @@ OTA 数据分区的容量是 2 个 flash 扇区的大小(0x2000 字节),
|
||||
* 应用程序出现严重错误,无法继续工作,必须回滚到此前的版本,:cpp:func:`esp_ota_mark_app_invalid_rollback_and_reboot` 将正在运行的版本标记为 ``ESP_OTA_IMG_INVALID`` 然后复位。引导加载程序不会选取此版本,而是启动此前正常运行的版本。
|
||||
* 如果 :ref:`CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE` 使能,则无需调用函数便可复位,回滚至之前的应用版本。
|
||||
|
||||
注解:应用程序的状态不是写到程序的二进制镜像,而是写到 ``otadata`` 分区。该分区有一个 ``ota_seq`` 计数器,该计数器是 OTA 应用分区的指针,指向下次启动时选取应用所在的分区 (ota_0, ota_1, ...)。
|
||||
.. note::
|
||||
|
||||
应用程序的状态不是写到程序的二进制镜像,而是写到 ``otadata`` 分区。该分区有一个 ``ota_seq`` 计数器,该计数器是 OTA 应用分区的指针,指向下次启动时选取应用所在的分区 (ota_0, ota_1, ...)。
|
||||
|
||||
应用程序 OTA 状态
|
||||
^^^^^^^^^^^^^^^^^
|
||||
@@ -73,7 +76,7 @@ Kconfig 中的 :ref:`CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE` 可以帮助用户
|
||||
* 引导加载程序选取一个新版应用程序来引导,这样应用程序状态就不会设置为 ``ESP_OTA_IMG_INVALID`` 或 ``ESP_OTA_IMG_ABORTED``。
|
||||
* 引导加载程序检查所选取的新版应用程序,若状态设置为 ``ESP_OTA_IMG_NEW``,则写入 ``ESP_OTA_IMG_PENDING_VERIFY``。该状态表示,需确认应用程序的可操作性,如不确认,发生重启,则状态会重写为 ``ESP_OTA_IMG_ABORTED`` (见上文),该应用程序不可再启动,将回滚至上一版本。
|
||||
* 新版应用程序启动,应进行自测。
|
||||
* 若通过自测,则必须调用函数 :cpp:func:`esp_ota_mark_app_valid_cancel_rollback`,因为新版应用程序在等待确认其可操作性 (``ESP_OTA_IMG_PENDING_VERIFY`` 状态)。
|
||||
* 若通过自测,则必须调用函数 :cpp:func:`esp_ota_mark_app_valid_cancel_rollback`,因为新版应用程序在等待确认其可操作性( ``ESP_OTA_IMG_PENDING_VERIFY`` 状态)。
|
||||
* 若未通过自测,则调用函数 :cpp:func:`esp_ota_mark_app_invalid_rollback_and_reboot`,回滚至之前能正常工作的应用程序版本,同时将无效的新版本应用程序设置为 ``ESP_OTA_IMG_INVALID``。
|
||||
* 如果新版应用程序可操作性没有确认,则状态一直为 ``ESP_OTA_IMG_PENDING_VERIFY``。下一次启动时,状态变更为 ``ESP_OTA_IMG_ABORTED``,阻止其再次启动,之后回滚到之前的版本。
|
||||
|
||||
@@ -195,10 +198,10 @@ Kconfig 中的 :ref:`CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE` 可以帮助用户
|
||||
具体可参考 :ref:`signed-app-verify`。
|
||||
|
||||
|
||||
OTA 工具 (otatool.py)
|
||||
---------------------
|
||||
OTA 工具 ``otatool.py``
|
||||
----------------------------
|
||||
|
||||
`app_update` 组件中有 :component_file:`otatool.py<app_update/otatool.py>` 工具,用于在目标设备上完成下列 OTA 分区相关操作:
|
||||
``app_update`` 组件中有 :component_file:`otatool.py<app_update/otatool.py>` 工具,用于在目标设备上完成下列 OTA 分区相关操作:
|
||||
|
||||
- 读取 otadata 分区 (read_otadata)
|
||||
- 擦除 otadata 分区,将设备复位至工厂应用程序 (erase_otadata)
|
||||
@@ -212,7 +215,7 @@ OTA 工具 (otatool.py)
|
||||
Python API
|
||||
^^^^^^^^^^
|
||||
|
||||
首先,确保已导入 `otatool` 模块。
|
||||
首先,确保已导入 ``otatool`` 模块。
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@@ -255,7 +258,7 @@ Python API
|
||||
命令行界面
|
||||
^^^^^^^^^^
|
||||
|
||||
`otatool.py` 的命令行界面具有如下结构:
|
||||
``otatool.py`` 的命令行界面具有如下结构:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -280,7 +283,7 @@ Python API
|
||||
otatool.py --port "/dev/ttyUSB1" read_ota_partition --name=ota_3 --output=ota_3.bin
|
||||
|
||||
|
||||
更多信息可用 `--help` 指令查看:
|
||||
更多信息可用 ``--help`` 指令查看:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
||||
@@ -73,18 +73,18 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
|
||||
{IDF_TARGET_NAME} 电源管理算法
|
||||
--------------------------------
|
||||
|
||||
下表列出了启用动态调频时如何切换 CPU 频率和 APB 频率。您可以使用 :cpp:func:`esp_pm_configure` 或者 :ref:`CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ` 指定 CPU 最大频率。
|
||||
下表列出了启用动态调频时如何切换 CPU 频率和 APB 频率。可以使用 :cpp:func:`esp_pm_configure` 或 :ref:`CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ` 指定 CPU 最大频率。
|
||||
|
||||
.. include:: inc/power_management_{IDF_TARGET_PATH_NAME}.rst
|
||||
|
||||
如果没有获取任何管理锁,调用 :cpp:func:`esp_pm_configure` 将启动 Light-sleep 模式。 Light-sleep 模式持续时间由以下因素决定:
|
||||
如果没有获取任何管理锁,调用 :cpp:func:`esp_pm_configure` 将启动 Light-sleep 模式。Light-sleep 模式持续时间由以下因素决定:
|
||||
|
||||
- 处于阻塞状态的 FreeRTOS 任务数(有限超时)
|
||||
- :doc:`高分辨率定时器 <esp_timer>` API 注册的计数器数量
|
||||
|
||||
您也可以设置 Light-sleep 模式在最近事件(任务解除阻塞,或计时器超时)之前持续多久才唤醒芯片。
|
||||
也可以设置 Light-sleep 模式在最近事件(任务解除阻塞,或计时器超时)之前的持续时间,在持续时间结束后再唤醒芯片。
|
||||
|
||||
为了跳过不必要的唤醒,可以将 `skip_unhandled_events` 选项设置为 true 来初始化 esp_timer。带有此标志的定时器不会唤醒系统,有助于减少功耗。
|
||||
为了跳过不必要的唤醒,可以将 ``skip_unhandled_events`` 选项设置为 ``true`` 来初始化 ``esp_timer``。带有此标志的定时器不会唤醒系统,有助于减少功耗。
|
||||
|
||||
|
||||
动态调频和外设驱动
|
||||
@@ -94,11 +94,11 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
|
||||
|
||||
以下外设不受 APB 频率变更的影响:
|
||||
|
||||
- **UART**:如果 REF_TICK 或者 XTAL 用作时钟源,则 UART 不受 APB 频率变更影响。请查看 :cpp:member:`uart_config_t::source_clk`。
|
||||
- **LEDC**:如果 REF_TICK 用作时钟源,则 LEDC 不受 APB 频率变更影响。请查看 :cpp:func:`ledc_timer_config` 函数。
|
||||
- **RMT**:如果 REF_TICK 或者 XTAL 被用作时钟源,则 RMT 不受 APB 频率变更影响。请查看 :cpp:member:`rmt_config_t::flags` 以及 `RMT_CHANNEL_FLAGS_AWARE_DFS` 宏。
|
||||
- **UART**:如果 ``REF_TICK`` 或者 XTAL 用作时钟源,则 UART 不受 APB 频率变更影响。请查看 :cpp:member:`uart_config_t::source_clk`。
|
||||
- **LEDC**:如果 ``REF_TICK`` 用作时钟源,则 LEDC 不受 APB 频率变更影响。请查看 :cpp:func:`ledc_timer_config` 函数。
|
||||
- **RMT**:如果 ``REF_TICK`` 或者 XTAL 被用作时钟源,则 RMT 不受 APB 频率变更影响。请查看 :cpp:member:`rmt_config_t::flags` 以及 `RMT_CHANNEL_FLAGS_AWARE_DFS` 宏。
|
||||
- **GPTimer**:如果 XTAL 用作时钟源,则 GPTimer 不受 APB 频率变更影响。请查看 :cpp:member:`gptimer_config_t::clk_src`。
|
||||
- **TSENS**:XTAL 或 RTC_8M 用作时钟源,因此不受 APB 频率变化影响。
|
||||
- **TSENS**:XTAL 或 ``RTC_8M`` 用作时钟源,因此不受 APB 频率变化影响。
|
||||
|
||||
目前以下外设驱动程序可感知动态调频,并在调频期间使用 ``ESP_PM_APB_FREQ_MAX`` 锁:
|
||||
|
||||
@@ -124,7 +124,7 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
|
||||
|
||||
- PCNT
|
||||
- Sigma-delta
|
||||
- 旧版定时器驱动(Timer Group)
|
||||
- 旧版定时器驱动 (Timer Group)
|
||||
:SOC_MCPWM_SUPPORTED: - MCPWM
|
||||
|
||||
|
||||
@@ -135,8 +135,7 @@ Light-sleep 外设下电
|
||||
|
||||
{IDF_TARGET_NAME} 支持在 Light-sleep 时掉电外设的电源域.
|
||||
|
||||
如果在 menuconfig 中启用了 :ref:`CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP`,在初始化外设时,驱动会将外设工作的寄存器上下文注册到休眠备份链表中,
|
||||
在进入休眠前,REG_DMA 外设会读取休眠备份链表中的配置,根据链表中的配置将外设的寄存器上下文备份至内存,REG_DMA 也会在唤醒时将上下文从内存恢复到外设寄存中。
|
||||
如果在 menuconfig 中启用了 :ref:`CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP`,在初始化外设时,驱动会将外设工作的寄存器上下文注册到休眠备份链表中,在进入休眠前,``REG_DMA`` 外设会读取休眠备份链表中的配置,根据链表中的配置将外设的寄存器上下文备份至内存,``REG_DMA`` 也会在唤醒时将上下文从内存恢复到外设寄存中。
|
||||
|
||||
目前 IDF 支持以下外设的 Light-sleep 上下文备份:
|
||||
- INT_MTX
|
||||
@@ -168,7 +167,8 @@ Light-sleep 外设下电
|
||||
- PARL_IO
|
||||
- UART1
|
||||
|
||||
对于未支持 Light-sleep 上下文备份的外设,若启用了电源管理功能,应在外设工作时持有 `ESP_PM_NO_LIGHT_SLEEP` 锁以避免进入休眠导致外设工作上下文丢失。
|
||||
对于未支持 Light-sleep 上下文备份的外设,若启用了电源管理功能,应在外设工作时持有 ``ESP_PM_NO_LIGHT_SLEEP`` 锁以避免进入休眠导致外设工作上下文丢失。
|
||||
|
||||
|
||||
API 参考
|
||||
-------------
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
睡眠模式
|
||||
===========
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
{IDF_TARGET_SPI_POWER_DOMAIN:default="VDD_SPI", esp32="VDD_SDIO"}
|
||||
@@ -65,7 +66,7 @@ RTC 控制器中内嵌定时器,可用于在预定义的时间到达后唤醒
|
||||
|
||||
.. only:: SOC_ULP_SUPPORTED
|
||||
|
||||
关于 RTC 时钟选项的更多细节,请参考 *{IDF_TARGET_NAME} 技术参考手册* > *ULP 协处理器* [`PDF <{IDF_TARGET_TRM_EN_URL}#ulp>`__]。
|
||||
关于 RTC 时钟选项的更多细节,请参考 **{IDF_TARGET_NAME} 技术参考手册** > **ULP 协处理器** [`PDF <{IDF_TARGET_TRM_EN_URL}#ulp>`__]。
|
||||
|
||||
在这种唤醒模式下,无需为睡眠模式中的 RTC 外设或内存供电。
|
||||
|
||||
@@ -86,7 +87,7 @@ RTC 控制器中内嵌定时器,可用于在预定义的时间到达后唤醒
|
||||
|
||||
.. only:: SOC_PM_SUPPORT_EXT0_WAKEUP
|
||||
|
||||
外部唤醒 (ext0)
|
||||
外部唤醒 (``ext0``)
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
RTC IO 模块中包含这样一个逻辑——当某个 RTC GPIO 被设置为预定义的逻辑值时,触发唤醒。RTC IO 是 RTC 外设电源域的一部分,因此如果该唤醒源被请求,RTC 外设将在 Deep-sleep 模式期间保持供电。
|
||||
@@ -99,14 +100,16 @@ RTC 控制器中内嵌定时器,可用于在预定义的时间到达后唤醒
|
||||
|
||||
可调用 :cpp:func:`esp_sleep_enable_ext0_wakeup` 函数来启用此唤醒源。
|
||||
|
||||
.. warning:: 从睡眠模式中唤醒后,用于唤醒的 IO pad 将被配置为 RTC IO。因此,在将该 pad 用作数字 GPIO 之前,请调用 :cpp:func:`rtc_gpio_deinit` 函数对其进行重新配置。
|
||||
.. warning::
|
||||
|
||||
从睡眠模式中唤醒后,用于唤醒的 IO pad 将被配置为 RTC IO。因此,在将该 pad 用作数字 GPIO 之前,请调用 :cpp:func:`rtc_gpio_deinit` 函数对其进行重新配置。
|
||||
|
||||
.. only:: SOC_PM_SUPPORT_EXT1_WAKEUP
|
||||
|
||||
外部唤醒 (ext1)
|
||||
外部唤醒 (``ext1``)
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
RTC 控制器中包含使用多个 RTC GPIO 触发唤醒的逻辑。您可以从以下两个逻辑函数中选择其一,用于触发唤醒:
|
||||
RTC 控制器中包含使用多个 RTC GPIO 触发唤醒的逻辑。从以下两个逻辑函数中任选其一,均可触发唤醒:
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
@@ -135,9 +138,10 @@ RTC 控制器中内嵌定时器,可用于在预定义的时间到达后唤醒
|
||||
gpio_pulldown_en(gpio_num);
|
||||
|
||||
.. warning::
|
||||
|
||||
- 使用 EXT1 唤醒源时,用于唤醒的 IO pad 将被配置为 RTC IO。因此,在将该 pad 用作数字 GPIO 之前,请调用 :cpp:func:`rtc_gpio_deinit` 函数对其进行重新配置。
|
||||
|
||||
- RTC 外设在默认情况下配置为断电,此时,唤醒 IO 在进入睡眠状态前将被设置为保持状态。因此,从 Light-sleep 状态唤醒芯片后,请调用 `rtc_gpio_hold_dis` 来禁用保持功能,以便对管脚进行重新配置。对于 Deep-sleep 唤醒,此问题已经在应用启动阶段解决。
|
||||
- RTC 外设在默认情况下配置为断电,此时,唤醒 IO 在进入睡眠状态前将被设置为保持状态。因此,从 Light-sleep 状态唤醒芯片后,请调用 ``rtc_gpio_hold_dis`` 来禁用保持功能,以便对管脚进行重新配置。对于 Deep-sleep 唤醒,此问题已经在应用启动阶段解决。
|
||||
|
||||
可调用 :cpp:func:`esp_sleep_enable_ext1_wakeup` 函数来启用此唤醒源。
|
||||
|
||||
@@ -170,9 +174,10 @@ RTC 控制器中内嵌定时器,可用于在预定义的时间到达后唤醒
|
||||
可调用 :cpp:func:`esp_sleep_enable_gpio_wakeup` 函数来启用此唤醒源。
|
||||
|
||||
.. warning::
|
||||
在进入 Light-sleep 模式前,请查看您将要驱动的 GPIO 管脚的电源域。如果有管脚属于 {IDF_TARGET_SPI_POWER_DOMAIN} 电源域,必须将此电源域配置为在睡眠期间保持供电。
|
||||
|
||||
例如,在 ESP32-WROOM-32 开发板上,GPIO16 和 GPIO17 连接到 {IDF_TARGET_SPI_POWER_DOMAIN} 电源域。如果这两个管脚被配置为在睡眠期间保持高电平,则您需将对应电源域配置为保持供电。您可以使用函数 :cpp:func:`esp_sleep_pd_config()`::
|
||||
在进入 Light-sleep 模式前,请查看将要驱动的 GPIO 管脚的电源域。如果有管脚属于 {IDF_TARGET_SPI_POWER_DOMAIN} 电源域,必须将此电源域配置为在睡眠期间保持供电。
|
||||
|
||||
例如,在 ESP32-WROOM-32 开发板上,GPIO16 和 GPIO17 连接到 {IDF_TARGET_SPI_POWER_DOMAIN} 电源域。如果这两个管脚被配置为在睡眠期间保持高电平,则需将对应电源域配置为保持供电。为此,可以使用函数 :cpp:func:`esp_sleep_pd_config()`::
|
||||
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_VDDSDIO, ESP_PD_OPTION_ON);
|
||||
|
||||
@@ -221,9 +226,9 @@ RTC 外设和内存断电
|
||||
|
||||
.. only:: not SOC_RTC_SLOW_MEM_SUPPORTED and SOC_RTC_FAST_MEM_SUPPORTED
|
||||
|
||||
{IDF_TARGET_NAME} 中只有 RTC 高速内存,因此,如果程序中的某些值被标记为 ``RTC_DATA_ATTR``、``RTC_SLOW_ATTR`` 或 ``RTC_FAST_ATTR`` 属性,那么所有这些值都将被存入 RTC 高速内存,默认情况下保持供电。如果有需要,您也可以使用函数 :cpp:func:`esp_sleep_pd_config` 对其进行修改。
|
||||
{IDF_TARGET_NAME} 中只有 RTC 高速内存,因此,如果程序中的某些值被标记为 ``RTC_DATA_ATTR``、``RTC_SLOW_ATTR`` 或 ``RTC_FAST_ATTR`` 属性,那么所有这些值都将被存入 RTC 高速内存,默认情况下保持供电。如有需要,也可以使用函数 :cpp:func:`esp_sleep_pd_config` 对其进行修改。
|
||||
|
||||
Flash 断电
|
||||
flash 断电
|
||||
^^^^^^^^^^
|
||||
|
||||
默认情况下,调用函数 :cpp:func:`esp_light_sleep_start` 后, flash **不会** 断电,因为在 sleep 过程中断电 flash 存在风险。具体而言,flash 断电需要时间,但是在此期间,系统有可能被唤醒,导致 flash 重新被上电。此时,断电尚未完成又重新上电的硬件行为有可能导致 flash 无法正常工作。
|
||||
@@ -231,9 +236,10 @@ Flash 断电
|
||||
理论上讲,在 flash 完全断电后可以仅唤醒系统,然而现实情况是 flash 断电所需的时间很难预测。如果用户为 flash 供电电路添加了滤波电容,断电所需时间可能会更长。此外,即使可以预知 flash 彻底断电所需的时间,有时也不能通过设置足够长的睡眠时间来确保 flash 断电的安全(比如,突发的异步唤醒源会使得实际的睡眠时间不可控)。
|
||||
|
||||
.. warning::
|
||||
|
||||
如果在 flash 的供电电路上添加了滤波电容,那么应当尽一切可能避免 flash 断电。
|
||||
|
||||
因为这些不可控的因素,ESP-IDF 很难保证 flash断电的绝对安全。因此 ESP-IDF 不推荐用户断电 flash。对于一些功耗敏感型应用,可以通过设置 Kconfig 配置项 :ref:`CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND` 来减少 light sleep 期间 flash 的功耗。这种方式在几乎所有场景下都要比断电 flash 更好,兼顾了安全性和功耗。
|
||||
因为这些不可控的因素,ESP-IDF 很难保证 flash 断电的绝对安全。因此 ESP-IDF 不推荐用户断电 flash。对于一些功耗敏感型应用,可以通过设置 Kconfig 配置项 :ref:`CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND` 来减少 light sleep 期间 flash 的功耗。这种方式在几乎所有场景下都要比断电 flash 更好,兼顾了安全性和功耗。
|
||||
|
||||
.. only:: SOC_SPIRAM_SUPPORTED
|
||||
|
||||
@@ -283,10 +289,8 @@ Flash 断电
|
||||
|
||||
应用程序通过 API :cpp:func:`esp_light_sleep_start` 或 :cpp:func:`esp_deep_sleep_start` 进入 Light-sleep 或 Deep-sleep 模式。此时,系统将按照被请求的唤醒源和断电选项配置有关的 RTC 控制器参数。
|
||||
|
||||
|
||||
允许在未配置唤醒源的情况下进入睡眠模式。在此情况下,芯片将一直处于睡眠模式,直到从外部被复位。
|
||||
|
||||
|
||||
UART 输出处理
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -309,7 +313,6 @@ UART 输出处理
|
||||
|
||||
对于 ext1 唤醒源,可以调用函数 :cpp:func:`esp_sleep_get_ext1_wakeup_status` 来确认触发唤醒的触摸管脚。
|
||||
|
||||
|
||||
应用程序示例
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ RTC 定时器有以下时钟源:
|
||||
获取当前时间
|
||||
--------------
|
||||
|
||||
要获取当前时间,请使用 POSIX 函数 ``gettimeofday()``。此外,您也可以使用以下标准 C 库函数来获取时间并对其进行操作:
|
||||
要获取当前时间,请使用 POSIX 函数 ``gettimeofday()``。此外,也可以使用以下标准 C 库函数来获取时间并对其进行操作:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -138,7 +138,7 @@ lwIP SNTP 库可在下列任一同步模式下工作:
|
||||
|
||||
如需查看示例代码,请前往 :example:`protocols/sntp` 目录。该目录下的示例展示了如何基于 lwIP SNTP 库实现时间同步。
|
||||
|
||||
您也可以直接使用 lwIP API,但请务必注意线程安全。线程安全的 API 如下:
|
||||
也可以直接使用 lwIP API,但请务必注意线程安全。线程安全的 API 如下:
|
||||
|
||||
- :cpp:func:`sntp_set_time_sync_notification_cb` 用于设置通知时间同步过程的回调函数。
|
||||
- :cpp:func:`sntp_get_sync_status` 和 :cpp:func:`sntp_set_sync_status` 用于获取/设置时间同步状态。
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
ULP RISC-V 协处理器编程
|
||||
==================================
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
ULP RISC-V 协处理器是 ULP 的一种变体,用于 {IDF_TARGET_NAME}。与 ULP FSM 类似,ULP RISC-V 协处理器可以在主 CPU 处于低功耗模式时执行传感器读数等任务。其与 ULP FSM 的主要区别在于,ULP RISC-V 可以通过标准 GNU 工具使用 C 语言进行编程。ULP RISC-V 可以访问 RTC_SLOW_MEM 内存区域及 RTC_CNTL、RTC_IO、SARADC 等外设的寄存器。RISC-V 处理器是一种 32 位定点处理器,指令集基于 RV32IMC,包括硬件乘除法和压缩指令。
|
||||
ULP RISC-V 协处理器是 ULP 的一种变体,用于 {IDF_TARGET_NAME}。与 ULP FSM 类似,ULP RISC-V 协处理器可以在主 CPU 处于低功耗模式时执行传感器读数等任务。其与 ULP FSM 的主要区别在于,ULP RISC-V 可以通过标准 GNU 工具使用 C 语言进行编程。ULP RISC-V 可以访问 RTC_SLOW_MEM 内存区域及 ``RTC_CNTL``、``RTC_IO``、``SARADC`` 等外设的寄存器。RISC-V 处理器是一种 32 位定点处理器,指令集基于 RV32IMC,包括硬件乘除法和压缩指令。
|
||||
|
||||
安装 ULP RISC-V 工具链
|
||||
-----------------------------------
|
||||
|
||||
ULP RISC-V 协处理器代码以 C 语言(或汇编语言)编写,使用基于 GCC 的 RISC-V 工具链进行编译。
|
||||
|
||||
如果您已依照 :doc:`快速入门指南 <../../../get-started/index>` 中的介绍安装好了 ESP-IDF 及其 CMake 构建系统,那么 ULP RISC-V 工具链已经被默认安装到了您的开发环境中。
|
||||
如果依照 :doc:`快速入门指南 <../../../get-started/index>` 中的介绍安装好了 ESP-IDF 及其 CMake 构建系统,那么 ULP RISC-V 工具链已经被默认安装到了你的开发环境中。
|
||||
|
||||
.. note:: 在早期版本的 ESP-IDF 中,RISC-V 工具链具有不同的名称:`riscv-none-embed-gcc`。
|
||||
.. note::
|
||||
|
||||
在早期版本的 ESP-IDF 中,RISC-V 工具链具有不同的名称:``riscv-none-embed-gcc``。
|
||||
|
||||
编译 ULP RISC-V 代码
|
||||
-----------------------------
|
||||
|
||||
要将 ULP RISC-V 代码编译为某组件的一部分,必须执行以下步骤:
|
||||
|
||||
1. ULP RISC-V 代码以 C 语言或汇编语言编写(必须使用 `.S` 扩展名),必须放在组件目录中一个独立的目录中,例如 `ulp/`。
|
||||
1. ULP RISC-V 代码以 C 语言或汇编语言编写(必须使用 ``.S`` 扩展名),必须放在组件目录中一个独立的目录中,例如 ``ulp/``。
|
||||
|
||||
.. note:: 当注册组件时(通过 ``idf_component_register``),该目录不应被添加至 ``SRC_DIRS`` 参数,因为目前该步骤需用于 ULP FSM。如何正确添加 ULP 源文件,请见以下步骤。
|
||||
.. note::
|
||||
|
||||
当注册组件时(通过 ``idf_component_register``),该目录不应被添加至 ``SRC_DIRS`` 参数,因为目前该步骤需用于 ULP FSM。如何正确添加 ULP 源文件,请见以下步骤。
|
||||
|
||||
2. 注册后从组件 CMakeLists.txt 中调用 ``ulp_embed_binary`` 示例如下::
|
||||
|
||||
@@ -35,11 +40,11 @@ ULP RISC-V 协处理器代码以 C 语言(或汇编语言)编写,使用基
|
||||
|
||||
``ulp_embed_binary`` 的第一个参数指定生成的 ULP 二进制文件名。生成的其他文件,如 ELF 文件、.map 文件、头文件和链接器导出文件等也可使用此名称。第二个参数指定 ULP 源文件。最后,第三个参数指定组件源文件列表,其中包括生成的头文件。此列表用以正确构建依赖,并确保在构建过程中先生成后编译包含头文件的源文件。请参考下文,查看为 ULP 应用程序生成的头文件等相关概念。
|
||||
|
||||
3. 使用常规方法(例如 `idf.py app`)编译应用程序。
|
||||
3. 使用常规方法(例如 ``idf.py app``)编译应用程序。
|
||||
|
||||
在内部,构建系统将按照以下步骤编译 ULP 程序:
|
||||
|
||||
1. **通过 C 编译器和汇编器运行每个源文件。** 此步骤在组件编译目录中生成目标文件(.obj.c 或 .obj.S,取决于处理的源文件)。
|
||||
1. **通过 C 编译器和汇编器运行每个源文件。** 此步骤在组件编译目录中生成目标文件( ``.obj.c`` 或 ``.obj.S``,取决于处理的源文件)。
|
||||
|
||||
2. **通过 C 预处理器运行链接器脚本模版。** 模版位于 ``components/ulp/ld`` 目录中。
|
||||
|
||||
@@ -115,7 +120,7 @@ ULP 中的所有硬件指令都不支持互斥,所以 Lock API 需通过一种
|
||||
|
||||
要运行 ULP RISC-V 程序,主程序需要调用 :cpp:func:`ulp_riscv_load_binary` 函数,将 ULP 程序加载到 RTC 内存中,然后调用 :cpp:func:`ulp_riscv_run` 函数,启动 ULP RISC-V 程序。
|
||||
|
||||
注意,必须在 menuconfig 中启用 `CONFIG_ULP_COPROC_ENABLED` 和 `CONFIG_ULP_COPROC_TYPE_RISCV` 选项,以便正常运行 ULP RISC-V 程序。``RTC slow memory reserved for coprocessor`` 选项设置的值必须足够存储 ULP RISC-V 代码和数据。如果应用程序组件包含多个 ULP 程序,RTC 内存必须足以容纳最大的程序。
|
||||
注意,必须在 menuconfig 中启用 ``CONFIG_ULP_COPROC_ENABLED`` 和 ``CONFIG_ULP_COPROC_TYPE_RISCV`` 选项,以便正常运行 ULP RISC-V 程序。``RTC slow memory reserved for coprocessor`` 选项设置的值必须足够存储 ULP RISC-V 代码和数据。如果应用程序组件包含多个 ULP 程序,RTC 内存必须足以容纳最大的程序。
|
||||
|
||||
每个 ULP RISC-V 程序均以二进制 BLOB 的形式嵌入到 ESP-IDF 应用程序中。应用程序可以引用此 BLOB,并以下面的方式加载此 BLOB(假设 ULP_APP_NAME 已被定义为 ``ulp_app_name``):
|
||||
|
||||
@@ -162,9 +167,13 @@ RTC I2C 控制器提供了在 RTC 电源域中作为 I2C 主机的功能。ULP R
|
||||
|
||||
初始化 RTC I2C 控制器之后,请务必先用 :cpp:func:`ulp_riscv_i2c_master_set_slave_addr` API 将 I2C 从机设备地址编入程序,再执行读写操作。
|
||||
|
||||
.. note:: RTC I2C 外设首先将检查 :cpp:func:`ulp_riscv_i2c_master_set_slave_reg_addr` API 是否将从机子寄存器地址编入程序。如未编入,I2C 外设将以 ``SENS_SAR_I2C_CTRL_REG[18:11]`` 作为后续读写操作的子寄存器地址。这可能会导致 RTC I2C 外设与某些无需对子寄存器进行配置的 I2C 设备或传感器不兼容。
|
||||
.. note::
|
||||
|
||||
.. note:: 在主 CPU 访问 RTC I2C 外设和 ULP RISC-V 内核访问 RTC I2C 外设之间,未提供硬件原子操作的正确性保护,因此请勿让两个内核同时访问外设。
|
||||
RTC I2C 外设首先将检查 :cpp:func:`ulp_riscv_i2c_master_set_slave_reg_addr` API 是否将从机子寄存器地址编入程序。如未编入,I2C 外设将以 ``SENS_SAR_I2C_CTRL_REG[18:11]`` 作为后续读写操作的子寄存器地址。这可能会导致 RTC I2C 外设与某些无需对子寄存器进行配置的 I2C 设备或传感器不兼容。
|
||||
|
||||
.. note::
|
||||
|
||||
在主 CPU 访问 RTC I2C 外设和 ULP RISC-V 内核访问 RTC I2C 外设之间,未提供硬件原子操作的正确性保护,因此请勿让两个内核同时访问外设。
|
||||
|
||||
如果基于 RTC I2C 的 ULP RISC-V 程序未按预期运行,可以进行以下完整性检查排查问题:
|
||||
|
||||
@@ -174,22 +183,22 @@ RTC I2C 控制器提供了在 RTC 电源域中作为 I2C 主机的功能。ULP R
|
||||
|
||||
* 如果 I2C 从机设备或传感器不需要子寄存器地址进行配置,它可能与 RTC I2C 外设不兼容。请参考前文注意事项。
|
||||
|
||||
* 如果 RTC 驱动程序在主 CPU 上运行时出现 `Write Failed!` 或 `Read Failed!` 的错误日志,检查是否出现以下情况:
|
||||
* 如果 RTC 驱动程序在主 CPU 上运行时出现 ``Write Failed!`` 或 ``Read Failed!`` 的错误日志,检查是否出现以下情况:
|
||||
|
||||
* I2C 从机设备或传感器与乐鑫 SoC 上的标准 I2C 主机设备一起正常工作,说明 I2C 从机设备本身没有问题。
|
||||
* 如果 RTC I2C 中断状态日志报告 `TIMEOUT` 错误或 `ACK` 错误,则通常表示 I2C 设备未响应 RTC I2C 控制器发出的 `START` 条件。如果 I2C 从机设备未正确连接到控制器管脚或处于异常状态,则可能会发生这种情况。在进行后续操作之前,请确保 I2C 从机设备状态良好且连接正确。
|
||||
* 如果 RTC I2C 中断状态日志报告 ``TIMEOUT`` 错误或 ``ACK`` 错误,则通常表示 I2C 设备未响应 RTC I2C 控制器发出的 ``START`` 条件。如果 I2C 从机设备未正确连接到控制器管脚或处于异常状态,则可能会发生这种情况。在进行后续操作之前,请确保 I2C 从机设备状态良好且连接正确。
|
||||
* 如果 RTC I2C 中断日志没有报告任何错误状态,则可能表示驱动程序接收 I2C 从机设备数据时速度较慢。这可能是由于 RTC I2C 控制器没有 TX/RX FIFO 来存储多字节数据,而是依赖于使用中断状态轮询机制来进行单字节传输。通过在外设的初始化配置参数中设置 SCL 低周期和 SCL 高周期,可以尽量提高外设 SCL 时钟的运行速度,在一定程度上缓解这一问题。
|
||||
|
||||
* **您还可以检查在没有任何 ULP RISC-V 代码干扰和任何睡眠模式未被激活的情况下,RTC I2C 控制器是否仅在主 CPU 上正常工作。** RTC I2C 外设在此基本配置下应该正常工作,这样可以排除 ULP 或睡眠模式导致的潜在问题。
|
||||
* **你还可以检查在没有任何 ULP RISC-V 代码干扰和任何睡眠模式未被激活的情况下,RTC I2C 控制器是否仅在主 CPU 上正常工作。** RTC I2C 外设在此基本配置下应该正常工作,这样可以排除 ULP 或睡眠模式导致的潜在问题。
|
||||
|
||||
调试 ULP RISC-V 程序
|
||||
----------------------------------
|
||||
|
||||
在对 ULP RISC-V 进行配置时,若程序未按预期运行,有时很难找出的原因。因为其内核的简单性,许多标准的调试方法如 JTAG 或 ``printf`` 无法使用。
|
||||
|
||||
以下方法可以帮助您调试 ULP RISC-V 程序:
|
||||
以下方法可以调试 ULP RISC-V 程序:
|
||||
|
||||
* 通过共享变量查看程序状态:如 :ref:`ulp-riscv-access-variables` 中所述,主 CPU 以及 ULP 内核都可以轻松访问 RTC 内存中的全局变量。通过 ULP 向该变量中写入状态信息,然后通过主 CPU 读取状态信息,可帮助您了解 ULP 内核的状态。该方法的缺点在于它要求主 CPU 一直处于唤醒状态,但现实情况可能并非如此。有时,保持主 CPU 处于唤醒状态还可能会掩盖一些问题,因为某些问题可能仅在特定电源域断电时才会出现。
|
||||
* 通过共享变量查看程序状态:如 :ref:`ulp-riscv-access-variables` 中所述,主 CPU 以及 ULP 内核都可以轻松访问 RTC 内存中的全局变量。通过 ULP 向该变量中写入状态信息,然后通过主 CPU 读取状态信息,有助于了解 ULP 内核的状态。该方法的缺点在于它要求主 CPU 一直处于唤醒状态,但现实情况可能并非如此。有时,保持主 CPU 处于唤醒状态还可能会掩盖一些问题,因为某些问题可能仅在特定电源域断电时才会出现。
|
||||
|
||||
* 使用 bit-banged UART 驱动程序打印:ULP RISC-V 组件中有一个低速 bit-banged UART TX 驱动程序,可用于打印独立于主 CPU 状态的信息。有关如何使用此驱动程序的示例,请参阅 :example:`system/ulp/ulp_riscv/uart_print`。
|
||||
|
||||
|
||||
@@ -3,18 +3,18 @@ ULP 协处理器编程
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
ULP(Ultra Low Power,超低功耗)协处理器是一种简单的有限状态机 (FSM),可以在主处理器处于深度睡眠模式时,使用 ADC、温度传感器和外部 I2C 传感器执行测量操作。ULP 协处理器可以访问 RTC_SLOW_MEM 内存区域及 RTC_CNTL、RTC_IO、SARADC 外设中的寄存器。ULP 协处理器使用 32 位固定宽度的指令,32 位内存寻址,配备 4 个 16 位通用寄存器。在 ESP-IDF 项目中,此协处理器被称作 `ULP FSM`。
|
||||
ULP(Ultra Low Power,超低功耗)协处理器是一种简单的有限状态机 (FSM),可以在主处理器处于深度睡眠模式时,使用 ADC、温度传感器和外部 I2C 传感器执行测量操作。ULP 协处理器可以访问 ``RTC_SLOW_MEM`` 内存区域及 ``RTC_CNTL``、``RTC_IO``、``SARADC`` 外设中的寄存器。ULP 协处理器使用 32 位固定宽度的指令,32 位内存寻址,配备 4 个 16 位通用寄存器。在 ESP-IDF 项目中,此协处理器被称作 ``ULP FSM``。
|
||||
|
||||
.. only:: esp32s2 or esp32s3
|
||||
|
||||
{IDF_TARGET_NAME} 基于 RISC-V 指令集架构提供另一种 ULP 协处理器。关于 `ULP RISC-V` 的详细信息,请参考 :doc:`ULP-RISC-V Coprocessor <../../../api-reference/system/ulp-risc-v>`。
|
||||
{IDF_TARGET_NAME} 基于 RISC-V 指令集架构提供另一种 ULP 协处理器。关于 ``ULP RISC-V`` 的详细信息,请参考 :doc:`ULP-RISC-V Coprocessor <../../../api-reference/system/ulp-risc-v>`。
|
||||
|
||||
安装工具链
|
||||
----------
|
||||
|
||||
ULP FSM 协处理器代码由汇编语言编写,使用 `binutils-esp32ulp 工具链`_ 进行编译。
|
||||
|
||||
如果您已经按照 :doc:`快速入门指南 <../../../get-started/index>` 中的介绍安装好了 ESP-IDF 及其 CMake 构建系统,那么 ULP 工具链已经被默认安装到了您的开发环境中。
|
||||
如果按照 :doc:`快速入门指南 <../../../get-started/index>` 中的介绍安装好了 ESP-IDF 及其 CMake 构建系统,那么 ULP 工具链已经默认安装到了你的开发环境中。
|
||||
|
||||
编写 ULP FSM
|
||||
-------------------
|
||||
@@ -32,9 +32,11 @@ ULP FSM 协处理器代码由汇编语言编写,使用 `binutils-esp32ulp 工
|
||||
|
||||
若需要将 ULP FSM 代码编译为某组件的一部分,则必须执行以下步骤:
|
||||
|
||||
1. 用汇编语言编写的 ULP FSM 代码必须导入到一个或多个 `.S` 扩展文件中,且这些文件必须放在组件目录中一个独立的目录中,例如 `ulp/`。
|
||||
1. 用汇编语言编写的 ULP FSM 代码必须导入到一个或多个 ``.S`` 扩展文件中,且这些文件必须放在组件目录中一个独立的目录中,例如 ``ulp/``。
|
||||
|
||||
.. note:: 在注册组件(通过 ``idf_component_register``)时,不应将该目录添加到 ``SRC_DIRS`` 参数中。因为 ESP-IDF 构建系统将基于文件扩展名编译在 ``SRC_DIRS`` 中搜索到的文件。对于 ``.S`` 文件,使用的是 ``{IDF_TARGET_TOOLCHAIN_PREFIX}-as`` 汇编器。但这并不适用于 ULP FSM 程序集文件,因此体现这种区别最简单的方式就是将 ULP FSM 程序集文件放到单独的目录中。同样,ULP FSM 程序集源文件也 **不应该** 添加到 ``SRCS`` 中。请参考如下步骤,查看如何正确添加 ULP FSM 程序集源文件。
|
||||
.. note::
|
||||
|
||||
在注册组件(通过 ``idf_component_register``)时,不应将该目录添加到 ``SRC_DIRS`` 参数中。因为 ESP-IDF 构建系统将基于文件扩展名编译在 ``SRC_DIRS`` 中搜索到的文件。对于 ``.S`` 文件,使用的是 ``{IDF_TARGET_TOOLCHAIN_PREFIX}-as`` 汇编器。但这并不适用于 ULP FSM 程序集文件,因此体现这种区别最简单的方式就是将 ULP FSM 程序集文件放到单独的目录中。同样,ULP FSM 程序集源文件也 **不应该** 添加到 ``SRCS`` 中。请参考如下步骤,查看如何正确添加 ULP FSM 程序集源文件。
|
||||
|
||||
2. 注册后从组件 CMakeLists.txt 中调用 ``ulp_embed_binary`` 示例如下::
|
||||
|
||||
@@ -49,7 +51,7 @@ ULP FSM 协处理器代码由汇编语言编写,使用 `binutils-esp32ulp 工
|
||||
|
||||
``ulp_embed_binary`` 的第一个参数为 ULP 二进制文件命名。指定的此名称也用于生成的其他文件,如:ELF 文件、.map 文件、头文件和链接器导出文件。第二个参数指定 ULP FSM 程序集源文件。最后,第三个参数指定组件源文件列表,其中包括被生成的头文件。此列表用以建立正确的依赖项,并确保在编译这些文件之前先创建生成的头文件。有关 ULP FSM 应用程序生成的头文件等相关概念,请参考下文。
|
||||
|
||||
3. 使用常规方法(例如 `idf.py app`)编译应用程序。
|
||||
3. 使用常规方法(例如 ``idf.py app``)编译应用程序。
|
||||
|
||||
在内部,构建系统将按照以下步骤编译 ULP FSM 程序:
|
||||
|
||||
@@ -65,7 +67,7 @@ ULP FSM 协处理器代码由汇编语言编写,使用 `binutils-esp32ulp 工
|
||||
|
||||
6. 使用 ``esp32ulp-elf-nm`` 在 ELF 文件中 **生成全局符号列表** (``ulp_app_name.sym``)。
|
||||
|
||||
7. **创建 LD 导出脚本和头文件** (``ulp_app_name.ld`` 和 ``ulp_app_name.h``),包含来自 ``ulp_app_name.sym`` 的符号。此步骤可借助 ``esp32ulp_mapgen.py`` 工具来完成。
|
||||
7. **创建 LD 导出脚本和头文件** (``ulp_app_name.ld`` 和 ``ulp_app_name.h``),包含来自 ``ulp_app_name.sym`` 的符号。此步骤可借助 ``esp32ulp_mapgen.py`` 工具来完成。
|
||||
|
||||
8. **将生成的二进制文件添加到要嵌入应用程序的二进制文件列表中。**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user