Hi everyone,
I’ve been working with the ULP co-processor on the ESP32-S3 and came across a few issues that I couldn’t resolve. While I found several older topics (most over a year old) related to this, including some specific to the ESP32-S3, none directly addressed my problem.
Problem Description
I am able to upload the ULP program by hardcoding the CONFIG_ESP32S3_ULP_COPROC_RESERVE_MEM
to 512 and copying out the upload function. However, the program immediately wakes up the ESP32-S3 without any clear reason.
To debug this, I am storing values in RTC_SLOW_MEM
. My question is: Is this the correct memory to use, or should I use another memory region for storing debug information?
Below is the output from the serial monitor. Each block represents a new boot:
ULP - RTC_SLOW_MEM[0] = -805304306
ULP - RTC_SLOW_MEM[1] = 41
ULP - RTC_SLOW_MEM[2] = 387139
ULP - RTC_SLOW_MEM[3] = -2147090432
ULP - RTC_SLOW_MEM[0] = -805304306
ULP - RTC_SLOW_MEM[1] = 82
ULP - RTC_SLOW_MEM[2] = 506578
ULP - RTC_SLOW_MEM[3] = -2147090432
ULP - RTC_SLOW_MEM[0] = -805304306
ULP - RTC_SLOW_MEM[1] = 123
ULP - RTC_SLOW_MEM[2] = 624057
ULP - RTC_SLOW_MEM[3] = -2147090432
What could I be doing wrong here?
(I can not see any signal on the GPIOs with my oscilloscope.)
Code Snippet
Here is the relevant code for the ULP program and its initialization:
ULP Program Definition:
#define GPIO20_BIT 20
#define GPIO3_BIT 3
#define GPIO20_STATUS_OFFSET 0
#define GPIO3_STATUS_OFFSET 1
#define INITIALIZED_OFFSET 2
#define WAKEUP_REASON_OFFSET 3
const ulp_insn_t ulp_program[] = {
// Prüfe, ob Initialisierung erfolgt ist
I_LD(R2, R3, INITIALIZED_OFFSET), // Lade ulp_initialized aus RTC_SLOW_MEM[INITIALIZED_OFFSET]
I_MOVI(R3, 1), // Lade 1 (Flag für initialisiert)
I_SUBR(R3, R3, R2), // Prüfe: ulp_initialized != 1
I_BE(1, 0), // Springe zu Label 1, wenn ulp_initialized == 1
// Initialisierung
I_RD_REG(RTC_GPIO_IN_REG, GPIO3_BIT, GPIO3_BIT), // GPIO3 initial auslesen
I_ST(R2, R3, GPIO3_STATUS_OFFSET), // Speichere initialen GPIO3-Wert in RTC_SLOW_MEM[GPIO3_STATUS_OFFSET]
I_RD_REG(RTC_GPIO_IN_REG, GPIO20_BIT, GPIO20_BIT), // GPIO20 initial auslesen
I_ST(R2, R3, GPIO20_STATUS_OFFSET), // Speichere initialen GPIO20-Wert in RTC_SLOW_MEM[GPIO20_STATUS_OFFSET]
I_MOVI(R2, 1), // Setze ulp_initialized = 1
I_ST(R2, R3, INITIALIZED_OFFSET), // Speichere ulp_initialized in RTC_SLOW_MEM[INITIALIZED_OFFSET]
M_BX(1), // Springe zur Hauptschleife
// Hauptschleife
M_LABEL(1),
// GPIO3 prüfen
I_RD_REG(RTC_GPIO_IN_REG, GPIO3_BIT, GPIO3_BIT), // GPIO3 auslesen
I_LD(R1, R3, GPIO3_STATUS_OFFSET), // Lade gespeicherten GPIO3-Wert
I_SUBR(R2, R0, R1), // Vergleiche mit gespeicherten Wert
I_BE(2, 0), // Springe zu Label 2, wenn keine Änderung festgestellt
// Änderung festgestellt: GPIO3
I_MOVI(R2, 3), // Speichere Grund (GPIO3 = 3)
I_ST(R2, R3, WAKEUP_REASON_OFFSET), // Schreibe Grund in RTC_SLOW_MEM[WAKEUP_REASON_OFFSET]
I_WAKE(), // Wakeup auslösen
I_HALT(),
// GPIO20 prüfen
M_LABEL(2),
I_RD_REG(RTC_GPIO_IN_REG, GPIO20_BIT, GPIO20_BIT), // GPIO20 auslesen
I_LD(R1, R3, GPIO20_STATUS_OFFSET), // Lade gespeicherten GPIO20-Wert
I_SUBR(R2, R0, R1), // Vergleiche mit gespeicherten Wert
I_BE(3, 0), // Springe zu Label 3, wenn keine Änderung festgestellt
// Änderung festgestellt: GPIO20
I_MOVI(R2, 20), // Speichere Grund (GPIO20 = 20)
I_ST(R2, R3, WAKEUP_REASON_OFFSET), // Schreibe Grund in RTC_SLOW_MEM[WAKEUP_REASON_OFFSET]
I_WAKE(), // Wakeup auslösen
I_HALT(),
// Keine Änderungen festgestellt
M_LABEL(3),
I_HALT(), // Halte das Programm an
};
ULP Initialization:
void init_ulp_program() {
Serial.print("CONFIG_ESP32S3_ULP_COPROC_RESERVE_MEM: ");
Serial.println(CONFIG_ESP32S3_ULP_COPROC_RESERVE_MEM);
memset(RTC_SLOW_MEM, 0, CONFIG_ESP32S3_ULP_COPROC_RESERVE_MEM);
size_t size = sizeof(ulp_program) / sizeof(ulp_insn_t);
esp_err_t err = ulp_process_macros_and_load_custom(0, ulp_program, &size);
if (err != ESP_OK) {
Serial.printf("ULP load failed: %d\n", err);
return;
}
// GPIO20 konfigurieren
rtc_gpio_init(GPIO_NUM_20);
rtc_gpio_set_direction(GPIO_NUM_20, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pullup_dis(GPIO_NUM_20);
rtc_gpio_pulldown_en(GPIO_NUM_20);
rtc_gpio_hold_en(GPIO_NUM_20); // activate Hold
// GPIO3 konfigurieren
rtc_gpio_init(GPIO_NUM_3);
rtc_gpio_set_direction(GPIO_NUM_3, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pullup_dis(GPIO_NUM_3);
rtc_gpio_pulldown_en(GPIO_NUM_3);
ulp_set_wakeup_period(0, 500 * 1000);
}
Wakeup Reason Extraction:
int readRtcMem() {
for (int i = 0; i < 5; i++) {
Serial.printf("ULP - RTC_SLOW_MEM[%d] = %d\n", i, RTC_SLOW_MEM[i]);
}
uint32_t wakeup_reason = RTC_SLOW_MEM[WAKEUP_REASON_OFFSET];
...
}
PlatformIO Configuration:
[env:esp32-s3-devkitc-1]
platform = espressif32 @ 6.9.0
framework = arduino
board = esp32-s3-devkitc-1
monitor_speed = 115200
board_build.flash_mode = qio
board_build.partitions = partitions-8mb.csv
Questions
- Why does the ULP program immediately wake up the ESP32-S3?
- Is
RTC_SLOW_MEM
the correct region for debugging, or should I use something else? - Are there any improvements or mistakes in my code that could cause this behavior?
Thank you in advance for your time and help! Any pointers would be greatly appreciated.