Merge branch 'feature/riscv_ulp_gpio_intr' into 'master'

ULP: Add example of using GPIO to wakeup the ULP-RISCV processor

Closes IDFGH-6589

See merge request espressif/esp-idf!17288
This commit is contained in:
Marius Vikhammer 2022-03-03 09:40:03 +08:00
commit 706a14884f
10 changed files with 261 additions and 10 deletions

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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);
}

View File

@ -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)

View File

@ -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
```

View File

@ -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}")

View File

@ -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;
}

View File

@ -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 <stdio.h>
#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);
}

View File

@ -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