# ULP LP 内核协处理器编程

[English]

ULP LP 内核 (Low-power core) 协处理器是 ESP32-C6 中 ULP 的一个变型。它具有超低功耗，同时还能在主 CPU 处于低功耗模式时保持运行。因此，LP 内核协处理器能够在主 CPU 处于睡眠模式时处理 GPIO 或传感器读取等任务，从而显著降低整个系统的整体功耗。

ULP LP 内核协处理器具有以下功能：

- RV32I 处理器（32 位 RISC-V ISA），支持乘法/除法 (M)、原子 (A) 和压缩 (C) 扩展。

- 中断控制器。

- 包含一个调试模块，支持通过 JTAG 进行外部调试。

- 当整个系统处于 active 模式时，可以访问所有的高功耗 (HP) SRAM 和外设。

- 当 HP 系统处于睡眠模式时，可以访问低功耗 (LP) SRAM 和外设。

## 编译 ULP LP 内核代码

ULP LP 内核代码会与 ESP-IDF 项目共同编译，生成一个单独的二进制文件，并自动嵌入到主项目的二进制文件中。编译可通过以下两种方式实现：

### 使用 `ulp_embed_binary`

1. 将用 C 语言或汇编语言编写的 ULP LP 内核代码（带有 `.S` 扩展名）放在组件目录下的专用目录中，例如 `ulp/`。

2. 在 `CMakeLists.txt` 文件中注册组件后，调用 `ulp_embed_binary` 函数。例如：

    idf_component_register()

    set(ulp_app_name ulp_${COMPONENT_NAME})
    set(ulp_sources "ulp/ulp_c_source_file.c" "ulp/ulp_assembly_source_file.S")
    set(ulp_exp_dep_srcs "ulp_c_source_file.c")

    ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}")

`ulp_embed_binary` 的第一个参数指定生成的 ULP 二进制文件名。该文件名也用于其他生成的文件，如 ELF 文件、映射文件、头文件和链接器导出文件。第二个参数指定 ULP 源文件。第三个参数指定组件源文件列表，其中包括生成的头文件。此列表用以正确构建依赖，并确保在编译这些文件前创建要生成的头文件。有关 ULP 应用程序生成头文件的概念，请参阅本文档后续章节。

在这个生成的头文件中，ULP 代码中的变量默认以 `ulp_` 作为前缀。

如果需要嵌入多个 ULP 程序，可以添加自定义前缀，以避免变量名冲突，如下所示：

    idf_component_register()

    set(ulp_app_name ulp_${COMPONENT_NAME})
    set(ulp_sources "ulp/ulp_c_source_file.c" "ulp/ulp_assembly_source_file.S")
    set(ulp_exp_dep_srcs "ulp_c_source_file.c")

    ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}" PREFIX "ULP::")

最后的 PREFIX 参数可以是 C 语言风格命名的前缀（如 `ulp2_`）或 C++ 风格命名的前缀（如 `ULP::`）。

### 使用自定义的 CMake 项目

也可以为 LP 内核创建自定义的 CMake 项目，从而更好地控制构建过程，并实现常规 CMake 项目的操作，例如设置编译选项、链接外部库等。

请在组件的 `CMakeLists.txt` 文件中将 ULP 项目添加为外部项目：

    ulp_add_project("ULP_APP_NAME" "${CMAKE_SOURCE_DIR}/PATH_TO_DIR_WITH_ULP_PROJECT_FILE/")

请创建一个文件夹，并在文件夹中添加 ULP 项目文件及 `CMakeLists.txt` 文件，该文件夹的位置应与 `ulp_add_project` 函数中指定的路径一致。`CMakeLists.txt` 文件应如下所示：

    cmake_minimum_required(VERSION 3.16)

    # 项目/目标名称由主项目传递，允许 IDF 依赖此目标
    # 将二进制文件嵌入到主应用程序中
    project(${ULP_APP_NAME})
    add_executable(${ULP_APP_NAME} main.c)

    # 导入 ULP 项目辅助函数
    include(IDFULPProject)

    # 应用默认的编译选项
    ulp_apply_default_options(${ULP_APP_NAME})

    # 应用 IDF ULP 组件提供的默认源文件
    ulp_apply_default_sources(${ULP_APP_NAME})

    # 添加构建二进制文件的目标，并添加链接脚本，用于将 ULP 共享变量导出到主应用程序
    ulp_add_build_binary_targets(${ULP_APP_NAME})

    # 以下内容是可选的，可以用于自定义构建过程

    # 创建自定义库
    set(lib_path "${CMAKE_CURRENT_LIST_DIR}/lib")
    add_library(custom_lib STATIC "${lib_path}/lib_src.c")
    target_include_directories(custom_lib PUBLIC "${lib_path}/")

    # 链接到库
    target_link_libraries(${ULP_APP_NAME} PRIVATE custom_lib)

    # 设置自定义编译标志
    target_compile_options(${ULP_APP_NAME} PRIVATE -msave-restore)

### 构建项目

若想编译和构建项目，请执行以下操作：

1. 在 menuconfig 中启用 CONFIG_ULP_COPROC_ENABLED 和 CONFIG_ULP_COPROC_TYPE 选项，并将 CONFIG_ULP_COPROC_TYPE 设置为 `CONFIG_ULP_COPROC_TYPE_LP_CORE`。CONFIG_ULP_COPROC_RESERVE_MEM 选项为 ULP 保留 RTC 内存，因此必须设置为一个足够大的值，以存储 ULP LP 内核代码和数据。如果应用程序组件包含多个 ULP 程序，那么 RTC 内存的大小必须足够容纳其中最大的程序。

2. 按照常规步骤构建应用程序（例如 `idf.py app`）。

在构建过程中，采取以下步骤来构建 ULP 程序：

> 1. 通过 C 编译器和汇编器运行每个源文件。 此步骤会在组件构建目录中生成目标文件 `.obj.c` 或 `.obj.S`，具体取决于处理的源文件。

> 2. 通过 C 预处理器运行链接器脚本模板。 模板位于 `components/ulp/ld` 目录中。

