feat(ulp-riscv): Added new example to demonstrate ULP RISC-V interrupts

This commit adds a new example which demonstrates how the ULP RISC-V
co-processor handles interrupts.
pull/13090/head
Sudeep Mohanty 2023-12-14 16:15:06 +01:00
rodzic 94e2516f6c
commit 4230acb971
9 zmienionych plików z 293 dodań i 0 usunięć

Wyświetl plik

@ -360,6 +360,12 @@ examples/system/ulp/ulp_riscv/i2c:
depends_components:
- ulp
examples/system/ulp/ulp_riscv/interrupts:
enable:
- if: SOC_RISCV_COPROC_SUPPORTED == 1
depends_components:
- ulp
examples/system/ulp/ulp_riscv/touch:
enable:
- if: SOC_RISCV_COPROC_SUPPORTED == 1

Wyświetl plik

@ -0,0 +1,6 @@
# The following 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)
project(ulp_riscv_interrupts)

Wyświetl plik

@ -0,0 +1,71 @@
| Supported Targets | ESP32-S2 | ESP32-S3 |
| ----------------- | -------- | -------- |
# ULP-RISC-V Interrupt Handling:
This example demonstrates how the ULP-RISC-V coprocessor can register and handle interrupts. Currently this example supports handling of -
- Software triggered interrupts
- RTC IO triggered interrupts
The example keeps a count of the software interrupts triggered on the ULP RISC-V core and wakes up the main processor from deep sleep after a certain threshold.
Additionaly, it wakes up the main processor from deep sleep when a button is pressed and the GPIO interrupt is triggered.
## How to use example
### Hardware Required
The example can be run on any ESP32-S2 or ESP32-S3 based development board connected to a computer with a single USB cable for flashing and monitoring. The external interface should have 3.3V outputs. You may use e.g. 3.3V compatible USB-to-Serial dongle.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
By default, this example uses GPIO#0 which is connected to the "Boot" button on esp32-s2/s3 development boards. To trigger the GPIO interrupt on the ULP RISC-V core, press the Boot button.
## Example Output
Output from main CPU:
```
Not a ULP RISC-V wakeup, initializing it!
Entering in deep sleep
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x5 (DSLEEP),boot:0x8 (SPI_FAST_FLASH_BOOT)
pro cpu reset by JTAG
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3810,len:0x12b4
load:0x403c9700,len:0x4
load:0x403c9704,len:0xaf4
load:0x403cc700,len:0x2bcc
entry 0x403c9898
W (91) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
ULP RISC-V woke up the main CPU!
ULP RISC-V SW Interrupt triggered 5 times.
Entering in deep sleep
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x5 (DSLEEP),boot:0x8 (SPI_FAST_FLASH_BOOT)
pro cpu reset by JTAG
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3810,len:0x12b4
load:0x403c9700,len:0x4
load:0x403c9704,len:0xaf4
load:0x403cc700,len:0x2bcc
entry 0x403c9898
W (91) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
ULP RISC-V woke up the main CPU!
ULP RISC-V GPIO Interrupt triggered.
Entering in deep sleep
```

Wyświetl plik

