Merge branch 'docs/update_ulp_lp_core_cn' into 'master'

docs: Update CN translation for ulp-lp-core.rst

Closes DOC-8773

See merge request espressif/esp-idf!33352
pull/11534/merge
Shen Meng Jing 2024-10-25 18:59:41 +08:00
commit b2bc90d97a
2 zmienionych plików z 202 dodań i 126 usunięć

Wyświetl plik

@ -1,11 +1,11 @@
ULP LP-Core Coprocessor Programming
ULP LP Core Coprocessor Programming
===================================
:link_to_translation:`zh_CN:[中文]`
The ULP LP-Core (Low-power core) coprocessor is a variant of the ULP present in {IDF_TARGET_NAME}. It features ultra-low power consumption while also being able to stay powered on while the main CPU stays in low-power modes. This enables the LP-Core coprocessor to handle tasks like GPIO or sensor readings while the main CPU is in sleep mode, resulting in significant overall power savings for the entire system.
The ULP LP core (Low-power core) coprocessor is a variant of the ULP present in {IDF_TARGET_NAME}. It features ultra-low power consumption while also being able to stay powered on while the main CPU stays in low-power modes. This enables the LP core coprocessor to handle tasks like GPIO or sensor readings while the main CPU is in sleep mode, resulting in significant overall power savings for the entire system.
The ULP LP-Core coprocessor has the following features:
The ULP LP core coprocessor has the following features:
* A RV32I (32-bit RISC-V ISA) processor, with the multiplication/division (M), atomic (A), and compressed (C) extensions.
* Interrupt controller.
@ -13,15 +13,15 @@ The ULP LP-Core coprocessor has the following features:
* Can access all of the High-power (HP) SRAM and peripherals when the entire system is active.
* Can access the Low-power (LP) SRAM and peripherals when the HP system is in sleep mode.
Compiling Code for the ULP LP-Core
Compiling Code for the ULP LP Core
----------------------------------
The ULP LP-Core code is compiled together with your ESP-IDF project as a separate binary and automatically embedded into the main project binary. There are two ways to achieve this:
The ULP LP core code is compiled together with your ESP-IDF project as a separate binary and automatically embedded into the main project binary. There are two ways to achieve this:
Using ``ulp_embed_binary``
~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Place the ULP LP-Core code, written in C or assembly (with the ``.S`` extension), in a dedicated directory within the component directory, such as ``ulp/``.
1. Place the ULP LP core code, written in C or assembly (with the ``.S`` extension), in a dedicated directory within the component directory, such as ``ulp/``.
2. After registering the component in the ``CMakeLists.txt`` file, call the ``ulp_embed_binary`` function. Here is an example:
@ -41,7 +41,7 @@ The first argument to ``ulp_embed_binary`` specifies the ULP binary name. The na
Using a Custom CMake Project
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It is also possible to create a custom CMake project for the LP-Core. This gives more control over the build process and allows you to set compile options, link external libraries and all other things that are possible with a regular CMake project.
It is also possible to create a custom CMake project for the LP core. This gives more control over the build process and allows you to set compile options, link external libraries and all other things that are possible with a regular CMake project.
To do this, add the ULP project as an external project in your component ``CMakeLists.txt`` file:
@ -90,7 +90,7 @@ Building Your Project
To compile and build your project:
1. Enable both :ref:`CONFIG_ULP_COPROC_ENABLED` and :ref:`CONFIG_ULP_COPROC_TYPE` in menuconfig, and set :ref:`CONFIG_ULP_COPROC_TYPE` to ``CONFIG_ULP_COPROC_TYPE_LP_CORE``. The :ref:`CONFIG_ULP_COPROC_RESERVE_MEM` option reserves RTC memory for the ULP, and must be set to a value big enough to store both the ULP LP-Core code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one.
1. Enable both :ref:`CONFIG_ULP_COPROC_ENABLED` and :ref:`CONFIG_ULP_COPROC_TYPE` in menuconfig, and set :ref:`CONFIG_ULP_COPROC_TYPE` to ``CONFIG_ULP_COPROC_TYPE_LP_CORE``. The :ref:`CONFIG_ULP_COPROC_RESERVE_MEM` option reserves RTC memory for the ULP, and must be set to a value big enough to store both the ULP LP core code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one.
2. Build the application as usual (e.g., ``idf.py app``).
@ -113,12 +113,12 @@ During the build process, the following steps are taken to build ULP program:
.. _ulp-lp-core-access-variables:
Accessing the ULP LP-Core Program Variables
Accessing the ULP LP Core Program Variables
-------------------------------------------
Global symbols defined in the ULP LP-Core program may be used inside the main program.
Global symbols defined in the ULP LP core program may be used inside the main program.
For example, the ULP LP-Core program may define a variable ``measurement_count`` which defines the number of GPIO measurements the program needs to make before waking up the chip from Deep-sleep.
For example, the ULP LP core program may define a variable ``measurement_count`` which defines the number of GPIO measurements the program needs to make before waking up the chip from Deep-sleep.
.. code-block:: c
@ -132,7 +132,7 @@ For example, the ULP LP-Core program may define a variable ``measurement_count``
...do something.
}
The main program can access the global ULP LP-Core program variables as the build system makes this possible by generating the ``${ULP_APP_NAME}.h`` and ``${ULP_APP_NAME}.ld`` files which define the global symbols present in the ULP LP-Core program. Each global symbol defined in the ULP LP-Core program is included in these files and are prefixed with ``ulp_``.
The main program can access the global ULP LP core program variables as the build system makes this possible by generating the ``${ULP_APP_NAME}.h`` and ``${ULP_APP_NAME}.ld`` files which define the global symbols present in the ULP LP core program. Each global symbol defined in the ULP LP core program is included in these files and are prefixed with ``ulp_``.
The header file contains the declaration of the symbol:
@ -148,7 +148,7 @@ The generated linker script file defines the locations of symbols in LP_MEM:
PROVIDE ( ulp_measurement_count = 0x50000060 );
To access the ULP LP-Core program variables from the main program, the generated header file should be included using an ``include`` statement. This allows the ULP LP-Core program variables to be accessed as regular variables.
To access the ULP LP core program variables from the main program, the generated header file should be included using an ``include`` statement. This allows the ULP LP core program variables to be accessed as regular variables.
.. code-block:: c
@ -160,15 +160,15 @@ To access the ULP LP-Core program variables from the main program, the generated
.. note::
Variables declared in the global scope of the LP-Core program reside in either the ``.bss`` or ``.data`` section of the binary. These sections are initialized when the LP-Core binary is loaded and executed. Accessing these variables from the main program on the HP-Core before the first LP-Core run may result in undefined behavior.
Variables declared in the global scope of the LP core program reside in either the ``.bss`` or ``.data`` section of the binary. These sections are initialized when the LP core binary is loaded and executed. Accessing these variables from the main program on the HP-Core before the first LP core run may result in undefined behavior.
Starting the ULP LP-Core Program
Starting the ULP LP Core Program
--------------------------------
To run a ULP LP-Core program, the main application needs to load the ULP program into RTC memory using the :cpp:func:`ulp_lp_core_load_binary` function, and then start it using the :cpp:func:`ulp_lp_core_run` function.
To run a ULP LP core program, the main application needs to load the ULP program into RTC memory using the :cpp:func:`ulp_lp_core_load_binary` function, and then start it using the :cpp:func:`ulp_lp_core_run` function.
Each ULP LP-Core program is embedded into the ESP-IDF application as a binary blob. The application can reference this blob and load it in the following way (supposed ULP_APP_NAME was defined to ``ulp_app_name``):
Each ULP LP core program is embedded into the ESP-IDF application as a binary blob. The application can reference this blob and load it in the following way (supposed ULP_APP_NAME was defined to ``ulp_app_name``):
.. code-block:: c
@ -191,17 +191,17 @@ Once the program is loaded into LP memory, the application can be configured and
ESP_ERROR_CHECK( ulp_lp_core_run(&cfg) );
ULP LP-Core Program Flow
ULP LP Core Program Flow
------------------------
How the ULP LP-Core coprocessor is started depends on the wake-up source selected in :cpp:type:`ulp_lp_core_cfg_t`. The most common use-case is for the ULP to periodically wake up, do some measurements before either waking up the main CPU or going back to sleep again.
How the ULP LP core coprocessor is started depends on the wake-up source selected in :cpp:type:`ulp_lp_core_cfg_t`. The most common use-case is for the ULP to periodically wake up, do some measurements before either waking up the main CPU or going back to sleep again.
The ULP has the following wake-up sources:
* :c:macro:`ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU` - LP Core can be woken up by the HP CPU.
* :c:macro:`ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER` - LP Core can be woken up by the LP timer.
* :c:macro:`ULP_LP_CORE_WAKEUP_SOURCE_ETM` - LP Core can be woken up by a ETM event. (Not yet supported)
* :c:macro:`ULP_LP_CORE_WAKEUP_SOURCE_LP_IO` - LP Core can be woken up when LP IO level changes. (Not yet supported)
* :c:macro:`ULP_LP_CORE_WAKEUP_SOURCE_LP_UART` - LP Core can be woken up after receiving a certain number of UART RX pulses. (Not yet supported)
* :c:macro:`ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU` - LP core can be woken up by the HP CPU.
* :c:macro:`ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER` - LP core can be woken up by the LP timer.
* :c:macro:`ULP_LP_CORE_WAKEUP_SOURCE_ETM` - LP core can be woken up by a ETM event. (Not yet supported)
* :c:macro:`ULP_LP_CORE_WAKEUP_SOURCE_LP_IO` - LP core can be woken up when LP IO level changes. (Not yet supported)
* :c:macro:`ULP_LP_CORE_WAKEUP_SOURCE_LP_UART` - LP core can be woken up after receiving a certain number of UART RX pulses. (Not yet supported)
When the ULP is woken up, it will go through the following steps:
@ -215,10 +215,10 @@ When the ULP is woken up, it will go through the following steps:
#. Call :cpp:func:`ulp_lp_core_halt`
ULP LP-Core Peripheral Support
ULP LP Core Peripheral Support
------------------------------
To enhance the capabilities of the ULP LP-Core coprocessor, it has access to peripherals that operate in the low-power domain. The ULP LP-Core coprocessor can interact with these peripherals when the main CPU is in sleep mode, and can wake up the main CPU once a wake-up condition is reached. The following peripherals are supported:
To enhance the capabilities of the ULP LP core coprocessor, it has access to peripherals that operate in the low-power domain. The ULP LP core coprocessor can interact with these peripherals when the main CPU is in sleep mode, and can wake up the main CPU once a wake-up condition is reached. The following peripherals are supported:
.. list::
@ -229,10 +229,10 @@ To enhance the capabilities of the ULP LP-Core coprocessor, it has access to per
.. only:: CONFIG_ESP_ROM_HAS_LP_ROM
ULP LP-Core ROM
ULP LP Core ROM
---------------
The ULP LP-Core ROM is a small pre-built piece of code located in LP-ROM, which can't be modified. Similar to the bootloader ROM code ran by the main CPU, this code is executed when the ULP LP-Core coprocessor is started. The ROM code initializes the ULP LP-Core coprocessor and then jumps to the user program. The ROM code also prints boot messages if the LP UART has been initialized.
The ULP LP core ROM is a small pre-built piece of code located in LP-ROM, which can't be modified. Similar to the bootloader ROM code ran by the main CPU, this code is executed when the ULP LP core coprocessor is started. The ROM code initializes the ULP LP core coprocessor and then jumps to the user program. The ROM code also prints boot messages if the LP UART has been initialized.
The ROM code is not executed if :cpp:member:`ulp_lp_core_cfg_t::skip_lp_rom_boot` is set to true. This is useful when you need the ULP to wake-up as quickly as possible and the extra overhead of initializing and printing is unwanted.
@ -244,12 +244,12 @@ To enhance the capabilities of the ULP LP-Core coprocessor, it has access to per
Since these functions are already present in LP-ROM no matter what, using these in your program allows you to reduce the RAM footprint of your ULP application.
ULP LP-Core Interrupts
ULP LP Core Interrupts
----------------------
The LP-Core coprocessor can be configured to handle interrupts from various sources. Examples of such interrupts could be LP IO low/high or LP timer interrupts. To register a handler for an interrupt, simply override any of the weak handlers provided by IDF. A complete list of handlers can be found in :component_file:`ulp_lp_core_interrupts.h <ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h>`. For details on which interrupts are available on a specific target, please consult **{IDF_TARGET_NAME} Technical Reference Manual** [`PDF <{IDF_TARGET_TRM_EN_URL}#ulp>`__].
The LP core coprocessor can be configured to handle interrupts from various sources. Examples of such interrupts could be LP IO low/high or LP timer interrupts. To register a handler for an interrupt, simply override any of the weak handlers provided by IDF. A complete list of handlers can be found in :component_file:`ulp_lp_core_interrupts.h <ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h>`. For details on which interrupts are available on a specific target, please consult **{IDF_TARGET_NAME} Technical Reference Manual** [`PDF <{IDF_TARGET_TRM_EN_URL}#ulp>`__].
For example, to override the handler for the LP IO interrupt, you can define the following function in your ULP LP-Core code:
For example, to override the handler for the LP IO interrupt, you can define the following function in your ULP LP core code:
.. code-block:: c
@ -260,39 +260,40 @@ For example, to override the handler for the LP IO interrupt, you can define the
:c:macro:`LP_CORE_ISR_ATTR` is a macro that is used to define the interrupt handler function. This macro ensures that registers are saved and restored correctly when the interrupt handler is called.
In addition to configuring the interrupt related registers for the interrupt source you want to handle, you also need to enable the interrupts globally in the LP-Core interrupt controller. This can be done using the :cpp:func:`ulp_lp_core_intr_enable` function.
In addition to configuring the interrupt related registers for the interrupt source you want to handle, you also need to enable the interrupts globally in the LP core interrupt controller. This can be done using the :cpp:func:`ulp_lp_core_intr_enable` function.
ULP LP-Core Clock Configuration
ULP LP Core Clock Configuration
-------------------------------
{IDF_TARGET_XTAL_FREQ:default="Not updated", esp32c5="48 MHz", esp32p4="40 MHz"}
The ULP LP-Core clock source is based on the system clock ``LP_FAST_CLK``, see `TRM <{IDF_TARGET_TRM_EN_URL}>`__ > ``Reset and Clock`` for more details.
The ULP LP Core clock source is based on the system clock ``LP_FAST_CLK``, see `TRM <{IDF_TARGET_TRM_EN_URL}>`__ > ``Reset and Clock`` for more details.
.. only:: SOC_CLK_LP_FAST_SUPPORT_XTAL
On {IDF_TARGET_NAME} ``LP_FAST_CLK`` supports using the external {IDF_TARGET_XTAL_FREQ} crystal (XTAL) as the source for ``LP_FAST_CLK``, which allows the ULP LP-Core to run at a higher frequency than with the default ``RTC_FAST_CLOCK`` which runs at around 20 MHz. The drawback is that this clock is normally powered down during sleep to reduce power consumption, with it selected XTAL will also stay powered on during sleep, increasing power consumption. If you only plan to use the LP-Core as a co-processor while the HP-Core is active, then this option can be used to increase both the performance and the frequency stability of the LP-Core.
On {IDF_TARGET_NAME}, ``LP_FAST_CLK`` supports using the external {IDF_TARGET_XTAL_FREQ} crystal (XTAL) as its clock source. This allows the ULP LP Core to run at a higher frequency than with the default ``RTC_FAST_CLOCK``, which runs at around 20 MHz. However, there is a trade-off: this clock is normally powered down during sleep to reduce power consumption, but if XTAL is selected as the source, it will remain powered up during sleep, which increases power consumption. If you only plan to use the LP Core as a coprocessor while the HP Core is active, then selecting XTAL can enhance both the performance and frequency stability of the LP Core.
To enable this feature set :ref:`CONFIG_RTC_FAST_CLK_SRC` to ``CONFIG_RTC_FAST_CLK_SRC_XTAL``.
To enable this feature, set :ref:`CONFIG_RTC_FAST_CLK_SRC` to ``CONFIG_RTC_FAST_CLK_SRC_XTAL``.
Debugging ULP LP-Core Applications
----------------------------------
When programming the LP-Core, it can sometimes be challenging to figure out why the program is not behaving as expected. Here are some strategies to help you debug your LP-Core program:
When programming the LP core, it can sometimes be challenging to figure out why the program is not behaving as expected. Here are some strategies to help you debug your LP core program:
* Use the LP-UART to print: the LP-Core has access to the LP-UART peripheral, which can be used for printing information independently of the main CPU sleep state. See :example:`system/ulp/lp_core/lp_uart/lp_uart_print` for an example of how to use this driver.
* Use the LP-UART to print: the LP core has access to the LP-UART peripheral, which can be used for printing information independently of the main CPU sleep state. See :example:`system/ulp/lp_core/lp_uart/lp_uart_print` for an example of how to use this driver.
* Routing :cpp:func:`lp_core_printf` to the HP-Core console UART with :ref:`CONFIG_ULP_HP_UART_CONSOLE_PRINT`. This allows you to easily print LP-Core information to the already connected HP-Core console UART. The drawback of this approach is that it requires the main CPU to be awake and since there is no synchronization between the LP and HP cores, the output may be interleaved.
* Routing :cpp:func:`lp_core_printf` to the HP-Core console UART with :ref:`CONFIG_ULP_HP_UART_CONSOLE_PRINT`. This allows you to easily print LP core information to the already connected HP-Core console UART. The drawback of this approach is that it requires the main CPU to be awake and since there is no synchronization between the LP and HP cores, the output may be interleaved.
* Share program state through shared variables: as described in :ref:`ulp-lp-core-access-variables`, both the main CPU and the ULP core can easily access global variables in RTC memory. Writing state information to such a variable from the ULP and reading it from the main CPU can help you discern what is happening on the ULP core. The downside of this approach is that it requires the main CPU to be awake, which will not always be the case. Keeping the main CPU awake might even, in some cases, mask problems, as some issues may only occur when certain power domains are powered down.
* Panic handler: the LP-Core has a panic handler that can dump the state of the LP-Core registers by the LP-UART when an exception is detected. To enable the panic handler, set the :ref:`CONFIG_ULP_PANIC_OUTPUT_ENABLE` option to ``y``. This option can be kept disabled to reduce LP-RAM usage by the LP-Core application. To recover a backtrace from the panic dump, it is possible to use esp-idf-monitor_., e.g.:
* Panic handler: the LP core has a panic handler that can dump the state of the LP core registers by the LP-UART when an exception is detected. To enable the panic handler, set the :ref:`CONFIG_ULP_PANIC_OUTPUT_ENABLE` option to ``y``. This option can be kept disabled to reduce LP-RAM usage by the LP core application. To recover a backtrace from the panic dump, it is possible to use esp-idf-monitor_., e.g.:
.. code-block:: bash
python -m esp_idf_monitor --toolchain-prefix riscv32-esp-elf- --target {IDF_TARGET_NAME} --decode-panic backtrace PATH_TO_ULP_ELF_FILE
Debugging ULP LP-Core Applications with GDB and OpenOCD
Debugging ULP LP Core Applications with GDB and OpenOCD
-------------------------------------------------------
It is also possible to debug code running on LP core using GDB and OpenOCD as you usually do for HP cores, but it has some specifics and limitations.
@ -307,7 +308,7 @@ Run OpenOCD with special config file for LP core debugging support. And then run
openocd -f board/{IDF_TARGET_PATH_NAME}-lpcore-builtin.cfg
riscv32-esp-elf-gdb -x gdbinit <path to main program ELF>
``gdbinit`` file contents with inline comments is below. For more details see the next section.
Below is the ``gdbinit`` file content with inline comments. For more details, see the next section.
.. code-block:: bash
@ -339,17 +340,17 @@ LP Core Debugging Specifics
.. list::
#. For convenient debugging you may need to add `-O0` compile option for ULP app in its CMakeLists.txt. See :example:`system/ulp/lp_core/debugging/` how to do this.
:not esp32p4: #. LP core supports limited set of HW exceptions, so, for example, writing at address `0x0` will not cause a panic as it would be for the code running on HP core. This can be overcome to some extent by enabling undefined behavior sanitizer for LP core application, so `ubsan` can help to catch some errors. But note that it will increase code size significantly and it can happen that application won't fit into RTC RAM. To enable `ubsan` for ULP app add `-fsanitize=undefined -fno-sanitize=shift-base` compile option to its CMakeLists.txt. See :example:`system/ulp/lp_core/debugging/` how to do this.
#. To be able to debug program running on LP core debug info and symbols need to be loaded to GDB. It can be done via GDB command line or in ``gdbinit`` file. See section above.
#. Upon startup LP core application is loaded into RAM, so all SW breakpoints set before that moment will get overwritten. The best moment to set breakpoints for LP core application is to do this when LP core program reaches `main` function.
#. When using IDEs it can be that it does not support breakpoint actions/commands configuration shown in ``gdbinit`` above, so in this case you have to preset all breakpoints before debug session start and disable all of them except for ``main``. When program is stopped at ``main`` manually enable remaining breakpoints and resume execution.
#. For convenient debugging, you may need to add ``-O0`` compile option for ULP app in its ``CMakeLists.txt``. See :example:`system/ulp/lp_core/debugging/` on how to do this.
:not esp32p4: #. LP core supports limited set of HW exceptions, so, for example, writing at address `0x0` will not cause a panic as it would be for the code running on HP core. This can be overcome to some extent by enabling undefined behavior sanitizer for LP core application, so `ubsan` can help to catch some errors. But note that it will increase code size significantly and it can happen that application won't fit into RTC RAM. To enable `ubsan` for ULP app, add ``-fsanitize=undefined -fno-sanitize=shift-base`` compile option to its ``CMakeLists.txt``. See :example:`system/ulp/lp_core/debugging/` on how to do this.
#. To be able to debug program running on LP core, debugging information and symbols need to be loaded to GDB. It can be done via GDB command line or in ``gdbinit`` file. See section above.
#. Upon startup, LP core application is loaded into RAM, so all SW breakpoints set before that moment will get overwritten. The best moment to set breakpoints for LP core application is to do this when LP core program reaches `main` function.
#. When using IDEs, it may lack support for configuring breakpoint actions or commands shown in ``gdbinit`` above. Consequently, you have to preset all breakpoints before debug session start and disable all of them except for ``main``. When program stops at ``main``, enable the remaining breakpoints and resume execution manually.
Limitations
~~~~~~~~~~~
#. Currently debugging is not supported when either HP or LP core enters any sleep mode. So it limits available debugging scenarios.
#. FreeRTOS support in OpenOCD is disabled when debugging LP core, so you won't be able to see tasks running in the system. Instead there will be several threads representing HP and LP cores:
#. Currently, debugging is not supported when either HP or LP core enters any sleep mode. So it limits available debugging scenarios.
#. FreeRTOS support in OpenOCD is disabled when debugging LP core, so you won't be able to see tasks running in the system. Instead, there will be several threads representing HP and LP cores:
.. code-block:: bash
@ -360,28 +361,23 @@ Limitations
* 2 Thread 2 "{IDF_TARGET_PATH_NAME}.cpu1" (Name: {IDF_TARGET_PATH_NAME}.cpu1, state: breakpoint) do_things (max=1000000000)
at /home/user/projects/esp/esp-idf/examples/system/ulp/lp_core/debugging/main/lp_core/main.c:21
#. When setting HW breakpoint in GDB it is set on both cores, so the number of available HW breakpoints is limited to the number of them supported by LP core ({IDF_TARGET_SOC_CPU_BREAKPOINTS_NUM} for {IDF_TARGET_NAME}).
#. OpenOCD flash support is disabled. It does not matter for LP core application because it is run completely from RAM and GDB can use SW breakpoints for it. But if you want to set a breakpoint on function from flash used by the code running on HP core (e.g. `app_main`) you should request to set HW breakpoint explicitly via ``hb`` / ``thb`` GDB commands.
#. Since main and ULP programs are linked as separate binaries it is possible for them to have global symbols (functions, variables) with the same name. When you set breakpoint for such a functions using its name GDB will set breakpoints for all of them. It could lead to the problems when one of the function is located in the flash because currently flash support is disabled in OpenOCD when debugging LP core. In that case you can use source line or address based breakpoints.
#. When setting HW breakpoint in GDB, it is set on both cores, so the number of available HW breakpoints is limited to the number of them supported by LP core ({IDF_TARGET_SOC_CPU_BREAKPOINTS_NUM} for {IDF_TARGET_NAME}).
#. OpenOCD flash support is disabled. It does not matter for LP core application because it is run completely from RAM and GDB can use SW breakpoints for it. But if you want to set a breakpoint on function from flash used by the code running on HP core (e.g., `app_main`), you should request to set HW breakpoint explicitly via ``hb`` and ``thb`` GDB commands.
#. Since the main and ULP programs are linked as separate binaries, it is possible for them to have global symbols (such as functions or variables) with the same name. If you set a breakpoint using the function name, GDB will apply it to all instances of that function. This can cause issues if one of the functions is located in the flash, as OpenOCD currently doesn't support flash when debugging the LP core. In such cases, you can set breakpoints using the source line or the function's memory address instead.
Application Examples
--------------------
* :example:`system/ulp/lp_core/gpio` polls GPIO while main CPU is in Deep-sleep.
.. list::
.. only:: esp32c6
* :example:`system/ulp/lp_core/lp_i2c` reads external I2C ambient light sensor (BH1750) while the main CPU is in Deep-sleep and wakes up the main CPU once a threshold is met.
* :example:`system/ulp/lp_core/lp_uart/lp_uart_echo` reads data written to a serial console and echoes it back. This example demonstrates the usage of the LP UART driver running on the LP core.
* :example:`system/ulp/lp_core/lp_uart/lp_uart_print` shows how to print various statements from a program running on the LP core.
* :example:`system/ulp/lp_core/interrupt` shows how to register an interrupt handler on the LP core to receive an interrupt triggered by the main CPU.
* :example:`system/ulp/lp_core/gpio_intr_pulse_counter` shows how to use GPIO interrupts to count pulses while the main CPU is in Deep-sleep mode.
* :example:`system/ulp/lp_core/build_system/` demonstrates how to include custom ``CMakeLists.txt`` file for the ULP app.
* :example:`system/ulp/lp_core/debugging` shows how to debug code running on LP core using GDB and OpenOCD.
- :example:`system/ulp/lp_core/gpio` polls GPIO while main CPU is in Deep-sleep.
:esp32c6: - :example:`system/ulp/lp_core/lp_i2c` reads external I2C ambient light sensor (BH1750) while the main CPU is in Deep-sleep and wakes up the main CPU once a threshold is met.
- :example:`system/ulp/lp_core/lp_uart/lp_uart_echo` reads data written to a serial console and echoes it back. This example demonstrates the usage of the LP UART driver running on the LP core.
- :example:`system/ulp/lp_core/lp_uart/lp_uart_print` shows how to print various statements from a program running on the LP core.
- :example:`system/ulp/lp_core/interrupt` shows how to register an interrupt handler on the LP core to receive an interrupt triggered by the main CPU.
- :example:`system/ulp/lp_core/gpio_intr_pulse_counter` shows how to use GPIO interrupts to count pulses while the main CPU is in Deep-sleep mode.
- :example:`system/ulp/lp_core/build_system/` demonstrates how to include custom ``CMakeLists.txt`` file for the ULP app.
- :example:`system/ulp/lp_core/debugging` shows how to debug code running on LP core using GDB and OpenOCD.
API Reference
-------------

