mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
docs:update CN translation for startup and debugging-example
This commit is contained in:
parent
cd5f4ad4c2
commit
bb6a02ae53
@ -2,6 +2,7 @@
|
||||
|
||||
General Notes About ESP-IDF Programming
|
||||
=======================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
This page has been split into two new pages:
|
||||
|
@ -150,6 +150,7 @@ See :ref:`jtag-debugging-tip-why-next-works-as-step` for potential limitation of
|
||||
|
||||
Checking and setting memory
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To display or set contents of memory use "Memory" tab at the bottom of "Debug" perspective.
|
||||
|
||||
With the "Memory" tab, we will read from and write to the memory location ``0x3FF44004`` labeled as ``GPIO_OUT_REG`` used to set and clear individual GPIO's.
|
||||
@ -178,7 +179,6 @@ You should see one bit being flipped over at memory location ``0x3FF44004`` (and
|
||||
|
||||
To set memory use the same "Monitor" tab and the same memory location. Type in alternate bit pattern as previously observed. Immediately after pressing enter you will see LED changing the state.
|
||||
|
||||
|
||||
.. _jtag-debugging-examples-eclipse-06:
|
||||
|
||||
Watching and setting program variables
|
||||
|
@ -9,7 +9,9 @@ This note explains various steps which happen before ``app_main`` function of an
|
||||
The high level view of startup process is as follows:
|
||||
|
||||
1. :ref:`first-stage-bootloader` in ROM loads second-stage bootloader image to RAM (IRAM & DRAM) from flash offset {IDF_TARGET_BOOTLOADER_OFFSET}.
|
||||
|
||||
2. :ref:`second-stage-bootloader` loads partition table and main app image from flash. Main app incorporates both RAM segments and read-only segments mapped via flash cache.
|
||||
|
||||
3. :ref:`application-startup` executes. At this point the second CPU and RTOS scheduler are started.
|
||||
|
||||
This process is explained in detail in the following sections.
|
||||
@ -37,15 +39,15 @@ Startup code called from the reset vector determines the boot mode by checking `
|
||||
|
||||
.. note::
|
||||
|
||||
During normal boot modes the RTC watchdog is enabled when this happens, so if the process is interrupted or stalled then the watchdog will reset the SOC automatically and repeat the boot process. This may cause the SoC to strap into a new boot mode, if the stapping GPIOs have changed.
|
||||
During normal boot modes the RTC watchdog is enabled when this happens, so if the process is interrupted or stalled then the watchdog will reset the SOC automatically and repeat the boot process. This may cause the SoC to strap into a new boot mode, if the strapping GPIOs have changed.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
Second stage bootloader binary image is loaded from flash starting at address 0x1000. If :doc:`/security/secure-boot-v1` is in use then the first 4kB sector of flash is used to store secure boot IV and digest of the bootloader image. Otherwise, this sector is unused.
|
||||
Second stage bootloader binary image is loaded from flash starting at address 0x1000. If :doc:`/security/secure-boot-v1` is in use then the first 4 kB sector of flash is used to store secure boot IV and digest of the bootloader image. Otherwise, this sector is unused.
|
||||
|
||||
.. only:: esp32s2
|
||||
|
||||
Second stage bootloader binary image is loaded from flash starting at address 0x1000. The 4kB sector of flash before this address is unused.
|
||||
Second stage bootloader binary image is loaded from flash starting at address 0x1000. The 4 kB sector of flash before this address is unused.
|
||||
|
||||
.. only:: not (esp32 or esp32s2)
|
||||
|
||||
@ -170,4 +172,4 @@ Unlike normal FreeRTOS tasks (or embedded C ``main`` functions), the ``app_main`
|
||||
|
||||
The ``start_cpu_other_cores_default`` function does some core-specific system initialization and then waits for the PRO CPU to start the FreeRTOS scheduler, at which point it executes ``esp_startup_start_app_other_cores`` which is another weak-linked function defaulting to ``esp_startup_start_app_other_cores_default``.
|
||||
|
||||
By default ``esp_startup_start_app_other_cores_default`` does nothing but spin in a busy-waiting loop until the scheduler of the PRO CPU triggers an interrupt to start the RTOS scheduler on the APP CPU.
|
||||
By default ``esp_startup_start_app_other_cores_default`` does nothing but spin in a busy-waiting loop until the scheduler of the PRO CPU triggers an interrupt to start the RTOS scheduler on the APP CPU.
|
@ -10,6 +10,8 @@
|
||||
|
||||
引导加载程序位于 Flash 的 `0x1000` 偏移地址处。
|
||||
|
||||
.. _bootloader-compatibility:
|
||||
|
||||
引导加载程序兼容性
|
||||
-------------------
|
||||
|
||||
|
@ -3,8 +3,9 @@
|
||||
ESP-IDF 编程注意事项
|
||||
====================
|
||||
|
||||
This page has been split into two new pages:
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
此页面已经分为以下两个新页面:
|
||||
|
||||
- :doc:`startup`
|
||||
- :doc:`memory-types`
|
||||
|
||||
|
@ -155,7 +155,7 @@
|
||||
|
||||
在 “Memory” 选项卡下,我们将在内存地址 ``0x3FF44004`` 处读取和写入内容。该地址也是 ``GPIO_OUT_REG`` 寄存器的地址,可以用来控制(设置或者清除)某个 GPIO 的电平。
|
||||
|
||||
关于该寄存器的更多详细信息,请参阅 `{IDF_TARGET_NAME} 技术参考手册 <{IDF_TARGET_TRM_CN_URL}>`_ 中的 IO_MUX 和 GPIO Matrix 章节。
|
||||
关于该寄存器的更多详细信息,请参阅 *{IDF_TARGET_NAME} 技术参考手册* > *IO MUX 和 GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__] 章节。
|
||||
|
||||
同样在 ``blink.c`` 项目文件中,在两个 ``gpio_set_level`` 语句的后面各设置一个断点,单击 “Memory” 选项卡,然后单击 “Add Memory Monitor” 按钮,在弹出的对话框中输入 ``0x3FF44004``。
|
||||
|
||||
@ -254,7 +254,7 @@
|
||||
|
||||
当看到 ``(gdb)`` 提示符的时候,应用程序已停止运行,LED 也停止闪烁。
|
||||
|
||||
要找到代码暂停的位置,输入 ``l`` 或者 ``list`` 命令,调试器会打印出停止点(``blink.c`` 代码文件的第 43 行)附近的几行代码 ::
|
||||
要找到代码暂停的位置,输入 ``l`` 或者 ``list`` 命令,调试器会打印出暂停点(``blink.c`` 代码文件的第 43 行)附近的几行代码 ::
|
||||
|
||||
(gdb) l
|
||||
38 }
|
||||
@ -514,7 +514,9 @@
|
||||
|
||||
使用命令 ``x`` 可以显示内存的内容,配合其余参数还可以调整所显示内存位置的格式和数量。运行 ``help x`` 可以查看更多相关细节。与 ``x`` 命令配合使用的命令是 ``set``,它允许你将值写入内存。
|
||||
|
||||
为了演示 ``x`` 和 ``set`` 的使用,我们将在内存地址 ``0x3FF44004`` 处读取和写入内容。该地址也是 ``GPIO_OUT_REG`` 寄存器的地址,可以用来控制(设置或者清除)某个 GPIO 的电平。关于该寄存器的更多详细信息,请参阅 `{IDF_TARGET_NAME} 技术参考手册 <{IDF_TARGET_TRM_CN_URL}>`_ 中的 IO_MUX 和 GPIO Matrix章节。
|
||||
为了演示 ``x`` 和 ``set`` 的使用,我们将在内存地址 ``0x3FF44004`` 处读取和写入内容。该地址也是 ``GPIO_OUT_REG`` 寄存器的地址,可以用来控制(设置或者清除)某个 GPIO 的电平。
|
||||
|
||||
关于该寄存器的更多详细信息,请参阅 *{IDF_TARGET_NAME} 技术参考手册* > *IO MUX 和 GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__] 章节。
|
||||
|
||||
同样在 ``blink.c`` 项目文件中,在两个 ``gpio_set_level`` 语句的后面各设置一个断点。输入两次 ``c`` 命令后停止在断点处,然后输入 ``x /1wx 0x3FF44004`` 来显示 ``GPIO_OUT_REG`` 寄存器的值::
|
||||
|
||||
@ -608,7 +610,7 @@
|
||||
Breakpoint 3 at 0x400db753: file /home/user-name/esp/blink/main/./blink.c, line 34.
|
||||
(gdb)
|
||||
|
||||
以上命令在 ``blink.c`` 文件的 ``34`` 处设置了一个条件断点,当 ``i==2`` 条件满足时,程序会停止运行。
|
||||
以上命令在 ``blink.c`` 文件的 ``34`` 处设置了一个条件断点,当 ``i == 2`` 条件满足时,程序会停止运行。
|
||||
|
||||
如果当前 ``i`` 的值小于 ``2`` 并且程序被恢复运行,那么 LED 就会循环闪烁,直到 ``i == 2`` 条件成立,最后程序停止在该处::
|
||||
|
||||
|
@ -3,24 +3,20 @@
|
||||
应用程序的内存布局
|
||||
------------------
|
||||
|
||||
{IDF_TARGET_NAME} 芯片具有灵活的内存映射功能,本小节将介绍 ESP-IDF
|
||||
默认使用这些功能的方式。
|
||||
{IDF_TARGET_NAME} 芯片具有灵活的内存映射功能,本小节将介绍 ESP-IDF 默认使用这些功能的方式。
|
||||
|
||||
ESP-IDF 应用程序的代码可以放在以下内存区域之一。
|
||||
|
||||
.. _iram:
|
||||
|
||||
IRAM(指令 RAM)
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
ESP-IDF 将内部 SRAM0 区域(在技术参考手册中有定义)的一部分分配为指令
|
||||
RAM。除了开始的 64kB 用作 PRO CPU 和 APP CPU
|
||||
的高速缓存外,剩余内存区域(从 ``0x40080000`` 至
|
||||
``0x400A0000`` )被用来存储应用程序中部分需要在RAM中运行的代码。
|
||||
ESP-IDF 将内部 SRAM0 区域(在技术参考手册中有定义)的一部分分配为指令 RAM。除了开始的 64kB 用作 PRO CPU 和 APP CPU 的高速缓存外,剩余内存区域(从 ``0x40080000`` 至 ``0x400A0000`` )被用来存储应用程序中部分需要在 RAM 中运行的代码。
|
||||
|
||||
一些 ESP-IDF 的组件和 WiFi
|
||||
协议栈的部分代码通过链接脚本文件被存放到了这块内存区域。
|
||||
一些 ESP-IDF 的组件和 WiFi 协议栈的部分代码通过链接脚本文件被存放到了这块内存区域。
|
||||
|
||||
如果一些应用程序的代码需要放在 IRAM 中运行,可以使用 ``IRAM_ATTR``
|
||||
宏定义进行声明。
|
||||
如果一些应用程序的代码需要放在 IRAM 中运行,可以使用 ``IRAM_ATTR`` 宏定义进行声明。
|
||||
|
||||
.. code:: c
|
||||
|
||||
@ -33,54 +29,32 @@ RAM。除了开始的 64kB 用作 PRO CPU 和 APP CPU
|
||||
|
||||
下面列举了应用程序中可能或者应该放入 IRAM 中运行例子。
|
||||
|
||||
- 当注册中断处理程序的时候设置了
|
||||
``ESP_INTR_FLAG_IRAM`` ,那么中断处理程序就必须要放在 IRAM
|
||||
中运行。这种情况下,ISR 只能调用存放在 IRAM 或者 ROM
|
||||
中的函数。 *注意* :目前所有 FreeRTOS 的 API 都已经存放到了 IRAM
|
||||
中,所以在中断中调用 FreeRTOS 的中断专属 API 是安全的。如果将 ISR
|
||||
放在 IRAM 中运行,那么必须使用宏定义 ``DRAM_ATTR`` 将该 ISR
|
||||
用到所有常量数据和调用的函数(包括但不限于 ``const char`` 数组)放入
|
||||
DRAM 中。
|
||||
- 当注册中断处理程序的时候设置了 ``ESP_INTR_FLAG_IRAM`` ,那么中断处理程序就必须要放在 IRAM 中运行。这种情况下,ISR 只能调用存放在 IRAM 或者 ROM 中的函数。 *注意* :目前所有 FreeRTOS 的 API 都已经存放到了 IRAM 中,所以在中断中调用 FreeRTOS 的中断专属 API 是安全的。如果将 ISR 放在 IRAM 中运行,那么必须使用宏定义 ``DRAM_ATTR`` 将该 ISR 用到所有常量数据和调用的函数(包括但不限于 ``const char`` 数组)放入 DRAM 中。
|
||||
|
||||
- 可以将一些时间关键的代码放在 IRAM 中,这样可以缩减从 Flash
|
||||
加载代码所消耗的时间。{IDF_TARGET_NAME} 是通过 32kB 的高速缓存来从外部 Flash
|
||||
中读取代码和数据的,将函数放在 IRAM
|
||||
中运行可以减少由高速缓存未命中引起的时间延迟。
|
||||
- 可以将一些时间关键的代码放在 IRAM 中,这样可以缩减从 Flash 加载代码所消耗的时间。{IDF_TARGET_NAME} 是通过 32kB 的高速缓存来从外部 Flash 中读取代码和数据的,将函数放在 IRAM 中运行可以减少由高速缓存未命中引起的时间延迟。
|
||||
|
||||
.. _irom:
|
||||
|
||||
IROM(代码从 Flash 中运行)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
如果一个函数没有被显式地声明放在 IRAM 或者 RTC 内存中,则将其置于 Flash
|
||||
中。Flash 技术参考手册中介绍了 Flash MMU 允许代码从 Flash
|
||||
执行的机制。ESP-IDF 将从 Flash 中执行的代码放在
|
||||
``0x400D0000 — 0x40400000`` 区域的开始,在启动阶段,二级引导程序会初始化
|
||||
Flash MMU,将代码在 Flash
|
||||
中的位置映射到这个区域的开头。对这个区域的访问会被透明地缓存到
|
||||
``0x40070000 — 0x40080000`` 范围内的两个 32kB 的块中。
|
||||
如果一个函数没有被显式地声明放在 IRAM 或者 RTC 内存中,则将其置于 Flash 中。Flash 技术参考手册中介绍了 Flash MMU 允许代码从 Flash 执行的机制。ESP-IDF 将从 Flash 中执行的代码放在 ``0x400D0000 — 0x40400000`` 区域的开始,在启动阶段,二级引导程序会初始化 Flash MMU,将代码在 Flash 中的位置映射到这个区域的开头。对这个区域的访问会被透明地缓存到 ``0x40070000 — 0x40080000`` 范围内的两个 32kB 的块中。
|
||||
|
||||
请注意,使用 Window ABI ``CALLx`` 指令可能无法访问
|
||||
``0x40000000 — 0x40400000``
|
||||
区域以外的代码,所以要特别留意应用程序是否使用了
|
||||
``0x40400000 — 0x40800000`` 或者 ``0x40800000 — 0x40C00000``
|
||||
区域,ESP-IDF 默认不会使用这两个区域。
|
||||
请注意,使用 Window ABI ``CALLx`` 指令可能无法访问 ``0x40000000 — 0x40400000`` 区域以外的代码,所以要特别留意应用程序是否使用了 ``0x40400000 — 0x40800000`` 或者 ``0x40800000 — 0x40C00000`` 区域,ESP-IDF 默认不会使用这两个区域。
|
||||
|
||||
RTC 快速内存
|
||||
~~~~~~~~~~~~
|
||||
|
||||
从深度睡眠模式唤醒后必须要运行的代码要放在 RTC
|
||||
内存中,更多信息请查阅文档 :doc:`深度睡眠 <deep-sleep-stub>`
|
||||
。
|
||||
从深度睡眠模式唤醒后必须要运行的代码要放在 RTC 内存中,更多信息请查阅文档 :doc:`深度睡眠 <deep-sleep-stub>`。
|
||||
|
||||
.. _dram:
|
||||
|
||||
DRAM(数据 RAM)
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
链接器将非常量静态数据和零初始化数据放入 ``0x3FFB0000 — 0x3FFF0000`` 这
|
||||
256kB 的区域。注意,如果使用蓝牙堆栈,此区域会减少
|
||||
64kB(通过将起始地址移至 ``0x3FFC0000`` )。如果使用了内存跟踪的功能,该区域的长度还要减少
|
||||
16kB 或者 32kB。放置静态数据后,留在此区域中的剩余空间都用作运行时堆。
|
||||
链接器将非常量静态数据和零初始化数据放入 ``0x3FFB0000 — 0x3FFF0000`` 这 256kB 的区域。注意,如果使用蓝牙堆栈,此区域会减少 64kB(通过将起始地址移至 ``0x3FFC0000`` )。如果使用了内存跟踪的功能,该区域的长度还要减少 16kB 或者 32kB。放置静态数据后,留在此区域中的剩余空间都用作运行时堆。
|
||||
|
||||
常量数据也可以放在 DRAM 中,例如,用在 ISR 中的常量数据(参见上面 IRAM
|
||||
部分的介绍),为此需要使用 ``DRAM_ATTR`` 宏来声明。
|
||||
常量数据也可以放在 DRAM 中,例如,用在 ISR 中的常量数据(参见上面 IRAM 部分的介绍),为此需要使用 ``DRAM_ATTR`` 宏来声明。
|
||||
|
||||
.. code:: c
|
||||
|
||||
@ -88,12 +62,9 @@ DRAM(数据 RAM)
|
||||
char buffer[64];
|
||||
sprintf(buffer, format_string, ptr, val);
|
||||
|
||||
毋庸置疑,不建议在 ISR 中使用 ``printf``
|
||||
和其余输出函数。出于调试的目的,可以在 ISR 中使用 ``ESP_EARLY_LOGx``
|
||||
来输出日志,不过要确保将 ``TAG`` 和格式字符串都放在了 ``DRAM`` 中。
|
||||
毋庸置疑,不建议在 ISR 中使用 ``printf`` 和其余输出函数。出于调试的目的,可以在 ISR 中使用 ``ESP_EARLY_LOGx`` 来输出日志,不过要确保将 ``TAG`` 和格式字符串都放在了 ``DRAM`` 中。
|
||||
|
||||
宏 ``__NOINIT_ATTR`` 可以用来声明将数据放在 ``.noinit``
|
||||
段中,放在此段中的数据不会在启动时被初始化,并且在软件重启后会保留原来的值。
|
||||
宏 ``__NOINIT_ATTR`` 可以用来声明将数据放在 ``.noinit`` 段中,放在此段中的数据不会在启动时被初始化,并且在软件重启后会保留原来的值。
|
||||
|
||||
例子:
|
||||
|
||||
@ -101,24 +72,19 @@ DRAM(数据 RAM)
|
||||
|
||||
__NOINIT_ATTR uint32_t noinit_data;
|
||||
|
||||
.. _drom:
|
||||
|
||||
DROM(数据存储在 Flash 中)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
默认情况下,链接器将常量数据放入一个 4MB 区域
|
||||
(``0x3F400000 — 0x3F800000``) ,该区域用于通过 Flash MMU
|
||||
和高速缓存来访问外部
|
||||
Flash。一种特例情况是,字面量会被编译器嵌入到应用程序代码中。
|
||||
默认情况下,链接器将常量数据放入一个 4MB 区域 (``0x3F400000 — 0x3F800000``) ,该区域用于通过 Flash MMU 和高速缓存来访问外部 Flash。一种特例情况是,字面量会被编译器嵌入到应用程序代码中。
|
||||
|
||||
RTC 慢速内存
|
||||
~~~~~~~~~~~~
|
||||
|
||||
从 RTC
|
||||
内存运行的代码(例如深度睡眠模块的代码)使用的全局和静态变量必须要放在
|
||||
RTC 慢速内存中。更多详细说明请查看文档
|
||||
:doc:`深度睡眠 <deep-sleep-stub>` 。
|
||||
从 RTC 内存运行的代码(例如深度睡眠模块的代码)使用的全局和静态变量必须要放在 RTC 慢速内存中。更多详细说明请查看文档 :doc:`深度睡眠 <deep-sleep-stub>` 。
|
||||
|
||||
宏 ``RTC_NOINIT_ATTR`` 用来声明将数据放入 RTC
|
||||
慢速内存中,该数据在深度睡眠唤醒后将保持不变。
|
||||
宏 ``RTC_NOINIT_ATTR`` 用来声明将数据放入 RTC 慢速内存中,该数据在深度睡眠唤醒后将保持不变。
|
||||
|
||||
例子:
|
||||
|
||||
@ -129,9 +95,7 @@ RTC 慢速内存中。更多详细说明请查看文档
|
||||
DMA 能力要求
|
||||
------------
|
||||
|
||||
大多数的 DMA 控制器(比如 SPI,SDMMC 等)都要求发送/接收缓冲区放在 DRAM
|
||||
中,并且按字对齐。我们建议将 DMA 缓冲区放在静态变量中而不是堆栈中。使用
|
||||
``DMA_ATTR`` 宏可以声明该全局/本地的静态变量具备 DMA 能力,例如:
|
||||
大多数的 DMA 控制器(比如 SPI,SDMMC 等)都要求发送/接收缓冲区放在 DRAM 中,并且按字对齐。我们建议将 DMA 缓冲区放在静态变量中而不是堆栈中。使用 ``DMA_ATTR`` 宏可以声明该全局/本地的静态变量具备 DMA 能力,例如:
|
||||
|
||||
.. code:: c
|
||||
|
||||
@ -168,14 +132,9 @@ DMA 能力要求
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_SPIRAM_SUPPORTED:- 如果堆栈在 pSRAM 中,切勿尝试这么做,因为堆栈在 pSRAM 中的话就要按照
|
||||
:doc:`片外SRAM <external-ram>` 文档介绍的步骤来操作(至少要在
|
||||
``menuconfig`` 中使能
|
||||
``SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY`` ),所以请确保你的任务不在
|
||||
pSRAM 中。
|
||||
:SOC_SPIRAM_SUPPORTED:- 如果堆栈在 pSRAM 中,切勿尝试这么做,因为堆栈在 pSRAM 中的话就要按照 :doc:`片外SRAM <external-ram>` 文档介绍的步骤来操作(至少要在 ``menuconfig`` 中使能 ``SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY`` ),所以请确保你的任务不在 PSRAM 中。
|
||||
|
||||
- 在函数中使用 ``WORD_ALIGNED_ATTR``
|
||||
宏来修饰变量,将其放在适当的位置上,比如:
|
||||
- 在函数中使用 ``WORD_ALIGNED_ATTR`` 宏来修饰变量,将其放在适当的位置上,比如:
|
||||
|
||||
.. code:: c
|
||||
|
||||
|
@ -4,60 +4,54 @@
|
||||
|
||||
{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000"}
|
||||
|
||||
本文将会介绍 {IDF_TARGET_NAME} 从上电到运行 ``app_main``
|
||||
函数中间所经历的步骤(即启动流程)。
|
||||
本文将会介绍 {IDF_TARGET_NAME} 从上电到运行 ``app_main`` 函数中间所经历的步骤(即启动流程)。
|
||||
|
||||
宏观上,该启动流程可以分为如下 3 个步骤:
|
||||
|
||||
1. 一级引导程序被固化在了 {IDF_TARGET_NAME} 内部的 ROM 中,它会从 Flash 的
|
||||
{IDF_TARGET_BOOTLOADER_OFFSET} 偏移地址处加载二级引导程序至 RAM(IRAM & DRAM) 中。
|
||||
1. :ref:`first-stage-bootloader` 被固化在了 {IDF_TARGET_NAME} 内部的 ROM 中,它会从 flash 的 {IDF_TARGET_BOOTLOADER_OFFSET} 偏移地址处加载二级引导程序至 RAM (IRAM & DRAM) 中。
|
||||
|
||||
2. 二级引导程序从 Flash 中加载分区表和主程序镜像至内存中,主程序中包含了
|
||||
RAM 段和通过 Flash 高速缓存映射的只读段。
|
||||
2. :ref:`second-stage-bootloader` 从 flash 中加载分区表和主程序镜像至内存中,主程序中包含了 RAM 段和通过 flash 高速缓存映射的只读段。
|
||||
|
||||
3. 主程序运行,这时第二个 CPU 和 RTOS 的调度器可以开始运行。
|
||||
3. :ref:`application-startup` 运行,这时第二个 CPU 和 RTOS 的调度器启动。
|
||||
|
||||
下面会对上述过程进行更为详细的阐述。
|
||||
|
||||
.. _first-stage-bootloader:
|
||||
|
||||
一级引导程序
|
||||
~~~~~~~~~~~~
|
||||
|
||||
SoC 复位后,PRO CPU 会立即开始运行,执行复位向量代码,而 APP CPU
|
||||
仍然保持复位状态。在启动过程中,PRO CPU 会执行所有的初始化操作。APP CPU
|
||||
的复位状态会在应用程序启动代码的 ``call_start_cpu0``
|
||||
函数中失效。复位向量代码位于 {IDF_TARGET_NAME} 芯片掩膜 ROM 的 ``0x40000400``
|
||||
地址处,该地址不能被修改。
|
||||
.. only:: not CONFIG_FREERTOS_UNICORE
|
||||
|
||||
复位向量调用的启动代码会根据 ``GPIO_STRAP_REG`` 寄存器的值来确定 {IDF_TARGET_NAME}
|
||||
的工作模式,该寄存器保存着复位后 bootstrap
|
||||
引脚的电平状态。根据不同的复位原因,程序会执行不同的操作:
|
||||
SoC 复位后,PRO CPU 会立即开始运行,执行复位向量代码,而 APP CPU 仍然保持复位状态。在启动过程中,PRO CPU 会执行所有的初始化操作。APP CPU 的复位状态会在应用程序启动代码的 ``call_start_cpu0`` 函数中失效。复位向量代码位于 {IDF_TARGET_NAME} 芯片掩膜 ROM 处,且不能被修改。
|
||||
|
||||
1. 从深度睡眠模式复位:如果 ``RTC_CNTL_STORE6_REG`` 寄存器的值非零,并且
|
||||
``RTC_CNTL_STORE7_REG`` 寄存器中的 RTC 内存的 CRC
|
||||
校验值有效,那么程序会使用 ``RTC_CNTL_STORE6_REG``
|
||||
寄存器的值作为入口地址,并立即跳转到该地址运行。如果
|
||||
``RTC_CNTL_STORE6_REG`` 的值为零,或者 ``RTC_CNTL_STORE7_REG`` 中的
|
||||
CRC 校验值无效,又或者跳转到 ``RTC_CNTL_STORE6_REG``
|
||||
地址处运行的程序返回,那么将会执行上电复位的相关操作。 **注意** :如果想在这里运行自定义的代码,可以参考
|
||||
:doc:`深度睡眠 <deep-sleep-stub>` 文档里面介绍的方法。
|
||||
.. only:: CONFIG_FREERTOS_UNICORE
|
||||
|
||||
2. 上电复位、软件 SoC 复位、看门狗 SoC 复位:检查 ``GPIO_STRAP_REG``
|
||||
寄存器,判断是否 UART 或 SDIO 请求进入下载模式。如果是,则配置好 UART
|
||||
或者 SDIO,然后等待下载代码。否则程序将会执行软件 CPU
|
||||
复位的相关操作。
|
||||
SoC 复位后,CPU 会立即开始运行,执行所有的初始化操作。复位向量代码位于 {IDF_TARGET_NAME} 芯片掩膜 ROM 处,且不能被修改。
|
||||
|
||||
3. 软件 CPU 复位、看门狗 CPU 复位:根据 EFUSE 中的值配置 SPI
|
||||
Flash,然后尝试从 Flash
|
||||
中加载代码,这部分的内存将会在后面一小节详细介绍。如果从 Flash
|
||||
中加载代码失败,就会将 BASIC 解析器加压缩到 RAM
|
||||
中启动。需要注意的是,此时 RTC
|
||||
看门狗还在使能状态,如果在几百毫秒内没有任何输入事件,那么看门狗会再次复位
|
||||
SoC,重复整个过程。如果解析器收到了来自 UART
|
||||
的输入,程序会关闭看门狗。
|
||||
复位向量调用的启动代码会根据 ``GPIO_STRAP_REG`` 寄存器的值来确定 {IDF_TARGET_NAME} 的启动模式,该寄存器保存着复位后 bootstrap 引脚的电平状态。根据不同的复位原因,程序会执行如下操作:
|
||||
|
||||
应用程序的二进制镜像会从 Flash 的 {IDF_TARGET_BOOTLOADER_OFFSET} 地址处加载。Flash 的第一个
|
||||
4kB
|
||||
扇区用于存储安全引导程序和应用程序镜像的签名。有关详细信息,请查看安全启动文档。
|
||||
1. 从深度睡眠模式复位:如果 ``RTC_CNTL_STORE6_REG`` 寄存器的值非零,且 ``RTC_CNTL_STORE7_REG`` 寄存器中的 RTC 内存的 CRC 校验值有效,那么程序会使用 ``RTC_CNTL_STORE6_REG`` 寄存器的值作为入口地址,并立即跳转到该地址运行。如果 ``RTC_CNTL_STORE6_REG`` 的值为零,或 ``RTC_CNTL_STORE7_REG`` 中的 CRC 校验值无效,又或通过 ``RTC_CNTL_STORE6_REG`` 调用的代码返回,那么则像上电复位一样继续启动。 **注意**:如果想在这里运行自定义的代码,可以参考 :doc:`深度睡眠 <deep-sleep-stub>` 文档里面介绍的深度睡眠存根机制方法。
|
||||
|
||||
2. 上电复位、软件 SoC 复位、看门狗 SoC 复位:检查 ``GPIO_STRAP_REG`` 寄存器,判断是否请求自定义启动模式,如 UART 下载模式。如果是,ROM 会执行此自定义加载器模式。否则程会像软件 CPU 复位一样继续启动。请参考 {IDF_TARGET_NAME} 技术规格书了解 SoC 启动模式以及具体执行过程。
|
||||
|
||||
3. 软件 CPU 复位、看门狗 CPU 复位:根据 EFUSE 中的值配置 SPI flash,然后尝试从 flash 中加载代码,这部分将会在后面一小节详细介绍。
|
||||
|
||||
.. note::
|
||||
|
||||
正常启动模式下会使能 RTC 看门狗,因此,如果进程中断或停止,看门狗将自动重置 SOC 并重复启动过程。如果 strapping GPIOs 已更改,则可能导致 SoC 陷入新的启动模式。
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
二级引导程序二进制镜像会从 flash 的 ``0x1000`` 偏移地址处加载。如果正在使用 :doc:`/security/secure-boot-v1`,则 flash 的第一个 4 kB 扇区用于存储安全启动 IV 以及引导程序镜像的摘要,否则不使用该扇区。
|
||||
|
||||
.. only:: esp32s2
|
||||
|
||||
二级引导程序二进制镜像会从 flash 的 ``0x1000`` 偏移地址处加载。该地址前面的 flash 4 kB 扇区未使用。
|
||||
|
||||
.. only:: not (esp32 or esp32s2)
|
||||
|
||||
二级引导程序二进制镜像会从 flash 的 `` 0x0`` 偏移地址处加载。
|
||||
|
||||
.. TODO: describe application binary image format, describe optional flash configuration commands.
|
||||
|
||||
@ -66,66 +60,116 @@ SoC 复位后,PRO CPU 会立即开始运行,执行复位向量代码,而 A
|
||||
二级引导程序
|
||||
~~~~~~~~~~~~
|
||||
|
||||
在 ESP-IDF 中,存放在 Flash 的 {IDF_TARGET_BOOTLOADER_OFFSET}
|
||||
偏移地址处的二进制镜像就是二级引导程序。二级引导程序的源码可以在 ESP-IDF
|
||||
的 components/bootloader 目录下找到。请注意,对于 {IDF_TARGET_NAME}
|
||||
芯片来说,这并不是唯一的安排程序镜像的方式。事实上用户完全可以把一个功能齐全的应用程序烧写到
|
||||
Flash 的 {IDF_TARGET_BOOTLOADER_OFFSET} 偏移地址处运行,但这超出本文档的范围。ESP-IDF
|
||||
使用二级引导程序可以增加 Flash 分区的灵活性(使用分区表),并且方便实现
|
||||
Flash 加密,安全引导和空中升级(OTA)等功能。
|
||||
在 ESP-IDF 中,存放在 flash 的 {IDF_TARGET_BOOTLOADER_OFFSET} 偏移地址处的二进制镜像就是二级引导程序。二级引导程序的源码可以在 ESP-IDF 的 :idf:`components/bootloader` 目录下找到。ESP-IDF 使用二级引导程序可以增加 flash 分区的灵活性(使用分区表),并且方便实现 flash 加密,安全引导和空中升级(OTA)等功能。
|
||||
|
||||
当一级引导程序校验并加载完二级引导程序后,它会从二进制镜像的头部找到二级引导程序的入口点,并跳转过去运行。
|
||||
|
||||
二级引导程序从 Flash 的 ``0x8000``
|
||||
偏移地址处读取分区表。详细信息请参阅分区表文档
|
||||
:doc:`分区表 <partition-tables>` 。二级引导程序会寻找出厂分区和 OTA
|
||||
分区,然后根据 OTA *信息* 分区的数据决引导哪个分区。
|
||||
二级引导程序默认从 flash 的 ``0x8000`` 偏移地址处 :ref:`可配置的值 <CONFIG_PARTITION_TABLE_OFFSET>`)读取分区表。请参考 :doc:`分区表 <partition-tables>` 获取详细信息。引导程序会寻找工厂分区和 OTA 应用程序分区。如果在分区表中找到了 OTA 应用程序分区,引导程序将查询 ``otadata`` 分区以确定应引导哪个分区。更多信息请参考 :doc:`/api-reference/system/ota`。
|
||||
|
||||
对于选定的分区,二级引导程序将映射到 IRAM 和 DRAM
|
||||
的数据和代码段复制到它们的加载地址处。对于一些加载地址位于 DROM 和 IROM
|
||||
区域的段,会通过配置 Flash MMU
|
||||
为其提供正确的映射。请注意,二级引导程序会为 PRO CPU 和 APP CPU 都配置
|
||||
Flash MMU,但它只使能了 PRO CPU 的 Flash
|
||||
MMU。这么做的原因在于二级引导程序的代码被加载到了 APP CPU
|
||||
的高速缓存使用的内存区域,因此使能 APP CPU
|
||||
高速缓存的任务就交给了应用程序。一旦代码加载完毕并且设置好 Flash
|
||||
MMU,二级引导程序会从应用程序二进制镜像文件的头部寻找入口地址,然后跳转到该地址处运行。
|
||||
关于 ESP-IDF 引导程序可用的配置选项,请参考 :doc:`bootloader`。
|
||||
|
||||
目前还不支持添加钩子函数到二级引导程序中以自定义应用程序分区选择的逻辑,但是可以通过别的途径实现这个需求,比如根据某个
|
||||
GPIO 的不同状态来引导不同的应用程序镜像。此类自定义的功能将在未来添加到
|
||||
ESP-IDF 中。目前,可以通过将 bootloader
|
||||
组件复制到应用程序目录并在那里进行必要的更改来自定义引导程序。在这种情况下,ESP-IDF
|
||||
的编译系统将编译应用程序目录中的组件而不是 ESP-IDF 组件目录。
|
||||
对于选定的分区,二级引导程序将从 flash 逐段读取二进制镜像:
|
||||
|
||||
- 对于在内部 :ref:`iram` 或 :ref:`dram` 中具有加载地址的段,将把数据从 flash 复制到它们的加载地址处。
|
||||
- 对于一些加载地址位于 :ref:`drom` 或 :ref:`irom` 区域的段,通过配置 flash MMU,可为从 flash 到加载地址提供正确的映射。
|
||||
|
||||
.. only:: not CONFIG_FREERTOS_UNICORE
|
||||
|
||||
请注意,二级引导程序同时为 PRO CPU 和 APP CPU 配置 flash MMU,但仅使能 PRO CPU 的 flash MMU。原因是二级引导程序代码已加载到 APP CPU 的高速缓存使用的内存区域中。因此使能 APP CPU 高速缓存的任务就交给了应用程序。
|
||||
|
||||
一旦处理完所有段(即加载了代码并设置了 flash MMU),二级引导程序将验证应用程序的完整性,并从二进制镜像文件的头部寻找入口地址,然后跳转到该地址处运行。
|
||||
|
||||
.. _application-startup:
|
||||
|
||||
应用程序启动阶段
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
ESP-IDF 应用程序的入口是 ``components/{IDF_TARGET_PATH_NAME}/cpu_start.c`` 文件中的
|
||||
``call_start_cpu0``
|
||||
函数,该函数主要完成了两件事,一是启用堆分配器,二是使 APP CPU
|
||||
跳转到其入口点—— ``call_start_cpu1`` 函数。PRO CPU 上的代码会给 APP
|
||||
CPU 设置好入口地址,解除其复位状态,然后等待 APP CPU
|
||||
上运行的代码设置一个全局标志,以表明 APP CPU 已经正常启动。 完成后,PRO
|
||||
CPU 跳转到 ``start_cpu0`` 函数,APP CPU 跳转到 ``start_cpu1`` 函数。
|
||||
应用程序启动包含了从应用程序开始执行到 ``app_main`` 函数在主任务内部运行前的所有过程。可分为三个阶段:
|
||||
|
||||
``start_cpu0`` 和 ``start_cpu1``
|
||||
这两个函数都是弱类型的,这意味着如果某些特定的应用程序需要修改初始化顺序,就可以通过重写这两个函数来实现。 ``start_cpu0``
|
||||
默认的实现方式是初始化用户在 ``menuconfig``
|
||||
中选择的组件,具体实现步骤可以阅读 ``components/{IDF_TARGET_PATH_NAME}/cpu_start.c``
|
||||
文件中的源码。请注意,此阶段会调用应用程序中存在的 C++
|
||||
全局构造函数。一旦所有必要的组件都初始化好,就会创建 *main
|
||||
task* ,并启动 FreeRTOS 的调度器。
|
||||
- 硬件和基本 C 语言运行环境的端口初始化。
|
||||
- 软件服务和 FreeRTOS 的系统初始化。
|
||||
- 运行主任务并调用 ``app_main``。
|
||||
|
||||
当 PRO CPU 在 ``start_cpu0`` 函数中进行初始化的时候,APP CPU 在
|
||||
``start_cpu1`` 函数中自旋,等待 PRO CPU 上的调度器启动。一旦 PRO CPU
|
||||
上的调度器启动后,APP CPU 上的代码也会启动调度器。
|
||||
.. note::
|
||||
|
||||
通常不需要了解 ESP-IDF 应用程序初始化的所有阶段。如果需要仅从应用程序开发人员的角度了解初始化,请跳至 :ref:`app-main-task`。
|
||||
|
||||
端口初始化
|
||||
------------------
|
||||
|
||||
ESP-IDF 应用程序的入口是 :idf_file:`components/esp_system/port/cpu_start.c` 文件中的 ``call_start_cpu0`` 函数。这个函数由二级引导加载程序执行,并且从不返回。
|
||||
|
||||
该端口层的初始化功能会初始化基本的 C 运行环境 ("CRT"),并对 SoC 的内部硬件进行了初始配置。
|
||||
|
||||
.. list::
|
||||
|
||||
- 为应用程序重新配置 CPU 异常(允许应用程序中断处理程序运行,并使用为应用程序配置的选项来处理 :doc:`fatal-errors`,而不是使用 ROM 提供的简易版错误处理程序处理。
|
||||
- 如果没有设置选项 :ref:`CONFIG_BOOTLOADER_WDT_ENABLE`,则不使能 RTC 看门狗定时器。
|
||||
- 初始化内部存储器(数据和 bss)。
|
||||
- 完成 MMU 高速缓存配置。
|
||||
:SOC_SPIRAM_SUPPORTED: - 如果配置了 PSRAM,则使能 PSRAM。
|
||||
- 将 CPU 时钟设置为项目配置的频率。
|
||||
:CONFIG_ESP_SYSTEM_MEMPROT_FEATURE: - 如果配置了内存保护,则初始化内存保护。
|
||||
:esp32: - 根据应用程序头部设置重新配置主 SPI flash,这是为了与 ESP-IDF V4.0 之前的引导程序版本兼容,请参考 :ref:`bootloader-compatibility`。
|
||||
:not CONFIG_FREERTOS_UNICORE: - 如果应用程序被配置为在多个内核上运行,则启动另一个内核并等待其初始化(在类似的“端口层”初始化函数 ``call_start_cpu1`` 内)。
|
||||
|
||||
.. only:: not CONFIG_FREERTOS_UNICORE
|
||||
|
||||
``call_start_cpu0`` 完成运行后,将调用在 :idf_file:`components/esp_system/startup.c` 中找到的“系统层”初始化函数 ``start_cpu0``。其他内核也将完成端口层的初始化,并调用同一文件中的 ``start_other_cores``。
|
||||
|
||||
.. only:: CONFIG_FREERTOS_UNICORE
|
||||
|
||||
``call_start_cpu0`` 完成运行后,将调用在 :idf_file:`components/esp_system/startup.c` 中找到的“系统层”初始化函数 ``start_cpu0``。
|
||||
|
||||
系统初始化
|
||||
---------------------
|
||||
|
||||
主要的系统初始化函数是 ``start_cpu0``。默认情况下,这个函数与 ``start_cpu0_default`` 函数弱链接。这意味着可以覆盖这个函数,增加一些额外的初始化步骤。
|
||||
|
||||
主要的系统初始化阶段包括:
|
||||
|
||||
.. list::
|
||||
|
||||
- 如果默认的日志级别允许,则记录该应用程序的相关信息(项目名称、:ref:`app-version` 等)。
|
||||
- 初始化堆分配器(在这之前,所有分配必须是静态的或在堆栈上)。
|
||||
- 初始化 newlib 组件的系统调用和时间函数。
|
||||
- 配置断电检测器。
|
||||
- 根据 :ref:`串行控制台配置 <CONFIG_ESP_CONSOLE_UART>` 设置 libc stdin、stdout、和 stderr。
|
||||
:esp32: - 执行与安全有关的检查,包括为该配置烧录 efuse(包括 :ref:`禁用 ESP32 V3 的 ROM 下载模式 <CONFIG_SECURE_UART_ROM_DL_MODE>`、:ref:`CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE`)。
|
||||
:not esp32: - 执行与安全有关的检查,包括为该配置烧录 efuse(包括 :ref:`永久限制 ROM 下载模式 <CONFIG_SECURE_UART_ROM_DL_MODE>`)。
|
||||
- 初始化 SPI flash API 支持。
|
||||
- 调用全局 C++ 构造函数和任何标有 ``__attribute__((constructor))`` 的 C 函数。
|
||||
|
||||
二级系统初始化允许单个组件被初始化。如果一个组件有一个用 ``ESP_SYSTEM_INIT_FN`` 宏注释的初始化函数,它将作为二级初始化的一部分被调用。
|
||||
|
||||
.. _app-main-task:
|
||||
|
||||
Running the main task
|
||||
运行主任务
|
||||
---------------------
|
||||
|
||||
主任务是指运行 ``app_main`` 函数的任务,主任务的堆栈大小和优先级可以在
|
||||
``menuconfig``
|
||||
中进行配置。应用程序可以用此任务来完成用户程序相关的初始化设置,比如启动其他的任务。应用程序还可以将主任务用于事件循环和其他通用活动。如果
|
||||
``app_main`` 函数返回,那么主任务将会被删除。
|
||||
在所有其他组件都初始化后,主任务会被创建,FreeRTOS 调度器开始运行。
|
||||
|
||||
做完一些初始化任务后(需要启动调度器),主任务在固件中运行应用程序提供的函数 ``app_main``。
|
||||
|
||||
运行 ``app_main`` 的主任务有一个固定的 RTOS 优先级(比最小值高)和一个 :ref:`可配置的堆栈大小<CONFIG_ESP_MAIN_TASK_STACK_SIZE>`。
|
||||
|
||||
.. only:: not CONFIG_FREERTOS_UNICORE
|
||||
|
||||
主任务的内核亲和性也是可以配置的,请参考 :ref:`CONFIG_ESP_MAIN_TASK_AFFINITY`。
|
||||
|
||||
与普通的 FreeRTOS 任务(或嵌入式 C 的 ``main`` 函数)不同,``app_main`` 任务可以返回。如果``app_main`` 函数返回,那么主任务将会被删除。系统将继续运行其他的 RTOS 任务。因此可以将 ``app_main`` 实现为一个创建其他应用任务然后返回的函数,或主应用任务本身。
|
||||
|
||||
.. only:: not CONFIG_FREERTOS_UNICORE
|
||||
|
||||
APP CPU 的内核启动流程
|
||||
------------------------------------
|
||||
|
||||
APP CPU 的启动流程类似但更简单:
|
||||
|
||||
当运行系统初始化时,PRO CPU 上的代码会给 APP CPU 设置好入口地址,解除其复位状态,然后等待 APP CPU 上运行的代码设置一个全局标志,以表明 APP CPU 已经正常启动。 完成后,APP CPU 跳转到 :idf_file:`components/esp_system/port/cpu_start.c` 中的 ``call_start_cpu1`` 函数。
|
||||
|
||||
当 ``start_cpu0`` 函数对 PRO CPU 进行初始化的时候,APP CPU 运行 ``start_cpu_other_cores`` 函数。与 ``start_cpu0`` 函数类似,``start_cpu_other_cores`` 函数是弱链接的,默认为 ``start_cpu_other_cores_default`` 函数,但可以由应用程序替换为不同的函数。
|
||||
|
||||
``start_cpu_other_cores_default`` 函数做了一些与内核相关的系统初始化,然后等待 PRO CPU 启动 FreeRTOS 的调度器,启动完成后,它会执行 ``esp_startup_start_app_other_cores`` 函数,这是另一个默认为 ``esp_startup_start_app_other_cores_default`` 的弱链接函数。
|
||||
|
||||
默认情况下,``esp_startup_start_app_other_cores_default`` 只会自旋,直到 PRO CPU 上的调度器触发中断,以启动 APP CPU 上的 RTOS 调度器。
|
Loading…
Reference in New Issue
Block a user