Skip to content

Instantly share code, notes, and snippets.

@igrr
Last active July 21, 2025 21:13
Show Gist options
  • Select an option

  • Save igrr/4b002047fc034180b8f7ca01858e1de5 to your computer and use it in GitHub Desktop.

Select an option

Save igrr/4b002047fc034180b8f7ca01858e1de5 to your computer and use it in GitHub Desktop.
ESP32 ULP ISR example
#include <stdio.h>
#include <unistd.h>
#include "soc/rtc_cntl_reg.h"
#include "esp32/ulp.h"
#include "driver/rtc_cntl.h"
#include "esp_log.h"
#include "ulp_main.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.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 ulp_isr(void* arg)
{
SemaphoreHandle_t done = (SemaphoreHandle_t) arg;
xSemaphoreGiveFromISR(done);
}
void app_main()
{
esp_err_t err = ulp_load_binary(0, ulp_main_bin_start,
(ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
ESP_ERROR_CHECK(err);
ulp_set_wakeup_period(0, 100);
SemaphoreHandle_t ulp_isr_sem = xSemaphoreCreateBinary();
assert(ulp_isr_sem);
err = rtc_isr_register(&ulp_isr, (void*) ulp_isr_sem, RTC_CNTL_SAR_INT_ST_M);
ESP_ERROR_CHECK(err);
REG_SET_BIT(RTC_CNTL_INT_ENA_REG, RTC_CNTL_ULP_CP_INT_ENA_M);
err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
ESP_ERROR_CHECK(err);
int result = xSemaphoreTake(ulp_isr_sem, 1000 / portTICK_PERIOD_MS);
if (result == pdPASS) {
printf("ULP ISR triggered\n");
} else {
printf("ULP ISR timeout\n");
}
}
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/soc_ulp.h"
.text
.global entry
entry:
/* Do some stuff here */
/* Then wake up */
jump wake_up
wake_up:
/* Wake up the SoC, end program */
wake
WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)
halt
@MacWyznawca
Copy link

Hello!
Can you point out a similar example, but for ULP RISC-V with ESP-S3?
I am looking for a way to notify the active task on the main processor from ULP code (not in deep sleep).

@darthcloud
Copy link

@MacWyznawca for ULP RISC-V on ESP32-S3

you replace:

    err = rtc_isr_register(&ulp_isr, (void*) ulp_isr_sem, RTC_CNTL_SAR_INT_ST_M);
    ESP_ERROR_CHECK(err);
    REG_SET_BIT(RTC_CNTL_INT_ENA_REG, RTC_CNTL_ULP_CP_INT_ENA_M);

with

    err = rtc_isr_register(&ulp_isr, (void*) ulp_isr_sem, RTC_CNTL_COCPU_INT_ST_M);
    ESP_ERROR_CHECK(err);
    REG_SET_BIT(RTC_CNTL_INT_ENA_REG, RTC_CNTL_COCPU_INT_ST_M);

and use ulp_riscv_wakeup_main_processor(); function in the riscv application to trigger the interrupt.

@MacWyznawca
Copy link

Thanks @darthcloud!

@mickeyl
Copy link

mickeyl commented Jan 9, 2024

Thanks. The rtc_isr_register seems to take one additional flags argument and is a private function these days. #include "driver/rtc_cntl.h" issues a deprecation warning. Is this still ok to use?

@JellevanKraaij
Copy link

@mickeyl Did you find another solution?

@mickeyl
Copy link

mickeyl commented Jan 26, 2024

Seems to work fine these days. Espressif even added API for it in espressif/esp-idf@a67d15f#diff-62c3d8a0af115257113c79a645b4aa54908b590d97dd7473f705478b8bbd07c6

@BillBernacchi
Copy link

I used the suggested changes from above to make this code work on a ESP32S3 processor and did not get it to work. Does anyone have working example code they could post. I'm using C code on the ULP. I'm not sure what I'm doing wrong but any help would be appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment