Skip to content

Commit 954d129

Browse files
jath03sudeep-mohanty
authored andcommitted
feat(ulp): LP Timer interrupt example
This commit adds an example to demonstrate the use of the LP Timer interrupt on the LP Core.
1 parent a9eba40 commit 954d129

File tree

8 files changed

+170
-0
lines changed

8 files changed

+170
-0
lines changed

examples/system/.build-test-rules.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,12 @@ examples/system/ulp/lp_core/lp_spi:
334334
depends_components:
335335
- ulp
336336

337+
examples/system/ulp/lp_core/lp_timer_interrupt:
338+
disable:
339+
- if: (SOC_LP_CORE_SUPPORTED != 1) or (SOC_LP_TIMER_SUPPORTED != 1)
340+
depends_components:
341+
- ulp
342+
337343
examples/system/ulp/lp_core/lp_touch:
338344
enable:
339345
- if: SOC_TOUCH_SENSOR_SUPPORTED == 1 and (SOC_DEEP_SLEEP_SUPPORTED == 1 and SOC_LP_CORE_SUPPORTED == 1)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# For more information about build system see
2+
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
3+
# The following five lines of boilerplate have to be in your project's
4+
# CMakeLists in this exact order for cmake to work correctly
5+
cmake_minimum_required(VERSION 3.16)
6+
7+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
8+
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
9+
# idf_build_set_property(MINIMAL_BUILD ON)
10+
project(interrupts)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 |
2+
| ----------------- | -------- | -------- | -------- |
3+
4+
# LP-Core example with interrupt triggered from LP Timer
5+
6+
This example demonstrates how to program the ULP coprocessor to receive an interrupt triggered by the LP Timer
7+
8+
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.
9+
10+
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.
11+
12+
The main core will continuously read a counter of interrupts received as reported by the LP-Core.
13+
14+
## Example output
15+
16+
```
17+
LP core loaded with firmware and running successfully
18+
Interrupt count: 6
19+
```
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Register the component
2+
idf_component_register(SRCS "lp_interrupts_main.c"
3+
INCLUDE_DIRS ""
4+
REQUIRES ulp)
5+
6+
#
7+
# ULP support additions to component CMakeLists.txt.
8+
#
9+
# 1. The LP Core app name must be unique (if multiple components use LP Core).
10+
set(ulp_app_name lp_core_${COMPONENT_NAME})
11+
#
12+
# 2. Specify all C files.
13+
# Files should be placed into a separate directory (in this case, lp_core/),
14+
# which should not be added to COMPONENT_SRCS.
15+
set(ulp_lp_core_sources "lp_core/main.c")
16+
17+
#
18+
# 3. List all the component source files which include automatically
19+
# generated LP Core export file, ${ulp_app_name}.h:
20+
set(ulp_exp_dep_srcs "lp_interrupts_main.c")
21+
22+
#
23+
# 4. Call function to build ULP binary and embed in project using the argument
24+
# values above.
25+
ulp_embed_binary(${ulp_app_name} "${ulp_lp_core_sources}" "${ulp_exp_dep_srcs}")
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stdint.h>
8+
#include "ulp_lp_core_utils.h"
9+
#include "ulp_lp_core_interrupts.h"
10+
#include "ulp_lp_core_lp_timer_shared.h"
11+
12+
uint32_t lp_timer_intr_count = 0;
13+
14+
/* Add LP_CORE_ISR_ATTR to ensure registers are saved and restored */
15+
void LP_CORE_ISR_ATTR ulp_lp_core_lp_timer_intr_handler(void)
16+
{
17+
ulp_lp_core_lp_timer_intr_clear();
18+
lp_timer_intr_count++;
19+
ulp_lp_core_lp_timer_set_wakeup_time(500000);
20+
}
21+
22+
int main(void)
23+
{
24+
ulp_lp_core_intr_enable();
25+
ulp_lp_core_lp_timer_set_wakeup_time(500000);
26+
ulp_lp_core_lp_timer_intr_enable(true);
27+
28+
while (1) {
29+
/* Wait forever, handling interrupts */
30+
asm volatile("wfi");
31+
}
32+
return 0;
33+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stdio.h>
8+
#include "esp_sleep.h"
9+
#include "esp_err.h"
10+
#include "lp_core_main.h"
11+
#include "ulp_lp_core.h"
12+
#include "freertos/FreeRTOS.h"
13+
#include "freertos/task.h"
14+
15+
extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_main_bin_start");
16+
extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_main_bin_end");
17+
18+
static void lp_core_init(void)
19+
{
20+
/* Set LP core wakeup source as the HP CPU */
21+
ulp_lp_core_cfg_t cfg = {
22+
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
23+
};
24+
25+
/* Load LP core firmware */
26+
ESP_ERROR_CHECK(ulp_lp_core_load_binary(lp_core_main_bin_start, (lp_core_main_bin_end - lp_core_main_bin_start)));
27+
28+
/* Run LP core */
29+
ESP_ERROR_CHECK(ulp_lp_core_run(&cfg));
30+
31+
// Give the LP core time to start up
32+
vTaskDelay(pdMS_TO_TICKS(100));
33+
34+
printf("LP core loaded with firmware and running successfully\n");
35+
}
36+
37+
void app_main(void)
38+
{
39+
/* Load LP Core binary and start the coprocessor */
40+
lp_core_init();
41+
42+
for (;;) {
43+
if (ulp_lp_timer_intr_count > 0) {
44+
printf("Interrupt count: %"PRIu32"\n", ulp_lp_timer_intr_count);
45+
vTaskDelay(pdMS_TO_TICKS(250));
46+
printf("\x1b[1F"); // Move to beginning of previous line
47+
printf("\x1b[2K"); // Clear entire line
48+
}
49+
}
50+
51+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: CC0-1.0
3+
import time
4+
5+
import pytest
6+
from pytest_embedded import Dut
7+
from pytest_embedded_idf.utils import idf_parametrize
8+
9+
10+
@pytest.mark.generic
11+
@idf_parametrize('target', ['esp32c5', 'esp32c6', 'esp32p4'], indirect=['target'])
12+
def test_lp_timer_interrupt(dut: Dut) -> None:
13+
# Wait for LP core to be loaded and running
14+
dut.expect_exact('LP core loaded with firmware and running successfully')
15+
16+
# Add a delay to allow interrupts to occur
17+
time.sleep(2)
18+
19+
# Wait for the interrupt count line to be printed
20+
interrupt_count = dut.expect(r'Interrupt count: (\d+)')
21+
count = int(interrupt_count.group(1).decode('utf8'))
22+
assert count > 0, f'Expected interrupt count to be greater than 0, got {count}'
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Enable ULP
2+
CONFIG_ULP_COPROC_ENABLED=y
3+
CONFIG_ULP_COPROC_TYPE_LP_CORE=y
4+
CONFIG_ULP_COPROC_RESERVE_MEM=4096

0 commit comments

Comments
 (0)