@ -0,0 +1,27 @@
# Set usual component variables
set(COMPONENT_SRCS "ulp_riscv_example_main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
set(COMPONENT_REQUIRES ulp)
register_component()
#
# ULP support additions to component CMakeLists.txt.
#
# 1. The ULP app name must be unique (if multiple components use ULP).
set(ulp_app_name ulp_${COMPONENT_NAME})
#
# 2. Specify all C and Assembly source files.
# Files should be placed into a separate directory (in this case, ulp/),
# which should not be added to COMPONENT_SRCS.
set(ulp_riscv_sources "ulp/main.c")
#
# 3. List all the component source files which include automatically
# generated ULP export file, ${ulp_app_name}.h:
set(ulp_exp_dep_srcs "ulp_riscv_example_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_riscv_sources}" "${ulp_exp_dep_srcs}")

Wyświetl plik

@ -0,0 +1,10 @@
menu "ULP RISC-V Interrupt Example Configuration"
config EXAMPLE_GPIO_INT
int "GPIO interrupt pin"
range 0 21
default 0
help
GPIO number to trigger an interrupt on the ULP RISC-V core.
endmenu

Wyświetl plik

@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* ULP RISC-V interrupts example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
This code runs on ULP RISC-V coprocessor
*/
#include "ulp_riscv_utils.h"
#include "ulp_riscv_gpio.h"
#define WAKE_PIN CONFIG_EXAMPLE_GPIO_INT
int sw_int_cnt = 0;
int wake_by_sw = 0;
int wake_by_gpio = 0;
/* SW Interrupt Handler */
void sw_int_handler(void *arg)
{
sw_int_cnt++;
}
/* GPIO Interrupt Handler */
void gpio_int_handler(void *arg)
{
wake_by_gpio = 1;
ulp_riscv_wakeup_main_processor();
}
int main(void) {
/* Register SW interrupt handler */
ulp_riscv_enable_sw_intr(sw_int_handler, NULL);
/* Configure GPIO in input mode for interrupt */
ulp_riscv_gpio_init(WAKE_PIN);
ulp_riscv_gpio_input_enable(WAKE_PIN);
/* Register GPIO interrupt handler */
ulp_riscv_gpio_isr_register(WAKE_PIN, ULP_RISCV_GPIO_INTR_POSEDGE, gpio_int_handler, NULL);
while (1) {
/* Trigger SW interrupt */
ulp_riscv_trigger_sw_intr();
if ((sw_int_cnt % 5 == 0) && !wake_by_gpio) {
wake_by_sw = 1;
ulp_riscv_wakeup_main_processor();
}
ulp_riscv_delay_cycles(1000 * ULP_RISCV_CYCLES_PER_MS);
}
return 0;
}

Wyświetl plik

@ -0,0 +1,71 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* ULP RISC-V interrupts example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "esp_sleep.h"
#include "ulp_riscv.h"
#include "ulp_main.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end");
static void init_ulp_program(void);
void app_main(void)
{
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
/* not a wakeup from ULP, load the firmware */
if (cause != ESP_SLEEP_WAKEUP_ULP) {
printf("Not a ULP RISC-V wakeup, initializing it! \n");
init_ulp_program();
}
/* ULP RISC-V handled an interrupt on GPIO#0 */
if (cause == ESP_SLEEP_WAKEUP_ULP) {
printf("ULP RISC-V woke up the main CPU! \n");
if (ulp_wake_by_sw) {
printf("ULP RISC-V SW Interrupt triggered %lu times.\r\n", ulp_sw_int_cnt);
ulp_wake_by_sw = 0;
} else if (ulp_wake_by_gpio) {
printf("ULP RISC-V GPIO Interrupt triggered.\r\n");
ulp_wake_by_gpio = 0;
}
}
/* Go back to sleep, only the ULP RISC-V will run */
printf("Entering in deep sleep\n\n");
/* Small delay to ensure the messages are printed */
vTaskDelay(100);
ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup());
esp_deep_sleep_start();
}
static void init_ulp_program(void)
{
esp_err_t err = ulp_riscv_load_binary(ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start));
ESP_ERROR_CHECK(err);
/* The first argument is the period index, which is not used by the ULP-RISC-V timer
* The second argument is the period in microseconds, which gives a wakeup time period of: 20ms
*/
ulp_set_wakeup_period(0, 20000);
/* Start the program */
err = ulp_riscv_run();
ESP_ERROR_CHECK(err);
}

Wyświetl plik

@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import time
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.generic
def test_ulp_riscv_interrupts(dut: Dut) -> None:
dut.expect_exact('Not a ULP RISC-V wakeup, initializing it!')
dut.expect_exact('Entering in deep sleep')
# Give the chip time to enter deepsleep and trigger a software interrupt
time.sleep(5)
# Verify if SW interrupt got triggered
dut.expect_exact('ULP RISC-V SW Interrupt triggered 5 times.')
# Set GPIO#0 using DTR
dut.serial.proc.setDTR(True)
time.sleep(1)
dut.serial.proc.setDTR(False)
# Verify if GPIO interrupt got triggered
dut.expect_exact('ULP RISC-V GPIO Interrupt triggered.', timeout=5)

Wyświetl plik

@ -0,0 +1,9 @@
# Enable ULP
CONFIG_ULP_COPROC_ENABLED=y
CONFIG_ULP_COPROC_TYPE_RISCV=y
CONFIG_ULP_COPROC_RESERVE_MEM=4096
# Set log level to Warning to produce clean output
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_BOOTLOADER_LOG_LEVEL=2
CONFIG_LOG_DEFAULT_LEVEL_WARN=y
CONFIG_LOG_DEFAULT_LEVEL=2