> 3. 将对象文件链接到一个 ELF 输出文件中， 即 `ulp_app_name.elf`。在此阶段生成的映射文件 `ulp_app_name.map` 可用于调试。

> 4. 将 ELF 文件的内容转储到一个二进制文件中， 即 `ulp_app_name.bin`。此二进制文件接下来可以嵌入到应用程序中。

> 5. 使用 `riscv32-esp-elf-nm` 在 ELF 文件中 生成全局符号列表， 即 `ulp_app_name.sym`。

> 6. 创建一个 LD 导出脚本和一个头文件， 即 `ulp_app_name.ld` 和 `ulp_app_name.h`，并在其中包含 `ulp_app_name.sym` 中的符号。此步骤可以通过 `esp32ulp_mapgen.py` 实现。

> 7. 将生成的二进制文件添加到要嵌入到应用程序中的二进制文件列表。

## 访问 ULP LP 内核程序变量

在主程序中可以使用在 ULP LP 内核程序中定义的全局符号。

例如，ULP LP 内核程序定义了一个变量 `measurement_count`，用来表示程序从深度睡眠中唤醒芯片前所需的 GPIO 测量次数。

    volatile int measurement_count;

    int some_function()
    {
        //读取测量次数以便后续使用。
        int temp = measurement_count;

        ...do something.
    }

主程序可以访问 ULP LP 内核程序全局变量，这是因为构建系统生成了 `${ULP_APP_NAME}.h` 和 `${ULP_APP_NAME}.ld` 文件，文件中定义了 ULP LP 内核程序中现有的的全局符号。在 ULP LP 内核程序中定义的每个全局符号都包含在这两个文件中，并具有前缀 `ulp_`。

头文件中包含符号的声明：

    extern uint32_t ulp_measurement_count;

注意，所有的符号（变量、函数）都被声明为 `uint32_t` 类型。数组被声明为 `uint32_t [SIZE]` 类型。函数需要先获取符号地址，再转换为适当的类型。

要从主程序访问 ULP LP 内核程序变量，应使用 `include` 语句将生成的头文件包含在主程序中，这样就可以像访问常规变量一样访问 ULP LP 内核程序变量。

    #include "ulp_app_name.h"

    void init_ulp_vars() {
        ulp_measurement_count = 64;
    }

备注

- LP 内核程序全局变量存储在二进制文件的 `.bss` 或者 `.data` 部分。这些部分在加载和执行 LP 内核二进制文件时被初始化。在首次运行 LP 内核之前，从 HP-Core 主程序访问这些变量可能会导致未定义行为。

- 默认以 `ulp_` 作为前缀。你可以在使用 `ulp_embed_binary` 时指定前缀，以避免多个 ULP 程序之间的命名冲突。

## 启动 ULP LP 内核程序

要运行 ULP LP 内核程序，主应用程序需要先使用 `ulp_lp_core_load_binary()` 函数将 ULP 程序加载到 RTC 内存中，然后使用 `ulp_lp_core_run()` 函数进行启动。

每个 ULP LP 内核程序以二进制 blob 的形式嵌入到 ESP-IDF 应用程序中。应用程序可以按照如下方式引用和加载该 blob（假设 ULP_APP_NAME 被定义为 `ulp_app_name`）：

    extern const uint8_t bin_start[] asm("_binary_ulp_app_name_bin_start");
    extern const uint8_t bin_end[]   asm("_binary_ulp_app_name_bin_end");

    void start_ulp_program() {
        ESP_ERROR_CHECK( ulp_lp_core_load_binary( bin_start,
            (bin_end - bin_start)) );
    }

将程序加载到 LP 内存后，就可以调用 `ulp_lp_core_run()` 配置和启动应用程序：

    ulp_lp_core_cfg_t cfg = {
        .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER, // LP 内核会定期被 LP 定时器唤醒
        .lp_timer_sleep_duration_us = 10000,
    };

    ESP_ERROR_CHECK( ulp_lp_core_run(&cfg) );

## ULP LP 内核程序流程

ULP LP 内核协处理器如何启动取决于 `ulp_lp_core_cfg_t` 中选择的唤醒源。最常见的用例是 ULP 定期唤醒，在进行一些测量后唤醒主 CPU，或者再次进入睡眠状态。

ULP 有以下唤醒源：

  - `ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU` - LP 内核可以被 HP CPU 唤醒。

  - `ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER` - LP 内核可以被 LP 定时器唤醒。

  - `ULP_LP_CORE_WAKEUP_SOURCE_ETM` - LP 内核可以被 ETM 事件唤醒。

  - `ULP_LP_CORE_WAKEUP_SOURCE_LP_IO` - 当 LP IO 电平变化时，LP 内核会被唤醒。

  - `ULP_LP_CORE_WAKEUP_SOURCE_LP_UART` - 当 LP UART 在不同模式下接收到唤醒数据时，LP 内核会被唤醒。

ULP 被唤醒时会经历以下步骤：

1. 初始化系统功能，如中断

2. 调用用户代码 `main()`

3. 从 `main()` 返回

4. 如果指定了 `lp_timer_sleep_duration_us`，则配置下一个唤醒闹钟

5. 调用 `ulp_lp_core_halt()`

## ULP LP 内核支持的外设

为了增强 ULP LP 内核协处理器的功能，它可以访问在低功耗电源域运行的外设。ULP LP 内核协处理器可以在主 CPU 处于睡眠模式时与这些外设进行交互，并在达到唤醒条件时唤醒主 CPU。以下为支持的外设：

- LP IO

- LP I2C

- LP UART

## ULP LP 内核中断

配置 LP 内核协处理器，可以处理各种类型的中断，例如 LP IO 低/高电平中断或是 LP 定时器中断。只需重写 IDF 提供的任何一个弱处理函数，就可以注册一个中断处理程序。所有处理程序可见 ulp_lp_core_interrupts.h 。有关特定目标可使用的中断的详细信息，请参阅 ESP32-C6 技术参考手册 [PDF]。

例如，要重写 LP IO 中断的处理程序，可以在 ULP LP 内核代码中定义以下函数：

    void LP_CORE_ISR_ATTR ulp_lp_core_lp_io_intr_handler(void)
    {
        // 处理中断，清除中断源
    }

