forked from espressif/esp-idf
Merge branch 'contrib/github_pr_15717' into 'master'
feat(ulp): LP Timer interrupt support (GitHub PR) Closes IDFGH-15026 See merge request espressif/esp-idf!38613
This commit is contained in:
@@ -73,6 +73,11 @@ FORCE_INLINE_ATTR void lp_timer_ll_clear_lp_intsts_mask(lp_timer_dev_t *dev, uin
|
|||||||
dev->lp_int_clr.val = mask;
|
dev->lp_int_clr.val = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE_ATTR void lp_timer_ll_lp_alarm_intr_enable(lp_timer_dev_t *dev, bool enable)
|
||||||
|
{
|
||||||
|
dev->lp_int_ena.main_timer_lp_int_ena = enable;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -73,6 +73,11 @@ FORCE_INLINE_ATTR void lp_timer_ll_clear_lp_intsts_mask(lp_timer_dev_t *dev, uin
|
|||||||
dev->lp_int_clr.val = mask;
|
dev->lp_int_clr.val = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE_ATTR void lp_timer_ll_lp_alarm_intr_enable(lp_timer_dev_t *dev, bool enable)
|
||||||
|
{
|
||||||
|
dev->lp_int_en.alarm = enable;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -73,6 +73,11 @@ FORCE_INLINE_ATTR void lp_timer_ll_clear_lp_intsts_mask(lp_timer_dev_t *dev, uin
|
|||||||
dev->lp_int_clr.val = mask;
|
dev->lp_int_clr.val = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE_ATTR void lp_timer_ll_lp_alarm_intr_enable(lp_timer_dev_t *dev, bool enable)
|
||||||
|
{
|
||||||
|
dev->lp_int_ena.main_timer_lp_int_ena = enable;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -57,6 +57,11 @@ FORCE_INLINE_ATTR void lp_timer_ll_clear_overflow_intr_status(lp_timer_dev_t *de
|
|||||||
dev->int_clr.overflow = 1;
|
dev->int_clr.overflow = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE_ATTR void lp_timer_ll_lp_alarm_intr_enable(lp_timer_dev_t *dev, bool enable)
|
||||||
|
{
|
||||||
|
dev->lp_int_en.alarm = enable;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -73,6 +73,11 @@ FORCE_INLINE_ATTR void lp_timer_ll_clear_lp_intsts_mask(lp_timer_dev_t *dev, uin
|
|||||||
dev->lp_int_clr.val = mask;
|
dev->lp_int_clr.val = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE_ATTR void lp_timer_ll_lp_alarm_intr_enable(lp_timer_dev_t *dev, bool enable)
|
||||||
|
{
|
||||||
|
dev->lp_int_ena.main_timer_lp_int_ena = enable;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -104,6 +104,20 @@ void ulp_lp_core_sw_intr_enable(bool enable);
|
|||||||
*/
|
*/
|
||||||
void ulp_lp_core_sw_intr_clear(void);
|
void ulp_lp_core_sw_intr_clear(void);
|
||||||
|
|
||||||
|
#if SOC_LP_TIMER_SUPPORTED
|
||||||
|
/**
|
||||||
|
* @brief Enable the LP Timer interrupt
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ulp_lp_core_lp_timer_intr_enable(bool enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear the interrupt status for the LP Timer interrupt
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ulp_lp_core_lp_timer_intr_clear(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Puts the CPU into a wait state until an interrupt is triggered
|
* @brief Puts the CPU into a wait state until an interrupt is triggered
|
||||||
*
|
*
|
||||||
|
@@ -191,6 +191,18 @@ void ulp_lp_core_sw_intr_clear(void)
|
|||||||
pmu_ll_lp_clear_sw_intr_status(&PMU);
|
pmu_ll_lp_clear_sw_intr_status(&PMU);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SOC_LP_TIMER_SUPPORTED
|
||||||
|
void ulp_lp_core_lp_timer_intr_enable(bool enable)
|
||||||
|
{
|
||||||
|
lp_timer_ll_lp_alarm_intr_enable(&LP_TIMER, enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ulp_lp_core_lp_timer_intr_clear(void)
|
||||||
|
{
|
||||||
|
lp_timer_ll_clear_lp_alarm_intr_status(&LP_TIMER);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void ulp_lp_core_wait_for_intr(void)
|
void ulp_lp_core_wait_for_intr(void)
|
||||||
{
|
{
|
||||||
asm volatile("wfi");
|
asm volatile("wfi");
|
||||||
|
@@ -334,6 +334,12 @@ examples/system/ulp/lp_core/lp_spi:
|
|||||||
depends_components:
|
depends_components:
|
||||||
- ulp
|
- ulp
|
||||||
|
|
||||||
|
examples/system/ulp/lp_core/lp_timer_interrupt:
|
||||||
|
disable:
|
||||||
|
- if: (SOC_LP_CORE_SUPPORTED != 1) or (SOC_LP_TIMER_SUPPORTED != 1)
|
||||||
|
depends_components:
|
||||||
|
- ulp
|
||||||
|
|
||||||
examples/system/ulp/lp_core/lp_touch:
|
examples/system/ulp/lp_core/lp_touch:
|
||||||
enable:
|
enable:
|
||||||
- if: SOC_TOUCH_SENSOR_SUPPORTED == 1 and (SOC_DEEP_SLEEP_SUPPORTED == 1 and SOC_LP_CORE_SUPPORTED == 1)
|
- if: SOC_TOUCH_SENSOR_SUPPORTED == 1 and (SOC_DEEP_SLEEP_SUPPORTED == 1 and SOC_LP_CORE_SUPPORTED == 1)
|
||||||
|
@@ -0,0 +1,10 @@
|
|||||||
|
# For more information about build system see
|
||||||
|
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||||
|
# The following five lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||||
|
# idf_build_set_property(MINIMAL_BUILD ON)
|
||||||
|
project(interrupts)
|
19
examples/system/ulp/lp_core/lp_timer_interrupt/README.md
Normal file
19
examples/system/ulp/lp_core/lp_timer_interrupt/README.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 |
|
||||||
|
| ----------------- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# LP-Core example with interrupt triggered from LP Timer
|
||||||
|
|
||||||
|
This example demonstrates how to program the ULP coprocessor to receive an interrupt triggered by the LP Timer
|
||||||
|
|
||||||
|
ULP program written in C can be found across `lp_core/main.c`. The build system compiles and links this program, converts it into binary format, and embeds it into the .rodata section of the ESP-IDF application.
|
||||||
|
|
||||||
|
At runtime, the application running inside the main CPU loads ULP program into the `RTC_SLOW_MEM` memory region using `ulp_lp_core_load_binary` function. The main code then configures the ULP and starts the coprocessor by using `ulp_lp_core_run`. Once the ULP program is started, it runs continuously, waiting for interrupts. The main program will periodically trigger interrupts on the LP-Core.
|
||||||
|
|
||||||
|
The main core will continuously read a counter of interrupts received as reported by the LP-Core.
|
||||||
|
|
||||||
|
## Example output
|
||||||
|
|
||||||
|
```
|
||||||
|
LP core loaded with firmware and running successfully
|
||||||
|
Interrupt count: 6
|
||||||
|
```
|
@@ -0,0 +1,25 @@
|
|||||||
|
# Register the component
|
||||||
|
idf_component_register(SRCS "lp_interrupts_main.c"
|
||||||
|
INCLUDE_DIRS ""
|
||||||
|
REQUIRES ulp)
|
||||||
|
|
||||||
|
#
|
||||||
|
# ULP support additions to component CMakeLists.txt.
|
||||||
|
#
|
||||||
|
# 1. The LP Core app name must be unique (if multiple components use LP Core).
|
||||||
|
set(ulp_app_name lp_core_${COMPONENT_NAME})
|
||||||
|
#
|
||||||
|
# 2. Specify all C files.
|
||||||
|
# Files should be placed into a separate directory (in this case, lp_core/),
|
||||||
|
# which should not be added to COMPONENT_SRCS.
|
||||||
|
set(ulp_lp_core_sources "lp_core/main.c")
|
||||||
|
|
||||||
|
#
|
||||||
|
# 3. List all the component source files which include automatically
|
||||||
|
# generated LP Core export file, ${ulp_app_name}.h:
|
||||||
|
set(ulp_exp_dep_srcs "lp_interrupts_main.c")
|
||||||
|
|
||||||
|
#
|
||||||
|
# 4. Call function to build ULP binary and embed in project using the argument
|
||||||
|
# values above.
|
||||||
|
ulp_embed_binary(${ulp_app_name} "${ulp_lp_core_sources}" "${ulp_exp_dep_srcs}")
|
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "ulp_lp_core_utils.h"
|
||||||
|
#include "ulp_lp_core_interrupts.h"
|
||||||
|
#include "ulp_lp_core_lp_timer_shared.h"
|
||||||
|
|
||||||
|
uint32_t lp_timer_intr_count = 0;
|
||||||
|
|
||||||
|
/* Add LP_CORE_ISR_ATTR to ensure registers are saved and restored */
|
||||||
|
void LP_CORE_ISR_ATTR ulp_lp_core_lp_timer_intr_handler(void)
|
||||||
|
{
|
||||||
|
ulp_lp_core_lp_timer_intr_clear();
|
||||||
|
lp_timer_intr_count++;
|
||||||
|
ulp_lp_core_lp_timer_set_wakeup_time(500000);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
ulp_lp_core_intr_enable();
|
||||||
|
ulp_lp_core_lp_timer_set_wakeup_time(500000);
|
||||||
|
ulp_lp_core_lp_timer_intr_enable(true);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Wait forever, handling interrupts */
|
||||||
|
asm volatile("wfi");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "esp_sleep.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "lp_core_main.h"
|
||||||
|
#include "ulp_lp_core.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_main_bin_start");
|
||||||
|
extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_main_bin_end");
|
||||||
|
|
||||||
|
static void lp_core_init(void)
|
||||||
|
{
|
||||||
|
/* Set LP core wakeup source as the HP CPU */
|
||||||
|
ulp_lp_core_cfg_t cfg = {
|
||||||
|
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Load LP core firmware */
|
||||||
|
ESP_ERROR_CHECK(ulp_lp_core_load_binary(lp_core_main_bin_start, (lp_core_main_bin_end - lp_core_main_bin_start)));
|
||||||
|
|
||||||
|
/* Run LP core */
|
||||||
|
ESP_ERROR_CHECK(ulp_lp_core_run(&cfg));
|
||||||
|
|
||||||
|
// Give the LP core time to start up
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
|
||||||
|
printf("LP core loaded with firmware and running successfully\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
/* Load LP Core binary and start the coprocessor */
|
||||||
|
lp_core_init();
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (ulp_lp_timer_intr_count > 0) {
|
||||||
|
printf("Interrupt count: %"PRIu32"\n", ulp_lp_timer_intr_count);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(250));
|
||||||
|
printf("\x1b[1F"); // Move to beginning of previous line
|
||||||
|
printf("\x1b[2K"); // Clear entire line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,22 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
import time
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
from pytest_embedded_idf.utils import idf_parametrize
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic
|
||||||
|
@idf_parametrize('target', ['esp32c5', 'esp32c6', 'esp32p4'], indirect=['target'])
|
||||||
|
def test_lp_timer_interrupt(dut: Dut) -> None:
|
||||||
|
# Wait for LP core to be loaded and running
|
||||||
|
dut.expect_exact('LP core loaded with firmware and running successfully')
|
||||||
|
|
||||||
|
# Add a delay to allow interrupts to occur
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# Wait for the interrupt count line to be printed
|
||||||
|
interrupt_count = dut.expect(r'Interrupt count: (\d+)')
|
||||||
|
count = int(interrupt_count.group(1).decode('utf8'))
|
||||||
|
assert count > 0, f'Expected interrupt count to be greater than 0, got {count}'
|
@@ -0,0 +1,4 @@
|
|||||||
|
# Enable ULP
|
||||||
|
CONFIG_ULP_COPROC_ENABLED=y
|
||||||
|
CONFIG_ULP_COPROC_TYPE_LP_CORE=y
|
||||||
|
CONFIG_ULP_COPROC_RESERVE_MEM=4096
|
Reference in New Issue
Block a user