From c974a000d7956d02ded34c2e2278b0eaf2b3d3ce Mon Sep 17 00:00:00 2001 From: Marius Vikhammer Date: Mon, 28 Feb 2022 12:05:48 +0800 Subject: [PATCH] ULP: Add example of using GPIO to wakeup the ULP-RISCV processor --- components/ulp/ulp_riscv/include/ulp_riscv.h | 26 +++++- .../ulp/ulp_riscv/include/ulp_riscv_utils.h | 6 ++ components/ulp/ulp_riscv/ulp_riscv.c | 45 ++++++++-- components/ulp/ulp_riscv/ulp_riscv_utils.c | 5 ++ .../ulp_riscv/gpio_interrupt/CMakeLists.txt | 12 +++ .../system/ulp_riscv/gpio_interrupt/README.md | 27 ++++++ .../gpio_interrupt/main/CMakeLists.txt | 27 ++++++ .../ulp_riscv/gpio_interrupt/main/ulp/main.c | 31 +++++++ .../main/ulp_riscv_gpio_intr_example_main.c | 83 +++++++++++++++++++ .../gpio_interrupt/sdkconfig.defaults | 9 ++ 10 files changed, 261 insertions(+), 10 deletions(-) create mode 100644 examples/system/ulp_riscv/gpio_interrupt/CMakeLists.txt create mode 100644 examples/system/ulp_riscv/gpio_interrupt/README.md create mode 100644 examples/system/ulp_riscv/gpio_interrupt/main/CMakeLists.txt create mode 100644 examples/system/ulp_riscv/gpio_interrupt/main/ulp/main.c create mode 100644 examples/system/ulp_riscv/gpio_interrupt/main/ulp_riscv_gpio_intr_example_main.c create mode 100644 examples/system/ulp_riscv/gpio_interrupt/sdkconfig.defaults diff --git a/components/ulp/ulp_riscv/include/ulp_riscv.h b/components/ulp/ulp_riscv/include/ulp_riscv.h index 847a56d253..9cb7295b9a 100644 --- a/components/ulp/ulp_riscv/include/ulp_riscv.h +++ b/components/ulp/ulp_riscv/include/ulp_riscv.h @@ -15,8 +15,32 @@ extern "C" { #endif +typedef enum { + ULP_RISCV_WAKEUP_SOURCE_TIMER, + ULP_RISCV_WAKEUP_SOURCE_GPIO, +} ulp_riscv_wakeup_source_t; + +typedef struct { + ulp_riscv_wakeup_source_t wakeup_source; +} ulp_riscv_cfg_t; + +#define ULP_RISCV_DEFAULT_CONFIG() \ + { \ + .wakeup_source = ULP_RISCV_WAKEUP_SOURCE_TIMER, \ + } + /** - * @brief Run the program loaded into RTC memory + * @brief Configure the ULP and run the program loaded into RTC memory + * + * @param cfg pointer to the config struct + * @return ESP_OK on success + */ +esp_err_t ulp_riscv_config_and_run(ulp_riscv_cfg_t* cfg); + +/** + * @brief Configure the ULP with default settings + * and run the program loaded into RTC memory + * * @return ESP_OK on success */ esp_err_t ulp_riscv_run(void); diff --git a/components/ulp/ulp_riscv/include/ulp_riscv_utils.h b/components/ulp/ulp_riscv/include/ulp_riscv_utils.h index ed7d1daabc..0f6d749754 100644 --- a/components/ulp/ulp_riscv/include/ulp_riscv_utils.h +++ b/components/ulp/ulp_riscv/include/ulp_riscv_utils.h @@ -96,6 +96,12 @@ void ulp_riscv_timer_resume(void); */ void ulp_riscv_delay_cycles(uint32_t cycles); +/** + * @brief Clears the GPIO wakeup interrupt bit + * + */ +void ulp_riscv_gpio_wakeup_clear(void); + #ifdef __cplusplus } #endif diff --git a/components/ulp/ulp_riscv/ulp_riscv.c b/components/ulp/ulp_riscv/ulp_riscv.c index 7882834d5d..7a769f1477 100644 --- a/components/ulp/ulp_riscv/ulp_riscv.c +++ b/components/ulp/ulp_riscv/ulp_riscv.c @@ -20,8 +20,32 @@ #include "ulp_common.h" #include "esp_rom_sys.h" -esp_err_t ulp_riscv_run(void) +static esp_err_t ulp_riscv_config_wakeup_source(ulp_riscv_wakeup_source_t wakeup_source) { + esp_err_t ret = ESP_OK; + + switch (wakeup_source) { + case ULP_RISCV_WAKEUP_SOURCE_TIMER: + /* start ULP_TIMER */ + CLEAR_PERI_REG_MASK(RTC_CNTL_ULP_CP_CTRL_REG, RTC_CNTL_ULP_CP_FORCE_START_TOP); + SET_PERI_REG_MASK(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + break; + + case ULP_RISCV_WAKEUP_SOURCE_GPIO: + SET_PERI_REG_MASK(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_GPIO_WAKEUP_ENA); + break; + + default: + ret = ESP_ERR_INVALID_ARG; + } + + return ret; +} + +esp_err_t ulp_riscv_config_and_run(ulp_riscv_cfg_t* cfg) +{ + esp_err_t ret = ESP_OK; + #if CONFIG_IDF_TARGET_ESP32S2 /* Reset COCPU when power on. */ SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_SHUT_RESET_EN); @@ -42,11 +66,8 @@ esp_err_t ulp_riscv_run(void) /* Select ULP-RISC-V to send the DONE signal. */ SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_DONE_FORCE); - /* start ULP_TIMER */ - CLEAR_PERI_REG_MASK(RTC_CNTL_ULP_CP_CTRL_REG, RTC_CNTL_ULP_CP_FORCE_START_TOP); - SET_PERI_REG_MASK(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + ret = ulp_riscv_config_wakeup_source(cfg->wakeup_source); - return ESP_OK; #elif CONFIG_IDF_TARGET_ESP32S3 /* Reset COCPU when power on. */ SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_CLK_FO); @@ -73,9 +94,7 @@ esp_err_t ulp_riscv_run(void) /* Set the CLKGATE_EN signal */ SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_CLKGATE_EN); - /* start ULP_TIMER */ - CLEAR_PERI_REG_MASK(RTC_CNTL_ULP_CP_CTRL_REG, RTC_CNTL_ULP_CP_FORCE_START_TOP); - SET_PERI_REG_MASK(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + ret = ulp_riscv_config_wakeup_source(cfg->wakeup_source); /* Select RISC-V as the ULP_TIMER trigger target * Selecting the RISC-V as the Coprocessor at the end is a workaround @@ -87,8 +106,16 @@ esp_err_t ulp_riscv_run(void) esp_rom_delay_us(20); REG_WRITE(RTC_CNTL_INT_CLR_REG, RTC_CNTL_COCPU_INT_CLR | RTC_CNTL_COCPU_TRAP_INT_CLR | RTC_CNTL_ULP_CP_INT_CLR); - return ESP_OK; + #endif + + return ret; +} + +esp_err_t ulp_riscv_run(void) +{ + ulp_riscv_cfg_t cfg = ULP_RISCV_DEFAULT_CONFIG(); + return ulp_riscv_config_and_run(&cfg); } void ulp_riscv_timer_stop(void) diff --git a/components/ulp/ulp_riscv/ulp_riscv_utils.c b/components/ulp/ulp_riscv/ulp_riscv_utils.c index bbbf64e9ad..75e48982c0 100644 --- a/components/ulp/ulp_riscv/ulp_riscv_utils.c +++ b/components/ulp/ulp_riscv/ulp_riscv_utils.c @@ -53,3 +53,8 @@ void ulp_riscv_timer_resume(void) { SET_PERI_REG_MASK(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); } + +void ulp_riscv_gpio_wakeup_clear(void) +{ + SET_PERI_REG_MASK(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_GPIO_WAKEUP_CLR); +} diff --git a/examples/system/ulp_riscv/gpio_interrupt/CMakeLists.txt b/examples/system/ulp_riscv/gpio_interrupt/CMakeLists.txt new file mode 100644 index 0000000000..6207a44ed8 --- /dev/null +++ b/examples/system/ulp_riscv/gpio_interrupt/CMakeLists.txt @@ -0,0 +1,12 @@ +# 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.5) + +if(NOT IDF_TARGET STREQUAL "esp32s2") + #IDF-4514 + message(FATAL_ERROR "DO NOT BUILD THIS APP FOR ANY TARGET OTHER THAN ESP32-S2 OTHERWISE YOU MAY BRICK YOUR DEVICE") + return() +endif() + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ulp_riscv_example) diff --git a/examples/system/ulp_riscv/gpio_interrupt/README.md b/examples/system/ulp_riscv/gpio_interrupt/README.md new file mode 100644 index 0000000000..5fd432b6fc --- /dev/null +++ b/examples/system/ulp_riscv/gpio_interrupt/README.md @@ -0,0 +1,27 @@ +| Supported Targets | ESP32-S2 | +| ----------------- | -------- | + +# ULP-RISC-V simple example with GPIO Interrupt: + +This example demonstrates how to program the ULP-RISC-V coprocessor to wake up from a RTC IO interrupt, instead of waking periodically from the ULP timer. + +ULP program written in C can be found across `ulp/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_riscv_load_binary` function. The main code then configures the ULP GPIO wakeup source and starts the coprocessor by using `ulp_riscv_config_and_run` followed by putting the chip into deep sleep mode. + +When the wakeup source pin is pulled low the ULP-RISC-V coprocessor is woken up, sends a wakeup signal to the main CPU and goes back to sleep again. + +In this example the input signal is connected to GPIO0. Note that this pin was chosen because most development boards have a button connected to it, so the pulses to be counted can be generated by pressing the button. For real world applications this is not a good choice of a pin, because GPIO0 also acts as a bootstrapping pin. To change the pin number, check the Chip Pin List document and adjust `WAKEUP_PIN` variable in main.c. + + +## Example output + +``` +Not a ULP-RISC-V wakeup, initializing it! +Entering in deep sleep + +... + +ULP-RISC-V woke up the main CPU! +Entering in deep sleep +``` diff --git a/examples/system/ulp_riscv/gpio_interrupt/main/CMakeLists.txt b/examples/system/ulp_riscv/gpio_interrupt/main/CMakeLists.txt new file mode 100644 index 0000000000..f2eb1c5975 --- /dev/null +++ b/examples/system/ulp_riscv/gpio_interrupt/main/CMakeLists.txt @@ -0,0 +1,27 @@ +# Set usual component variables +set(COMPONENT_SRCS "ulp_riscv_gpio_intr_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_gpio_intr_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}") diff --git a/examples/system/ulp_riscv/gpio_interrupt/main/ulp/main.c b/examples/system/ulp_riscv/gpio_interrupt/main/ulp/main.c new file mode 100644 index 0000000000..25bb12fefa --- /dev/null +++ b/examples/system/ulp_riscv/gpio_interrupt/main/ulp/main.c @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* ULP-RISC-V 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.h" +#include "ulp_riscv_utils.h" + +int main (void) +{ + ulp_riscv_wakeup_main_processor(); + + /* Wakeup interrupt is a level interrupt, wait 1 sec to + allow user to release button to avoid waking up the ULP multiple times */ + ulp_riscv_delay_cycles(1000*1000 * ULP_RISCV_CYCLES_PER_US); + ulp_riscv_gpio_wakeup_clear(); + + /* ulp_riscv_halt() is called automatically when main exits */ + return 0; +} diff --git a/examples/system/ulp_riscv/gpio_interrupt/main/ulp_riscv_gpio_intr_example_main.c b/examples/system/ulp_riscv/gpio_interrupt/main/ulp_riscv_gpio_intr_example_main.c new file mode 100644 index 0000000000..291b53c9f3 --- /dev/null +++ b/examples/system/ulp_riscv/gpio_interrupt/main/ulp_riscv_gpio_intr_example_main.c @@ -0,0 +1,83 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* ULP riscv DS18B20 1wire temperature sensor 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 +#include "esp_sleep.h" +#include "soc/sens_reg.h" +#include "driver/gpio.h" +#include "ulp_riscv.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#define WAKEUP_PIN 0 + +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); + +static void wakeup_gpio_init(void) +{ + /* Configure the button GPIO as input, enable wakeup */ + gpio_config_t config = { + .pin_bit_mask = BIT64(WAKEUP_PIN), + .mode = GPIO_MODE_INPUT + }; + ESP_ERROR_CHECK(gpio_config(&config)); + + gpio_wakeup_enable(WAKEUP_PIN, GPIO_INTR_LOW_LEVEL); + gpio_hold_en(WAKEUP_PIN); +} + +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"); + wakeup_gpio_init(); + init_ulp_program(); + } + + if (cause == ESP_SLEEP_WAKEUP_ULP) { + printf("ULP-RISC-V woke up the main CPU! \n"); + } + + /* 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); + + /* RTC peripheral power domain needs to be kept on to detect + the GPIO state change */ + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + 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); + + /* Start the program */ + ulp_riscv_cfg_t cfg = { + .wakeup_source = ULP_RISCV_WAKEUP_SOURCE_GPIO, + }; + + err = ulp_riscv_config_and_run(&cfg); + ESP_ERROR_CHECK(err); +} diff --git a/examples/system/ulp_riscv/gpio_interrupt/sdkconfig.defaults b/examples/system/ulp_riscv/gpio_interrupt/sdkconfig.defaults new file mode 100644 index 0000000000..e3745e5057 --- /dev/null +++ b/examples/system/ulp_riscv/gpio_interrupt/sdkconfig.defaults @@ -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