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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
#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
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
@@ -104,6 +104,20 @@ void ulp_lp_core_sw_intr_enable(bool enable);
|
||||
*/
|
||||
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
|
||||
*
|
||||
|
@@ -191,6 +191,18 @@ void ulp_lp_core_sw_intr_clear(void)
|
||||
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)
|
||||
{
|
||||
asm volatile("wfi");
|
||||
|
@@ -334,6 +334,12 @@ examples/system/ulp/lp_core/lp_spi:
|
||||
depends_components:
|
||||
- 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:
|
||||
enable:
|
||||
- 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