`LP_CORE_ISR_ATTR` 宏用于定义中断处理函数，可确保调用中断处理程序时妥善保存并恢复寄存器。

除了为需要处理的中断源配置相关的中断寄存器外，还要调用 `ulp_lp_core_intr_enable()` 函数，在 LP 内核中断控制器中使能全局中断。

## ULP LP 内核时钟配置

ULP LP 内核的时钟源来自系统时钟 `LP_FAST_CLK`，详情请参见 技术参考手册 > `复位和时钟`。

## 调试 ULP LP 内核应用程序

在编程 LP 内核时，有时很难弄清楚程序未按预期运行的原因。请参考以下策略，调试 LP 内核程序：

- 使用 LP UART 打印：LP 内核可以访问 LP UART 外设，在主 CPU 处于睡眠状态时独立打印信息。有关使用此驱动程序的示例，请参阅 system/ulp/lp_core/lp_uart/lp_uart_print。

- 通过 CONFIG_ULP_HP_UART_CONSOLE_PRINT，将 `lp_core_printf()` 路由到 HP-Core 控制台 UART，可以轻松地将 LP 内核信息打印到已经连接的 HP-Core 控制台 UART。此方法的缺点是需要主 CPU 处于唤醒状态，并且由于 LP 内核与 HP 内未同步，输出可能会交错。

- 通过共享变量共享程序状态：如 访问 ULP LP 内核程序变量 所述，主 CPU 和 ULP 内核都可以轻松访问 RTC 内存中的全局变量。若想了解 ULP 内核的运行状态，可以将状态信息从 ULP 写入变量中，并通过主 CPU 读取信息。这种方法的缺点在于它需要主 CPU 一直处于唤醒状态，而这通常很难实现。另外，若主 CPU 一直处于唤醒状态，可能会掩盖某些问题，因为部分问题只会在特定电源域断电时发生。

- 紧急处理程序：当检测到异常时，LP 内核的紧急处理程序会把 LP 内核寄存器的状态通过 LP UART 发送出去。将 CONFIG_ULP_PANIC_OUTPUT_ENABLE 选项设置为 `y`，可以启用紧急处理程序。禁用此选项将减少 LP 内核应用程序的 LP-RAM 使用量。若想从紧急转储中解析栈回溯，可以使用 `idf.py monitor`。

警告

如果在单个项目中使用多个 ULP 应用程序，栈回溯解码可能无法正常工作。此时建议直接使用 esp-idf-monitor 工具，并指定正确的 ULP ELF 文件：

    python -m esp_idf_monitor --toolchain-prefix riscv32-esp-elf- --target esp32c6 --decode-panic backtrace PATH_TO_ULP_ELF_FILE

## 调试 ULP LP 内核应用程序：使用 GDB 和 OpenOCD

与调试 HP 内核类似，也可以用 GDB 和 OpenOCD 来调试 LP 内核上的代码，但要注意其特殊之处和限制条件。

### 调试会话

使用支持 LP 内核调试的特殊配置文件来运行 OpenOCD，然后用特殊的 `gdbinit` 文件运行 GDB。

    openocd -f board/esp32c6-lpcore-builtin.cfg
    riscv32-esp-elf-gdb -x gdbinit <path to main program ELF>

以下是带有内联注释的 `gdbinit` 文件内容，详细信息请参考下一章节。

    # 连接到目标
    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 内核调试特性

1. 为了方便调试，请在 ULP 应用的 `CMakeLists.txt` 文件中添加 `-O0` 编译选项。具体操作步骤请参见 system/ulp/lp_core/debugging/。

2. LP 内核支持的硬件异常类型有限，例如，写入地址 0x0 不会像在 HP 内核上一样造成系统崩溃。启用 LP 内核应用程序的未定义行为检测器 (ubsan) 可以捕捉一些错误，从而在一定程度上弥补这一限制。但请注意，这将显著增加代码量，可能会导致应用程序超出 RTC RAM 的容量限制。要启用 ubsan，请在 `CMakeLists.txt` 文件中添加 `-fsanitize=undefined -fno-sanitize=shift-base` 编译选项。具体操作步骤请参见 system/ulp/lp_core/debugging/。

3. 为了调试运行在 LP 内核上的程序，需要先将调试信息和符号加载到 GDB 中。这可以通过 GDB 命令行或在 `gdbinit` 文件中完成。具体操作步骤请参见上文。

4. LP 内核应用程序会在启时会加载到 RAM 中，在此之前设置的所有软件断点都会被覆盖。设置 LP 内核应用断点的最佳时机是在 LP 内核程序运行至 `main` 函数之时。

5. 使用 IDE 时，可能无法配置上述 `gdbinit` 文件中的断点操作或命令。因此，请在调试会话开始前预设并禁用所有断点，只保留 `main` 函数处的断点。当程序在 `main` 处停止时，手动启用其余断点并恢复执行。

### 限制

1. 调试场景有限制：目前，当 HP 内核或 LP 内核进入睡眠模式时，将无法调试。