Wyświetl plik

@ -1,11 +1,11 @@
ULP LP-Core 协处理器编程
===================================
ULP LP 内核协处理器编程
=======================
:link_to_translation:`en:[English]`
ULP LP-Core低功耗内核协处理器是 {IDF_TARGET_NAME} 中 ULP 的一个变型。它具有超低功耗,同时还能在主 CPU 处于低功耗模式时保持运行。因此LP-Core 协处理器能够在主 CPU 处于睡眠模式时处理 GPIO 或传感器读取等任务,从而显著降低整个系统的整体功耗。
ULP LP 内核 (Low-power core) 协处理器是 {IDF_TARGET_NAME} 中 ULP 的一个变型。它具有超低功耗,同时还能在主 CPU 处于低功耗模式时保持运行。因此LP 内核协处理器能够在主 CPU 处于睡眠模式时处理 GPIO 或传感器读取等任务,从而显著降低整个系统的整体功耗。
ULP LP-Core 协处理器具有以下功能:
ULP LP 内核协处理器具有以下功能:
* RV32I 处理器32 位 RISC-V ISA支持乘法/除法 (M)、原子 (A) 和压缩 (C) 扩展。
* 中断控制器。
@ -13,17 +13,17 @@ ULP LP-Core 协处理器具有以下功能:
* 当整个系统处于 active 模式时,可以访问所有的高功耗 (HP) SRAM 和外设。
* 当 HP 系统处于睡眠模式时,可以访问低功耗 (LP) SRAM 和外设。
编译 ULP LP-Core 代码
----------------------------------
编译 ULP LP 内核代码
--------------------
ULP LP-Core 代码会与 ESP-IDF 项目共同编译,生成一个单独的二进制文件,并自动嵌入到主项目的二进制文件中。编译可通过以下两种方式实现:
ULP LP 内核代码会与 ESP-IDF 项目共同编译,生成一个单独的二进制文件,并自动嵌入到主项目的二进制文件中。编译可通过以下两种方式实现:
使用 ``ulp_embed_binary``
~~~~~~~~~~~~~~~~~~~~~~~~~
1. 将用 C 语言或汇编语言编写的 ULP LP-Core 代码(带有 ``.S`` 扩展名)放在组件目录下的专用目录中,例如 ``ulp/``
1. 将用 C 语言或汇编语言编写的 ULP LP 内核代码(带有 ``.S`` 扩展名)放在组件目录下的专用目录中,例如 ``ulp/``
2. 在 CMakeLists.txt 文件中注册组件后,调用 ``ulp_embed_binary`` 函数。例如:
2. 在 ``CMakeLists.txt`` 文件中注册组件后,调用 ``ulp_embed_binary`` 函数。例如:
.. code-block:: cmake
@ -41,7 +41,7 @@ ULP LP-Core 代码会与 ESP-IDF 项目共同编译,生成一个单独的二
使用自定义的 CMake 项目
~~~~~~~~~~~~~~~~~~~~~~~
也可以为 LP-Core 创建自定义的 CMake 项目,从而更好地控制构建过程,并实现常规 CMake 项目的操作,例如设置编译选项、链接外部库等。
也可以为 LP 内核创建自定义的 CMake 项目,从而更好地控制构建过程,并实现常规 CMake 项目的操作,例如设置编译选项、链接外部库等。
请在组件的 ``CMakeLists.txt`` 文件中将 ULP 项目添加为外部项目:
@ -90,7 +90,7 @@ ULP LP-Core 代码会与 ESP-IDF 项目共同编译,生成一个单独的二
若想编译和构建项目,请执行以下操作:
1. 在 menuconfig 中启用 :ref:`CONFIG_ULP_COPROC_ENABLED`:ref:`CONFIG_ULP_COPROC_TYPE` 选项,并将 :ref:`CONFIG_ULP_COPROC_TYPE` 设置为 ``CONFIG_ULP_COPROC_TYPE_LP_CORE``。:ref:`CONFIG_ULP_COPROC_RESERVE_MEM` 选项为 ULP 保留 RTC 内存,因此必须设置为一个足够大的值,以存储 ULP LP-Core 代码和数据。如果应用程序组件包含多个 ULP 程序,那么 RTC 内存的大小必须足够容纳其中最大的程序。
1. 在 menuconfig 中启用 :ref:`CONFIG_ULP_COPROC_ENABLED`:ref:`CONFIG_ULP_COPROC_TYPE` 选项,并将 :ref:`CONFIG_ULP_COPROC_TYPE` 设置为 ``CONFIG_ULP_COPROC_TYPE_LP_CORE``。:ref:`CONFIG_ULP_COPROC_RESERVE_MEM` 选项为 ULP 保留 RTC 内存,因此必须设置为一个足够大的值,以存储 ULP LP 内核代码和数据。如果应用程序组件包含多个 ULP 程序,那么 RTC 内存的大小必须足够容纳其中最大的程序。
2. 按照常规步骤构建应用程序(例如 ``idf.py app``)。
@ -113,12 +113,12 @@ ULP LP-Core 代码会与 ESP-IDF 项目共同编译,生成一个单独的二
.. _ulp-lp-core-access-variables:
访问 ULP LP-Core 程序变量
-------------------------------------------
访问 ULP LP 内核程序变量
------------------------
在主程序中可以使用在 ULP LP-Core 程序中定义的全局符号。
在主程序中可以使用在 ULP LP 内核程序中定义的全局符号。
例如ULP LP-Core 程序定义了一个变量 ``measurement_count``,用来表示程序从深度睡眠中唤醒芯片前所需的 GPIO 测量次数。
例如ULP LP 内核程序定义了一个变量 ``measurement_count``,用来表示程序从深度睡眠中唤醒芯片前所需的 GPIO 测量次数。
.. code-block:: c
@ -132,7 +132,7 @@ ULP LP-Core 代码会与 ESP-IDF 项目共同编译,生成一个单独的二
...do something.
}
主程序可以访问 ULP LP-Core 程序全局变量,这是因为构建系统生成了 ``${ULP_APP_NAME}.h````${ULP_APP_NAME}.ld`` 文件,文件中定义了 ULP LP-Core 程序中现有的的全局符号。在 ULP LP-Core 程序中定义的每个全局符号都包含在这两个文件中,并具有前缀 ``ulp_``
主程序可以访问 ULP LP 内核程序全局变量,这是因为构建系统生成了 ``${ULP_APP_NAME}.h````${ULP_APP_NAME}.ld`` 文件,文件中定义了 ULP LP 内核程序中现有的的全局符号。在 ULP LP 内核程序中定义的每个全局符号都包含在这两个文件中,并具有前缀 ``ulp_``
头文件中包含符号的声明:
@ -148,7 +148,7 @@ ULP LP-Core 代码会与 ESP-IDF 项目共同编译,生成一个单独的二
PROVIDE ( ulp_measurement_count = 0x50000060 );
要从主程序访问 ULP LP-Core 程序变量,应使用 ``include`` 语句将生成的头文件包含在主程序中,这样就可以像访问常规变量一样访问 ULP LP-Core 程序变量。
要从主程序访问 ULP LP 内核程序变量,应使用 ``include`` 语句将生成的头文件包含在主程序中,这样就可以像访问常规变量一样访问 ULP LP 内核程序变量。
.. code-block:: c
@ -160,15 +160,15 @@ ULP LP-Core 代码会与 ESP-IDF 项目共同编译,生成一个单独的二
.. note::
LP-Core 程序全局变量存储在二进制文件的 ``.bss`` 或者 ``.data`` 部分。这些部分在加载和执行 LP-Core 二进制文件时被初始化。在首次运行 LP-Core 之前,从 HP-Core 主程序访问这些变量可能会导致未定义行为。
LP 内核程序全局变量存储在二进制文件的 ``.bss`` 或者 ``.data`` 部分。这些部分在加载和执行 LP 内核二进制文件时被初始化。在首次运行 LP 内核之前,从 HP-Core 主程序访问这些变量可能会导致未定义行为。
启动 ULP LP-Core 程序
--------------------------------
启动 ULP LP 内核程序
--------------------
要运行 ULP LP-Core 程序,主应用程序需要先使用 :cpp:func:`ulp_lp_core_load_binary` 函数将 ULP 程序加载到 RTC 内存中,然后使用 :cpp:func:`ulp_lp_core_run` 函数进行启动。
要运行 ULP LP 内核程序,主应用程序需要先使用 :cpp:func:`ulp_lp_core_load_binary` 函数将 ULP 程序加载到 RTC 内存中,然后使用 :cpp:func:`ulp_lp_core_run` 函数进行启动。
每个 ULP LP-Core 程序以二进制 blob 的形式嵌入到 ESP-IDF 应用程序中。应用程序可以按照如下方式引用和加载该 blob假设 ULP_APP_NAME 被定义为 ``ulp_app_name``
每个 ULP LP 内核程序以二进制 blob 的形式嵌入到 ESP-IDF 应用程序中。应用程序可以按照如下方式引用和加载该 blob假设 ULP_APP_NAME 被定义为 ``ulp_app_name``
.. code-block:: c
@ -191,10 +191,10 @@ ULP LP-Core 代码会与 ESP-IDF 项目共同编译,生成一个单独的二
ESP_ERROR_CHECK( ulp_lp_core_run(&cfg) );
ULP LP-Core 程序流程
------------------------
ULP LP 内核程序流程
-------------------
ULP LP-Core 协处理器如何启动取决于 :cpp:type:`ulp_lp_core_cfg_t` 中选择的唤醒源。最常见的用例是 ULP 定期唤醒,在进行一些测量后唤醒主 CPU或者再次进入睡眠状态。
ULP LP 内核协处理器如何启动取决于 :cpp:type:`ulp_lp_core_cfg_t` 中选择的唤醒源。最常见的用例是 ULP 定期唤醒,在进行一些测量后唤醒主 CPU或者再次进入睡眠状态。
ULP 有以下唤醒源:
* :c:macro:`ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU` - LP 内核可以被 HP CPU 唤醒。
@ -215,10 +215,10 @@ ULP 被唤醒时会经历以下步骤:
#. 调用 :cpp:func:`ulp_lp_core_halt`
ULP LP-Core 支持的外设
------------------------------
ULP LP 内核支持的外设
---------------------
为了增强 ULP LP-Core 协处理器的功能它可以访问在低功耗电源域运行的外设。ULP LP-Core 协处理器可以在主 CPU 处于睡眠模式时与这些外设进行交互,并在达到唤醒条件时唤醒主 CPU。以下为支持的外设
为了增强 ULP LP 内核协处理器的功能它可以访问在低功耗电源域运行的外设。ULP LP 内核协处理器可以在主 CPU 处于睡眠模式时与这些外设进行交互,并在达到唤醒条件时唤醒主 CPU。以下为支持的外设
.. list::
@ -229,14 +229,14 @@ ULP LP-Core 支持的外设
.. only:: CONFIG_ESP_ROM_HAS_LP_ROM
ULP LP-Core ROM
ULP LP 内核 ROM
---------------
ULP LP-Core ROM 是位于 LP-ROM 中的一小段预编译代码,用户无法修改。与主 CPU 运行的引导加载程序 ROM 代码类似ULP LP-Core ROM 也在 ULP LP-Core 协处理器启动时执行。该 ROM 代码会初始化 ULP LP-Core 协处理器,随后跳转到用户程序。如果已初始化 LP UART该 ROM 代码还会打印启动信息。
ULP LP 内核 ROM 是位于 LP-ROM 中的一小段预编译代码,用户无法修改。与主 CPU 运行的引导加载程序 ROM 代码类似ULP LP 内核 ROM 也在 ULP LP 内核协处理器启动时执行。该 ROM 代码会初始化 ULP LP 内核协处理器,随后跳转到用户程序。如果已初始化 LP UART该 ROM 代码还会打印启动信息。
如果已将 :cpp:member:`ulp_lp_core_cfg_t::skip_lp_rom_boot` 设置为真,则不会执行 ULP LP-Core ROM 代码。如需尽快唤醒 ULP同时避免初始化和信息打印产生额外开销则可使用这一功能。
如果已将 :cpp:member:`ulp_lp_core_cfg_t::skip_lp_rom_boot` 设置为真,则不会执行 ULP LP 内核 ROM 代码。如需尽快唤醒 ULP同时避免初始化和信息打印产生额外开销则可使用这一功能。
除上述启动代码ULP LP-Core ROM 代码还提供以下功能和接口:
除上述启动代码ULP LP 内核 ROM 代码还提供以下功能和接口:
* :component_file:`ROM.ld 接口 <esp_rom/{IDF_TARGET_PATH_NAME}/ld/{IDF_TARGET_PATH_NAME}lp.rom.ld>`
* :component_file:`newlib.ld 接口 <esp_rom/{IDF_TARGET_PATH_NAME}/ld/{IDF_TARGET_PATH_NAME}lp.rom.newlib.ld>`
@ -244,12 +244,12 @@ ULP LP-Core 支持的外设
在任何情况下,这些函数都存在于 LP-ROM 中,因此在程序中使用这些函数可以减少 ULP 应用程序的 RAM 占用。
ULP LP-Core 中断
----------------
ULP LP 内核中断
---------------
配置 LP-Core 协处理器,可以处理各种类型的中断,例如 LP IO 低/高电平中断或是 LP 定时器中断。只需重写 IDF 提供的任何一个弱处理函数,就可以注册一个中断处理程序。所有处理程序可见 :component_file:`ulp_lp_core_interrupts.h <ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h>`。有关特定目标可使用的中断的详细信息,请参阅 **{IDF_TARGET_NAME} 技术参考手册** [`PDF <{IDF_TARGET_TRM_CN_URL}#ulp>`__]。
配置 LP 内核协处理器,可以处理各种类型的中断,例如 LP IO 低/高电平中断或是 LP 定时器中断。只需重写 IDF 提供的任何一个弱处理函数,就可以注册一个中断处理程序。所有处理程序可见 :component_file:`ulp_lp_core_interrupts.h <ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h>`。有关特定目标可使用的中断的详细信息,请参阅 **{IDF_TARGET_NAME} 技术参考手册** [`PDF <{IDF_TARGET_TRM_CN_URL}#ulp>`__]。
例如,要重写 LP IO 中断的处理程序,可以在 ULP LP-Core 代码中定义以下函数:
例如,要重写 LP IO 中断的处理程序,可以在 ULP LP 内核代码中定义以下函数:
.. code-block:: c
@ -260,50 +260,130 @@ ULP LP-Core 中断
:c:macro:`LP_CORE_ISR_ATTR` 宏用于定义中断处理函数,可确保调用中断处理程序时妥善保存并恢复寄存器。
除了为需要处理的中断源配置相关的中断寄存器外,还要调用 :cpp:func:`ulp_lp_core_intr_enable` 函数,在 LP-Core 中断控制器中使能全局中断。
除了为需要处理的中断源配置相关的中断寄存器外,还要调用 :cpp:func:`ulp_lp_core_intr_enable` 函数,在 LP 内核中断控制器中使能全局中断。
调试 ULP LP-Core 应用程序
-------------------------
ULP LP 内核时钟配置
-------------------
在编程 LP-Core 时,有时很难弄清楚程序未按预期运行的原因。请参考以下策略,调试 LP-Core 程序:
{IDF_TARGET_XTAL_FREQ:default="未更新", esp32c5="48 MHz", esp32p4="40 MHz"}
* 使用 LP-UART 打印LP-Core 可以访问 LP-UART 外设,在主 CPU 处于睡眠状态时独立打印信息。有关使用此驱动程序的示例,请参阅 :example:`system/ulp/lp_core/lp_uart/lp_uart_print`
ULP LP 内核的时钟源来自系统时钟 ``LP_FAST_CLK``,详情请参见 `技术参考手册 <{IDF_TARGET_TRM_CN_URL}>`__ > ``复位和时钟``
* 通过 :ref:`CONFIG_ULP_HP_UART_CONSOLE_PRINT`,将 :cpp:func:`lp_core_printf` 路由到 HP-Core 控制台 UART可以轻松地将 LP-Core 信息打印到已经连接的 HP-Core 控制台 UART。此方法的缺点是需要主 CPU 处于唤醒状态,并且由于 LP 核与 HP 核未同步,输出可能会交错。
.. only:: SOC_CLK_LP_FAST_SUPPORT_XTAL
在 {IDF_TARGET_NAME} 上,``LP_FAST_CLK`` 支持使用外部 {IDF_TARGET_XTAL_FREQ} 晶振 (XTAL) 作为其时钟源。默认时钟源 ``RTC_FAST_CLOCK`` 的运行频率约为 20 MHz使用外部晶振时钟后ULP LP 内核将以更高的频率运行。缺点在于,``LP_FAST_CLK`` 在休眠期间通常会断电以减少功耗,而选择 XTAL 作为时钟源后,休眠期间时钟仍将保持通电,造成功耗增加。因此,如果仅希望在 HP 内核活动时将 LP 内核用作协处理器,则可以使用 XTAL 以提高 LP 内核的性能和频率稳定性。
要启用此功能,请将 :ref:`CONFIG_RTC_FAST_CLK_SRC` 设置为 ``CONFIG_RTC_FAST_CLK_SRC_XTAL``
调试 ULP LP 内核应用程序
------------------------
在编程 LP 内核时,有时很难弄清楚程序未按预期运行的原因。请参考以下策略,调试 LP 内核程序:
* 使用 LP-UART 打印LP 内核可以访问 LP-UART 外设,在主 CPU 处于睡眠状态时独立打印信息。有关使用此驱动程序的示例,请参阅 :example:`system/ulp/lp_core/lp_uart/lp_uart_print`
* 通过 :ref:`CONFIG_ULP_HP_UART_CONSOLE_PRINT`,将 :cpp:func:`lp_core_printf` 路由到 HP-Core 控制台 UART可以轻松地将 LP 内核信息打印到已经连接的 HP-Core 控制台 UART。此方法的缺点是需要主 CPU 处于唤醒状态,并且由于 LP 内核与 HP 内未同步,输出可能会交错。
* 通过共享变量共享程序状态:如 :ref:`ulp-lp-core-access-variables` 所述,主 CPU 和 ULP 内核都可以轻松访问 RTC 内存中的全局变量。若想了解 ULP 内核的运行状态,可以将状态信息从 ULP 写入变量中,并通过主 CPU 读取信息。这种方法的缺点在于它需要主 CPU 一直处于唤醒状态,而这通常很难实现。另外,若主 CPU 一直处于唤醒状态,可能会掩盖某些问题,因为部分问题只会在特定电源域断电时发生。
* 紧急处理程序当检测到异常时LP-Core 的紧急处理程序会把 LP-Core 寄存器的状态通过 LP-UART 发送出去。将 :ref:`CONFIG_ULP_PANIC_OUTPUT_ENABLE` 选项设置为 ``y``,可以启用紧急处理程序。禁用此选项将减少 LP-Core 应用程序的 LP-RAM 使用量。若想从紧急转储中解析栈回溯,可以使用 esp-idf-monitor_例如
* 紧急处理程序当检测到异常时LP 内核的紧急处理程序会把 LP 内核寄存器的状态通过 LP-UART 发送出去。将 :ref:`CONFIG_ULP_PANIC_OUTPUT_ENABLE` 选项设置为 ``y``,可以启用紧急处理程序。禁用此选项将减少 LP 内核应用程序的 LP-RAM 使用量。若想从紧急转储中解析栈回溯,可以使用 esp-idf-monitor_例如
.. code-block:: bash
python -m esp_idf_monitor --toolchain-prefix riscv32-esp-elf- --target {IDF_TARGET_NAME} --decode-panic backtrace PATH_TO_ULP_ELF_FILE
调试 ULP LP 内核应用程序:使用 GDB 和 OpenOCD
----------------------------------------------
与调试 HP 内核类似,也可以用 GDB 和 OpenOCD 来调试 LP 内核上的代码,但要注意其特殊之处和限制条件。
调试会话
~~~~~~~~
使用支持 LP 内核调试的特殊配置文件来运行 OpenOCD然后用特殊的 ``gdbinit`` 文件运行 GDB。
.. code-block:: bash
openocd -f board/{IDF_TARGET_PATH_NAME}-lpcore-builtin.cfg
riscv32-esp-elf-gdb -x gdbinit <path to main program ELF>
以下是带有内联注释的 ``gdbinit`` 文件内容,详细信息请参考下一章节。
.. code-block:: bash
# 连接到目标
target extended-remote :3333
# 重置芯片
mon reset halt
maintenance flush register-cache
# 添加 ULP 程序的符号和调试信息
add-symbol <ULP 程序 ELF 文件路径>
# 设置临时硬件断点
# 如果需要的断点数量超过硬件支持的数量
thb main
commands
# 在这里设置断点
# 此时 ULP 程序已加载到 RAM 中
# 若无可用的硬件断点插槽GDB 将设置软件断点
b func1
b func2
b func3
# 恢复执行
c
end
# 重置后启动主程序
c
LP 内核调试特性
~~~~~~~~~~~~~~~
.. list::
#. 为了方便调试,请在 ULP 应用的 ``CMakeLists.txt`` 文件中添加 ``-O0`` 编译选项。具体操作步骤请参见 :example:`system/ulp/lp_core/debugging/`
:not esp32p4: #. LP 内核支持的硬件异常类型有限,例如,写入地址 `0x0` 不会像在 HP 内核上一样造成系统崩溃。启用 LP 内核应用程序的未定义行为检测器 (`ubsan`) 可以捕捉一些错误,从而在一定程度上弥补这一限制。但请注意,这将显著增加代码量,可能会导致应用程序超出 RTC RAM 的容量限制。要启用 `ubsan`,请在 ``CMakeLists.txt`` 文件中添加 ``-fsanitize=undefined -fno-sanitize=shift-base`` 编译选项。具体操作步骤请参见 :example:`system/ulp/lp_core/debugging/`。
#. 为了调试运行在 LP 内核上的程序,需要先将调试信息和符号加载到 GDB 中。这可以通过 GDB 命令行或在 ``gdbinit`` 文件中完成。具体操作步骤请参见上文。
#. LP 内核应用程序会在启时会加载到 RAM 中,在此之前设置的所有软件断点都会被覆盖。设置 LP 内核应用断点的最佳时机是在 LP 内核程序运行至 ``main`` 函数之时。
#. 使用 IDE 时,可能无法配置上述 ``gdbinit`` 文件中的断点操作或命令。因此,请在调试会话开始前预设并禁用所有断点,只保留 ``main`` 函数处的断点。当程序在 ``main`` 处停止时,手动启用其余断点并恢复执行。
限制
~~~~
#. 调试场景有限制:目前,当 HP 内核或 LP 内核进入睡眠模式时,将无法调适。
#. 调试 内核时OpenOCD 不支持 FreeRTOS因此无法看到系统中正在运行的任务但会有几个线程代表 HP 和 LP 内核:
.. code-block:: bash
(gdb) info thread
Id Target Id Frame
1 Thread 1 "{IDF_TARGET_PATH_NAME}.cpu0" (Name: {IDF_TARGET_PATH_NAME}.cpu0, state: debug-request) 0x40803772 in esp_cpu_wait_for_intr ()
at /home/user/projects/esp/esp-idf/components/esp_hw_support/cpu.c:64
* 2 Thread 2 "{IDF_TARGET_PATH_NAME}.cpu1" (Name: {IDF_TARGET_PATH_NAME}.cpu1, state: breakpoint) do_things (max=1000000000)
at /home/user/projects/esp/esp-idf/examples/system/ulp/lp_core/debugging/main/lp_core/main.c:21
#. 在 GDB 中设置硬件断点时,这些断点会同时应用到两个内核上,因此可用的硬件断点数量受 LP 内核支持数量({IDF_TARGET_NAME} 有 {IDF_TARGET_SOC_CPU_BREAKPOINTS_NUM} 个)所限。
#. OpenOCD 的 flash 支持被禁用。LP 内核应用程序完全在 RAM 中运行,且 GDB 可以为其使用软件断点,因而该限制无关紧要。但若想在 HP 内核运行的代码中调用的 flash 函数(例如 `app_main`)上设置断点,则需要通过 ``hb````thb`` GDB 命令显式请求设置硬件断点。
#. 由于主程序和 ULP 程序被链接为独立的二进制文件,它们可能会拥有相同名称的全局符号(如函数或变量)。若通过函数名称设置断点,则 GDB 将为所有同名函数设置断点。在调试 LP 内核时OpenOCD 不支持 flash因此如果上述函数位于 flash 中,可能会引发问题。此时建议通过源代码行号或函数的内存地址来设置断点。
应用示例
--------
* :example:`system/ulp/lp_core/gpio` 展示了 ULP LP-Core 协处理器在主 CPU 深度睡眠时轮询 GPIO。
.. list::
.. only:: esp32c6
* :example:`system/ulp/lp_core/lp_i2c` 展示了 ULP LP-Core 协处理器在主 CPU 深度睡眠时读取外部 I2C 环境光传感器 (BH1750),并在达到阈值时唤醒主 CPU。
* :example:`system/ulp/lp_core/lp_uart/lp_uart_echo` 展示了低功耗内核上运行的 LP UART 驱动程序如何读取并回显写入串行控制台的数据。
* :example:`system/ulp/lp_core/lp_uart/lp_uart_print` 展示了如何在低功耗内核上使用串口打印功能。
* :example:`system/ulp/lp_core/interrupt` 展示了如何在 LP 内核上注册中断处理程序,接收由主 CPU 触发的中断。
* :example:`system/ulp/lp_core/gpio_intr_pulse_counter` 展示了如何在主 CPU 处于 Deep-sleep 模式时,使用 GPIO 中断为脉冲计数。
* :example:`system/ulp/lp_core/build_system/` 演示了如何为 ULP 应用程序添加自定义的 ``CMakeLists.txt`` 文件。
- :example:`system/ulp/lp_core/gpio` 展示了 ULP LP 内核协处理器在主 CPU 深度睡眠时轮询 GPIO。
:esp32c6: - :example:`system/ulp/lp_core/lp_i2c` 展示了 ULP LP 内核协处理器在主 CPU 深度睡眠时读取外部 I2C 环境光传感器 (BH1750),并在达到阈值时唤醒主 CPU。
- :example:`system/ulp/lp_core/lp_uart/lp_uart_echo` 展示了低功耗内核上运行的 LP UART 驱动程序如何读取并回显写入串行控制台的数据。
- :example:`system/ulp/lp_core/lp_uart/lp_uart_print` 展示了如何在低功耗内核上使用串口打印功能。
- :example:`system/ulp/lp_core/interrupt` 展示了如何在 LP 内核上注册中断处理程序,接收由主 CPU 触发的中断。
- :example:`system/ulp/lp_core/gpio_intr_pulse_counter` 展示了如何在主 CPU 处于 Deep-sleep 模式时,使用 GPIO 中断为脉冲计数。
- :example:`system/ulp/lp_core/build_system/` 演示了如何为 ULP 应用程序添加自定义的 ``CMakeLists.txt`` 文件。
- :example:`system/ulp/lp_core/debugging` 演示了如何使用 GDB 和 OpenOCD 来调试运行在 LP 内核上的代码。
API 参考
-------------
--------
主 CPU API 参考
~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~
.. include-build-file:: inc/ulp_lp_core.inc
.. include-build-file:: inc/lp_core_i2c.inc
@ -320,7 +400,7 @@ API 参考
.. include-build-file:: inc/lp_core_types.inc
LP 内核 API 参考
~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~
.. include-build-file:: inc/ulp_lp_core_utils.inc
.. include-build-file:: inc/ulp_lp_core_gpio.inc