2. 调试内核时，OpenOCD 不支持 FreeRTOS，因此无法看到系统中正在运行的任务，但会有几个线程代表 HP 和 LP 内核：

    (gdb) info thread
        Id   Target Id                                                          Frame
        1    Thread 1 "esp32c6.cpu0" (Name: esp32c6.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 "esp32c6.cpu1" (Name: esp32c6.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

1. 在 GDB 中设置硬件断点时，这些断点会同时应用到两个内核上，因此可用的硬件断点数量受 LP 内核支持数量（ESP32-C6 有 4 个）所限。

2. OpenOCD 的 flash 支持被禁用。LP 内核应用程序完全在 RAM 中运行，且 GDB 可以为其使用软件断点，因而该限制无关紧要。但若想在 HP 内核运行的代码中调用的 flash 函数（例如 app_main）上设置断点，则需要通过 `hb` 和 `thb` GDB 命令显式请求设置硬件断点。

3. 由于主程序和 ULP 程序被链接为独立的二进制文件，它们可能会拥有相同名称的全局符号（如函数或变量）。若通过函数名称设置断点，则 GDB 将为所有同名函数设置断点。在调试 LP 内核时，OpenOCD 不支持 flash，因此如果上述函数位于 flash 中，可能会引发问题。此时建议通过源代码行号或函数的内存地址来设置断点。

## 应用示例

- system/ulp/lp_core/gpio 展示了 ULP LP 内核协处理器在主 CPU 深度睡眠时轮询 GPIO。

- system/ulp/lp_core/lp_i2c 展示了 ULP LP 内核协处理器在主 CPU 深度睡眠时读取外部 I2C 环境光传感器 (BH1750)，并在达到阈值时唤醒主 CPU。

- system/ulp/lp_core/lp_uart/lp_uart_echo 展示了低功耗内核上运行的 LP UART 驱动程序如何读取并回显写入串行控制台的数据。

- system/ulp/lp_core/lp_uart/lp_uart_print 展示了如何在低功耗内核上使用串口打印功能。

- system/ulp/lp_core/lp_uart/lp_uart_char_seq_wakeup 展示了如何使用 LP UART 特定字符序列唤醒模式触发唤醒。

- system/ulp/lp_core/interrupt 展示了如何在 LP 内核上注册中断处理程序，接收由主 CPU 触发的中断。

- system/ulp/lp_core/gpio_intr_pulse_counter 展示了如何在主 CPU 处于 Deep-sleep 模式时，使用 GPIO 中断为脉冲计数。

- system/ulp/lp_core/build_system/ 演示了如何为 ULP 应用程序添加自定义的 `CMakeLists.txt` 文件。

- system/ulp/lp_core/debugging 演示了如何使用 GDB 和 OpenOCD 来调试运行在 LP 内核上的代码。

## API 参考

### 主 CPU API 参考

#### Header File

- components/ulp/lp_core/include/ulp_lp_core.h

- This header file can be included with:

  >     #include "ulp_lp_core.h"

- This header file is a part of the API provided by the `ulp` component. To declare that your component depends on `ulp`, add the following to your CMakeLists.txt:

  >     REQUIRES ulp

  > or

  >     PRIV_REQUIRES ulp

#### Functions

esp_err_t ulp_lp_core_run(ulp_lp_core_cfg_t *cfg)

  Configure the ULP and run the program loaded into RTC memory.

  返回:

    ESP_OK on success

esp_err_t ulp_lp_core_load_binary(const uint8_t *program_binary, size_t program_size_bytes)

  Load the program binary into RTC memory.

  参数:

    - program_binary -- pointer to program binary

    - program_size_bytes -- size of the program binary

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_SIZE if program_size_bytes is more than KiB

void ulp_lp_core_stop(void)

  Puts the ulp to sleep and disables all wakeup sources. To restart the ULP call ulp_lp_core_run() with the desired wake up trigger.

void ulp_lp_core_sw_intr_trigger(void)

  Trigger a SW interrupt to the LP CPU from the PMU.

  备注

  This is the same SW trigger that is used to wake up the LP CPU

#### Structures

struct ulp_lp_core_cfg_t

  ULP LP core init parameters.

  Public Members

  uint32_t wakeup_source

    Wakeup source flags

  uint32_t lp_timer_sleep_duration_us

    Sleep duration when ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER is specified. Measurement unit: us

#### Macros

ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU

ULP_LP_CORE_WAKEUP_SOURCE_LP_UART

ULP_LP_CORE_WAKEUP_SOURCE_LP_IO

ULP_LP_CORE_WAKEUP_SOURCE_ETM

ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER

ULP_LP_CORE_WAKEUP_SOURCE_LP_VAD

#### Header File

- components/ulp/lp_core/include/lp_core_i2c.h

- This header file can be included with:

  >     #include "lp_core_i2c.h"

- This header file is a part of the API provided by the `ulp` component. To declare that your component depends on `ulp`, add the following to your CMakeLists.txt:

  >     REQUIRES ulp

  > or

  >     PRIV_REQUIRES ulp

#### Functions

esp_err_t lp_core_i2c_master_init(i2c_port_t lp_i2c_num, const lp_core_i2c_cfg_t *cfg)

  Initialize and configure the LP I2C for use by the LP core Currently LP I2C can only be used in master mode.

  备注

  The internal pull-up resistors for SDA and SCL pins, if enabled, will provide a weak pull-up value of about 30-50 kOhm. Users are advised to enable external pull-ups for better performance at higher SCL frequencies.

  参数:

    - lp_i2c_num -- LP I2C port number

    - cfg -- Configuration parameters

  返回:

    esp_err_t ESP_OK when successful

#### Structures

struct lp_core_i2c_pin_cfg_t

  LP Core I2C pin config parameters.

  Public Members

  gpio_num_t sda_io_num

    GPIO pin for SDA signal. Only GPIO#6 can be used as the SDA pin.

  gpio_num_t scl_io_num

    GPIO pin for SCL signal. Only GPIO#7 can be used as the SCL pin.

  bool sda_pullup_en

    SDA line enable internal pullup. Can be configured if external pullup is not used.

  bool scl_pullup_en

    SCL line enable internal pullup. Can be configured if external pullup is not used.

struct lp_core_i2c_timing_cfg_t

  LP Core I2C timing config parameters.

  Public Members

  uint32_t clk_speed_hz

    LP I2C clock speed for master mode

struct lp_core_i2c_cfg_t

  LP Core I2C config parameters.

  Public Members

  lp_core_i2c_pin_cfg_t i2c_pin_cfg

    LP I2C pin configuration

  lp_core_i2c_timing_cfg_t i2c_timing_cfg

    LP I2C timing configuration

  soc_periph_lp_i2c_clk_src_t i2c_src_clk

    LP I2C source clock type

#### Macros

LP_I2C_SCL_IO

LP_I2C_SDA_IO

LP_I2C_DEFAULT_GPIO_CONFIG()

LP_I2C_FAST_MODE_CONFIG()

LP_I2C_STANDARD_MODE_CONFIG()

LP_I2C_DEFAULT_SRC_CLK()

LP_CORE_I2C_DEFAULT_CONFIG()

#### Header File

- components/ulp/lp_core/include/lp_core_uart.h

- This header file can be included with:

  >     #include "lp_core_uart.h"

- This header file is a part of the API provided by the `ulp` component. To declare that your component depends on `ulp`, add the following to your CMakeLists.txt:

  >     REQUIRES ulp

  > or

  >     PRIV_REQUIRES ulp

#### Functions

esp_err_t lp_core_uart_init(const lp_core_uart_cfg_t *cfg)

  Initialize and configure the LP UART to be used from the LP core.

  备注

  The LP UART initialization must be called from the main core (HP CPU)

  参数:

    cfg -- Configuration parameters

  返回:

    esp_err_t ESP_OK when successful

#### Structures

struct lp_core_uart_pin_cfg_t

  LP UART IO pins configuration.

  Public Members

  gpio_num_t tx_io_num

    GPIO pin for UART Tx signal. Only GPIO#5 can be used as the UART Tx pin

  gpio_num_t rx_io_num

    GPIO pin for UART Rx signal. Only GPIO#4 can be used as the UART Rx pin

  gpio_num_t rts_io_num

    GPIO pin for UART RTS signal. Only GPIO#2 can be used as the UART RTS pin

  gpio_num_t cts_io_num

    GPIO pin for UART CTS signal. Only GPIO#3 can be used as the UART CTS pin

struct lp_core_uart_proto_cfg_t

  LP UART protocol configuration.

  Public Members

  int baud_rate

    LP UART baud rate

  uart_word_length_t data_bits

    LP UART byte size

  uart_parity_t parity

    LP UART parity mode

  uart_stop_bits_t stop_bits

    LP UART stop bits

  uart_hw_flowcontrol_t flow_ctrl

    LP UART HW flow control mode (cts/rts)

  uint8_t rx_flow_ctrl_thresh

    LP UART HW RTS threshold

struct lp_core_uart_cfg_t

  LP UART configuration parameters.

  Public Members

  lp_core_uart_pin_cfg_t uart_pin_cfg

    LP UART pin configuration

  lp_core_uart_proto_cfg_t uart_proto_cfg

    LP UART protocol configuration

  lp_uart_sclk_t lp_uart_source_clk

    LP UART source clock selection

#### Macros

LP_UART_DEFAULT_TX_GPIO_NUM

  Default LP_IO Mux pins for LP UART

LP_UART_DEFAULT_RX_GPIO_NUM

LP_UART_DEFAULT_RTS_GPIO_NUM

LP_UART_DEFAULT_CTS_GPIO_NUM

LP_UART_DEFAULT_GPIO_CONFIG()

LP_UART_DEFAULT_PROTO_CONFIG()

LP_UART_DEFAULT_CLOCK_CONFIG()

LP_CORE_UART_DEFAULT_CONFIG()

#### Header File

- components/ulp/lp_core/include/lp_core_etm.h

- This header file can be included with:

  >     #include "lp_core_etm.h"

- This header file is a part of the API provided by the `ulp` component. To declare that your component depends on `ulp`, add the following to your CMakeLists.txt:

  >     REQUIRES ulp

  > or

  >     PRIV_REQUIRES ulp

#### Functions

esp_err_t lp_core_new_etm_event(const lp_core_etm_event_config_t *config, esp_etm_event_handle_t *out_event)

  Create a ETM event for LP-Core.

  备注

  The created ETM event object can be deleted later by calling `esp_etm_del_event`

  参数:

    - config -- [in] LP-Core ETM event configuration

    - out_event -- [out] Returned ETM event handle

  返回:

    - ESP_OK: Get ETM event successfully

    - ESP_ERR_INVALID_ARG: Get ETM event failed because of invalid argument

    - ESP_FAIL: Get ETM event failed because of other error

esp_err_t lp_core_new_etm_task(const lp_core_etm_task_config_t *config, esp_etm_task_handle_t *out_task)

  Create a ETM task for LP-Core.

  备注

  The created ETM task object can be deleted later by calling `esp_etm_del_task`

  参数:

    - config -- [in] LP-Core ETM task configuration

    - out_task -- [out] Returned ETM task handle

  返回:

    - ESP_OK: Get ETM task successfully

    - ESP_ERR_INVALID_ARG: Get ETM task failed because of invalid argument

    - ESP_FAIL: Get ETM task failed because of other error

#### Structures

struct lp_core_etm_event_config_t

  LP-Core ETM event configuration.

  Public Members

  lp_core_etm_event_type_t event_type

    LP-Core ETM event type

struct lp_core_etm_task_config_t

  LP-Core ETM task configuration.

  Public Members

  lp_core_etm_task_type_t task_type

    LP-Core ETM task type

#### Header File

- components/hal/include/hal/lp_core_types.h

- This header file can be included with:

  >     #include "hal/lp_core_types.h"

#### Enumerations

enum lp_core_etm_event_type_t

  LP-Core event types enum.

  Values:

  enumerator LP_CORE_EVENT_ERR_INTR

    Exception triggered on LP-Core

  enumerator LP_CORE_EVENT_START_INTR

    LP-Core clock has been turned on

  enumerator LP_CORE_EVENT_MAX

    Maximum number of LP-Core events

enum lp_core_etm_task_type_t

  LP-Core task types enum.

  Values:

  enumerator LP_CORE_TASK_WAKEUP_CPU

    LP-Core wake-up task

  enumerator LP_CORE_TASK_MAX

    Maximum number of LP-Core tasks

### LP 内核 API 参考

#### Header File

- components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h

#### Functions

void ulp_lp_core_update_wakeup_cause(void)

  Traverse all possible wake-up sources and update the wake-up cause so that ulp_lp_core_get_wakeup_cause can obtain the bitmap of the wake-up reasons.

  备注

  Do not call it from user ULP programs because it will clear the wake-up cause bits which were set at ULP startup in lp_core_startup().

uint32_t ulp_lp_core_get_wakeup_cause(void)

  Get the wakeup source which caused LP_CPU to wakeup from sleep.

  返回:

    Wakeup cause in bit map, for the meaning of each bit, refer to the definition of wakeup source in lp_core_ll.h

void ulp_lp_core_wakeup_main_processor(void)

  Wakeup main CPU from sleep or deep sleep.

  This raises a software interrupt signal, if the main CPU has configured the ULP as a wakeup source calling this function will make the main CPU to exit from sleep or deep sleep.

static inline uint32_t ulp_lp_core_get_cpu_cycles(void)

  Retrieves the current number of CPU cycles.

  返回:

    The current CPU cycle count.

void ulp_lp_core_delay_us(uint32_t us)

  Makes the co-processor busy-wait for a certain number of microseconds.

  备注

  The maximum supported delay depends on the LP core clock source and frequency. For values above the limits below, the computed delay may overflow and the result is undefined.

  - LP core @ 16 MHz (RC_FAST / default): us must be <= 134217727 (about 134.2 s)

  - LP core @ 40 MHz (XTAL 40 MHz): us must be <= 53687091 (about 53.7 s)

  - LP core @ 48 MHz (XTAL 48 MHz): us must be <= 44739242 (about 44.7 s)

  参数:

    us -- Number of microseconds to busy-wait for

void ulp_lp_core_delay_cycles(uint32_t cycles)

  Makes the co-processor busy-wait for a certain number of cycles.

  For reference, this corresponds approximately to:

  - LP core @ 16 MHz (RC_FAST / default): 0x7FFFFFFF cycles ≈ 134.2 s

  - LP core @ 40 MHz (XTAL 40 MHz): 0x7FFFFFFF cycles ≈ 53.7 s

  - LP core @ 48 MHz (XTAL 48 MHz): 0x7FFFFFFF cycles ≈ 44.7 s

  备注

  The maximum supported delay is 0x7FFFFFFF cycles. For larger values, the behavior is undefined. Split longer delays into smaller chunks if needed.

  参数:

    cycles -- Number of cycles to busy-wait for

void ulp_lp_core_lp_uart_reset_wakeup_en(void)

  Reset LP CORE uart wakeup enable.

void ulp_lp_core_halt(void)

  Finishes the ULP program and powers down the ULP until next wakeup.

  备注

  This function does not return. After called it will fully reset the ULP.

  备注

  The program will automatically call this function when returning from main().

  备注

  To stop the ULP from waking up, call ulp_lp_core_lp_timer_disable() before halting.

void ulp_lp_core_stop_lp_core(void)

  The LP core puts itself to sleep and disables all wakeup sources.

void ulp_lp_core_abort(void)

  Abort LP core operation.

void ulp_lp_core_sw_intr_enable(bool enable)

  Enable the SW triggered interrupt from the PMU.

  备注

  This is the same SW trigger interrupt that is used to wake up the LP CPU

  参数:

    enable -- true to enable, false to disable

void ulp_lp_core_sw_intr_clear(void)

  Clear the interrupt status for the SW triggered interrupt from the PMU.

void ulp_lp_core_lp_timer_intr_enable(bool enable)

  Enable the LP Timer interrupt.

void ulp_lp_core_lp_timer_intr_clear(void)

  Clear the interrupt status for the LP Timer interrupt.

void ulp_lp_core_wait_for_intr(void)

  Puts the CPU into a wait state until an interrupt is triggered.

  备注

  The CPU will draw less power when in this state compared to actively running

#### Header File

- components/ulp/lp_core/lp_core/include/ulp_lp_core_gpio.h

#### Functions

static inline void ulp_lp_core_gpio_init(lp_io_num_t lp_io_num)

  Initialize a rtcio pin.

  备注

  If IO is used in LP application, `rtc_gpio_init` must be called at least once for the using IO before loading LP core firmware in HP Code.

  参数:

    lp_io_num -- The rtc io pin to initialize

static inline void ulp_lp_core_gpio_output_enable(lp_io_num_t lp_io_num)

  Enable output.

  参数:

    lp_io_num -- The rtc io pin to enable output for

static inline void ulp_lp_core_gpio_output_disable(lp_io_num_t lp_io_num)

  Disable output.

  参数:

    lp_io_num -- The rtc io pin to disable output for

static inline void ulp_lp_core_gpio_input_enable(lp_io_num_t lp_io_num)

  Enable input.

  参数:

    lp_io_num -- The rtc io pin to enable input for

static inline void ulp_lp_core_gpio_input_disable(lp_io_num_t lp_io_num)

  Disable input.

  参数:

    lp_io_num -- The rtc io pin to disable input for

static inline void ulp_lp_core_gpio_set_level(lp_io_num_t lp_io_num, uint8_t level)

  Set rtcio output level.

  参数:

    - lp_io_num -- The rtc io pin to set the output level for

    - level -- 0: output low; 1: output high.

static inline uint32_t ulp_lp_core_gpio_get_level(lp_io_num_t lp_io_num)

  Get rtcio output level.

  参数:

    lp_io_num -- The rtc io pin to get the output level for

static inline void ulp_lp_core_gpio_set_output_mode(lp_io_num_t lp_io_num, rtcio_ll_out_mode_t mode)

  Set rtcio output mode.

  参数:

    - lp_io_num -- The rtc io pin to set the output mode for

    - mode -- RTCIO_LL_OUTPUT_NORMAL: normal, RTCIO_LL_OUTPUT_OD: open drain

static inline void ulp_lp_core_gpio_pullup_enable(lp_io_num_t lp_io_num)

  Enable internal pull-up resistor.

  参数:

    lp_io_num -- The rtc io pin to enable pull-up for

static inline void ulp_lp_core_gpio_pullup_disable(lp_io_num_t lp_io_num)

  Disable internal pull-up resistor.

  参数:

    lp_io_num -- The rtc io pin to disable pull-up for

static inline void ulp_lp_core_gpio_pulldown_enable(lp_io_num_t lp_io_num)

  Enable internal pull-down resistor.

  参数:

    lp_io_num -- The rtc io pin to enable pull-down for

static inline void ulp_lp_core_gpio_pulldown_disable(lp_io_num_t lp_io_num)

  Disable internal pull-down resistor.

  参数:

    lp_io_num -- The rtc io pin to disable pull-down for

static inline void ulp_lp_core_gpio_intr_enable(lp_io_num_t lp_io_num, gpio_int_type_t intr_type)

  Enable interrupt for lp io pin.

  参数:

    - lp_io_num -- The lp io pin to enable interrupt for

    - intr_type -- The interrupt type to enable

static inline void ulp_lp_core_gpio_clear_intr_status(void)

  Clear interrupt status for all lp io.

static inline void ulp_lp_core_gpio_wakeup_enable(lp_io_num_t lp_io_num, gpio_int_type_t intr_type)

  Enable wake up for lp io pin.

  参数:

    - lp_io_num -- The lp io pin to enable the wake up for

    - intr_type -- The interrupt type to enable wake up for

static inline void ulp_lp_core_gpio_wakeup_disable(lp_io_num_t lp_io_num)

  Disable wake up for lp io pin.

  参数:

    lp_io_num -- The lp io pin to disable the wake up for

#### Macros

RTCIO_OUTPUT_NORMAL

RTCIO_OUTPUT_OD

#### Enumerations

enum lp_io_num_t

  Values:

  enumerator LP_IO_NUM_0

    GPIO0, input and output

  enumerator LP_IO_NUM_1

    GPIO1, input and output

  enumerator LP_IO_NUM_2

    GPIO2, input and output

  enumerator LP_IO_NUM_3

    GPIO3, input and output

  enumerator LP_IO_NUM_4

    GPIO4, input and output

  enumerator LP_IO_NUM_5

    GPIO5, input and output

  enumerator LP_IO_NUM_6

    GPIO6, input and output

  enumerator LP_IO_NUM_7

    GPIO7, input and output

#### Header File

- components/ulp/lp_core/lp_core/include/ulp_lp_core_i2c.h

#### Functions

esp_err_t lp_core_i2c_master_read_from_device(i2c_port_t lp_i2c_num, uint16_t device_addr, uint8_t *data_rd, size_t size, int32_t ticks_to_wait)

  Read from I2C device.

  备注

  The LP I2C must have been initialized from the HP core using the lp_core_i2c_master_init() API before invoking this API.

  备注

  the LP I2C does not support 10-bit I2C device addresses.

  备注

  the LP I2C port number is ignored at the moment.

  参数:

    - lp_i2c_num -- LP I2C port number

    - device_addr -- I2C device address (7-bit)

    - data_rd -- Buffer to hold data to be read

    - size -- Size of data to be read in bytes

    - ticks_to_wait -- Operation timeout in CPU cycles. Set to -1 to wait forever.

  返回:

    esp_err_t ESP_OK when successful

esp_err_t lp_core_i2c_master_write_to_device(i2c_port_t lp_i2c_num, uint16_t device_addr, const uint8_t *data_wr, size_t size, int32_t ticks_to_wait)

  Write to I2C device.

  备注

  The LP I2C must have been initialized from the HP core using the lp_core_i2c_master_init() API before invoking this API.

  备注

  the LP I2C does not support 10-bit I2C device addresses.

  备注

  the LP I2C port number is ignored at the moment.

  参数:

    - lp_i2c_num -- LP I2C port number

    - device_addr -- I2C device address (7-bit)

    - data_wr -- Buffer which holds the data to be written

    - size -- Size of data to be written in bytes

    - ticks_to_wait -- Operation timeout in CPU cycles. Set to -1 to wait forever.

  返回:

    esp_err_t ESP_OK when successful

esp_err_t lp_core_i2c_master_write_read_device(i2c_port_t lp_i2c_num, uint16_t device_addr, const uint8_t *data_wr, size_t write_size, uint8_t *data_rd, size_t read_size, int32_t ticks_to_wait)

  Write to and then read from an I2C device in a single transaction.

  备注

  The LP I2C must have been initialized from the HP core using the lp_core_i2c_master_init() API before invoking this API.

  备注

  the LP I2C does not support 10-bit I2C device addresses.

  备注

  the LP I2C port number is ignored at the moment.

  参数:

    - lp_i2c_num -- LP I2C port number

    - device_addr -- I2C device address (7-bit)

    - data_wr -- Buffer which holds the data to be written

    - write_size -- Size of data to be written in bytes

    - data_rd -- Buffer to hold data to be read

    - read_size -- Size of data to be read in bytes

    - ticks_to_wait -- Operation timeout in CPU cycles. Set to -1 to wait forever.

  返回:

    esp_err_t ESP_OK when successful

void lp_core_i2c_master_set_ack_check_en(i2c_port_t lp_i2c_num, bool ack_check_en)

  Enable or disable ACK checking by the LP_I2C controller during write operations.

  When ACK checking is enabled, the hardware will check the ACK/NACK level received during write operations against the expected ACK/NACK level. If the received ACK/NACK level does not match the expected ACK/NACK level then the hardware will generate the I2C_NACK_INT and a STOP condition will be generated to stop the data transfer.

  备注

  ACK checking is enabled by default

  备注

  the LP I2C port number is ignored at the moment.

  参数:

    - lp_i2c_num -- LP I2C port number

    - ack_check_en -- true: enable ACK check false: disable ACK check

static inline void ulp_lp_core_lp_i2c_intr_enable(i2c_port_t lp_i2c_num, uint32_t mask)

  Enable LP I2C master-related interrupts at the peripheral.

  Enables the same interrupt sources used by the LP I2C master driver (see I2C_LL_MASTER_EVENT_INTR).

  参数:

    - lp_i2c_num -- Must be the LP I2C port (e.g. LP_I2C_NUM_0), not an HP I2C port.

    - mask -- Interrupt mask needs to be enabled

static inline void ulp_lp_core_lp_i2c_intr_disable(i2c_port_t lp_i2c_num, uint32_t mask)

  Disable LP I2C master-related interrupts at the peripheral.

  参数:

    - lp_i2c_num -- Must be the LP I2C port (e.g. LP_I2C_NUM_0), not an HP I2C port.

    - mask -- Interrupt mask needs to be disabled

#### Header File

- components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h

#### Functions

int lp_core_uart_tx_chars(uart_port_t lp_uart_num, const void *src, size_t size)

  Send data to the LP UART port if there is space available in the Tx FIFO.

  This function will not wait for enough space in the Tx FIFO to be available. It will just fill the available Tx FIFO slots and return when the FIFO is full. If there are no empty slots in the Tx FIFO, this function will not write any data.

  参数:

    - lp_uart_num -- LP UART port number

    - src -- data buffer address

    - size -- data length to send

  返回:

    - (-1) Error

    - OTHERS (>=0) The number of bytes pushed to the Tx FIFO

esp_err_t lp_core_uart_write_bytes(uart_port_t lp_uart_num, const void *src, size_t size, int32_t timeout)

  Write data to the LP UART port.

  This function will write data to the Tx FIFO. If a timeout value is configured, this function will timeout once the number of CPU cycles expire.

  参数:

    - lp_uart_num -- LP UART port number

    - src -- data buffer address

    - size -- data length to send

    - timeout -- Operation timeout in CPU cycles. Set to -1 to wait forever.

  返回:

    esp_err_t ESP_OK when successful

int lp_core_uart_read_bytes(uart_port_t lp_uart_num, void *buf, size_t size, int32_t timeout)

  Read data from the LP UART port.

  This function will read data from the Rx FIFO. If a timeout value is configured, then this function will timeout once the number of CPU cycles expire.

  参数:

    - lp_uart_num -- LP UART port number

    - buf -- data buffer address

    - size -- data length to send

    - timeout -- Operation timeout in CPU cycles. Set to -1 to wait forever.

  返回:

    - (-1) Error

    - OTHERS (>=0) The number of bytes read from the Rx FIFO

void lp_core_uart_tx_flush(uart_port_t lp_uart_num)

  Flush LP UART Tx FIFO.

  This function is automatically called before the LP core powers down once the main() function returns. It can also be called manually in the application to flush the Tx FIFO.

  参数:

    lp_uart_num -- LP UART port number

void lp_core_uart_reset_wakeup_en(void)

  Reset LP UART wakeup enable.

  This function is used to reset the LP UART wakeup enable. It will flush the UART buffer and reset the wakeup enable.

  备注

  Kept in lp_core_uart.c (not lp_core_utils.c) to avoid changing codegen of ulp_lp_core_delay_us() in the same compilation unit, which breaks delay_us calibration.

#### Header File

- components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h

#### Functions

void lp_core_printf(const char *format, ...)

  Print from the LP core.

  备注

  This function uses the LP UART peripheral to enable prints.The LP UART must be initialized with lp_core_uart_init() before using this function.

  备注

  This function is not a standard printf function and may not support all format specifiers or special characters.

  参数:

    - format -- string to be printed

    - ... -- variable argument list

void lp_core_print_char(char c)

  Print a single character from the LP core.

  参数:

    c -- character to be printed

void lp_core_print_str(const char *str)

  Print a null-terminated string from the LP core.

  参数:

    str -- null-terminated string to be printed

void lp_core_print_hex(int h)

  Print a hex value from the LP core.

  备注

  Does not print '0x', only the digits (will always print 8 digits)

  参数:

    h -- hex value to be printed

void lp_core_print_dec_two_digits(int d)

  Print a two digit integer from the LP-Core.

  参数:

    d -- integer to be printed

#### Header File

- components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h

#### Functions

void ulp_lp_core_intr_enable(void)

  Enables interrupts globally for the low power core.

  Available interrupt handlers for the low power core are as follows:

  ulp_lp_core_lp_io_intr_handler(void); ulp_lp_core_lp_i2c_intr_handler(void); ulp_lp_core_lp_uart_intr_handler(void); ulp_lp_core_lp_timer_intr_handler(void); ulp_lp_core_lp_pmu_intr_handler(void); ulp_lp_core_lp_spi_intr_handler(void); ulp_lp_core_trng_intr_handler(void); ulp_lp_core_lp_adc_intr_handler(void); ulp_lp_core_lp_touch_intr_handler(void); ulp_lp_core_tsens_intr_handler(void); ulp_lp_core_efuse_intr_handler(void); ulp_lp_core_lp_sysreg_intr_handler(void); ulp_lp_core_lp_ana_peri_intr_handler(void); ulp_lp_core_mailbox_intr_handler(void); ulp_lp_core_lp_wdt_intr_handler(void); ulp_lp_core_lp_rtc_intr_handler(void); ulp_lp_core_sw_intr_handler(void);

  Not all handlers are available on all chips. Please refer to the Technical Reference Manual for your chip for more information.

void ulp_lp_core_intr_disable(void)

  Disables interrupts globally for the low power core.

#### Macros

LP_CORE_ISR_ATTR

#### Header File

- components/ulp/lp_core/shared/include/ulp_lp_core_lp_uart_shared.h

- This header file can be included with:

  >     #include "ulp_lp_core_lp_uart_shared.h"

- This header file is a part of the API provided by the `ulp` component. To declare that your component depends on `ulp`, add the following to your CMakeLists.txt:

  >     REQUIRES ulp

  > or

  >     PRIV_REQUIRES ulp

#### Functions

esp_err_t lp_core_uart_wakeup_setup(const uart_wakeup_cfg_t *cfg)

  Initialize and configure the LP UART wakeup mode.

  备注

  This function configures the LP UART wakeup mode. Ensure that the UART has already been initialized with the lp_core_uart_init() call. Once the LP Core wakes up due to the LP UART, the wakeup will be kept triggered in the chips with SOC_LP_CORE_LP_UART_WAKEUP_KEEP_TRIGGERED. You need to call ulp_lp_core_lp_uart_reset_wakeup_en() to reset the wakeup signal and the UART buffer manually after waking up, which is done in ulp startup phase. Also be aware of limitations in different modes mentioned in the uart_wakeup_cfg_t struct.

  参数:

    cfg -- Configuration for LP UART wakeup mode.

  返回:

    esp_err_t ESP_OK when successful

void lp_core_uart_clear_buf(void)

  Clear LP UART rx tx buffer.

  备注

  Clear LP UART rx tx buffer.

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。