ESP32-C3 creaking bare-metal interruptions

User avatar
EUtrilla2002
Posts: 18
Joined: Mon Mar 31, 2025 10:23 am

ESP32-C3 creaking bare-metal interruptions

Postby EUtrilla2002 » Mon Sep 08, 2025 9:10 am

Hi
I would like to learn how can i attach interrupts in my ESP32-C3 (for example, light a led when i press a button) using bare-metal assembly in an ESP-IDF environment
I saw there is this man some years ago asking the same question: https://forum.allaboutcircuits.com/thre ... al.194375/ however, he did not say anything more :(

Help is much apreciated

Thanks

MicroController
Posts: 2663
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: ESP32-C3 creaking bare-metal interruptions

Postby MicroController » Mon Sep 08, 2025 9:36 am

ESP32-C3 TRM v1.3, Chapter 8 "Interrupt Matrix (INTERRUPT)"

User avatar
EUtrilla2002
Posts: 18
Joined: Mon Mar 31, 2025 10:23 am

Re: ESP32-C3 creaking bare-metal interruptions

Postby EUtrilla2002 » Thu Sep 25, 2025 11:42 am

Hi
Thanks for replying
After a certain time trying to follow up the guide, i came up with this code

Code: Select all

#include <stdint.h>
#include <stdio.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_task_wdt.h"


#define LED_GPIO               2
#define BUTTON_GPIO            4  

#define INTERRUPT_MATRIX_BASE      0x600C2000 

#define GPIO_OUT_W1TS_REG      0x60004008
#define GPIO_OUT_W1TC_REG      0x6000400C
#define GPIO_ENABLE_W1TS_REG   0x60004020
#define GPIO_ENABLE_W1TC_REG   0x60004024
#define GPIO_IN_REG            0x6000403C  // Registro para leer entradas
// Interrupciones 
#define MTVEC_CSR_ADDR 0x305
#define MTVEC_VECTORED_MODE 0x1

// IO_MUX (entrada digital)
#define IO_MUX_BASE             0x60009000
#define IO_MUX_GPIO4_REG        (IO_MUX_BASE + 0x14)  // GPIO4

// GPIO Matrix (interrupciones)
#define GPIO_PIN4_REG           0x60004084
#define GPIO_CPU_INT_REG        0x60004098
#define GPIO_STATUS_W1TC_REG    0x60004044

// Macros de bits
#define FUN_IE                  (1 << 9)
#define GPIO_PIN_INT_TYPE_S     5
#define GPIO_PIN_INT_ENA_S      13
// Asignar source 16 (GPIO_PROCPU_INTR) a la interrupción 4 de la CPU
#define INTERRUPT_CORE0_GPIO_INTERRUPT_PRO_MAP_REG 0x600C2040
// Configuración CPU para atender la interrupción 
#define INTERRUPT_CORE0_CPU_INT_TYPE_REG    0x600C20F8
#define INTERRUPT_CORE0_CPU_INT_PRI_BASE    0x600C2118 // + Num_P  * 0x04
#define INTERRUPT_CORE0_CPU_INT_ENABLE_REG  0x600C2104

// Definición de WCL (World Controller)
// El registro WCL_CORE_0_MTVEC_BASE_REG debe actualizarse al cambiar mtvec .
// La dirección base del WCL es relativa a la base del System and Memory . 
// Usamos una dirección conceptual/placeholder aquí:
#define WCL_BASE_ADDR 0x600D0000 // Dirección conceptual (la dirección exacta no está en las fuentes)
#define WCL_MTVEC_BASE_REG_OFFSET 0x00000004 // Offset conceptual 

// Limpieza
#define INTERRUPT_CORE0_CPU_INT_CLEAR_REG 0x600C210C

//Vector de interrupciones custom 
#define INTERRUPT_CORE0_INTR_STATUS_0_REG 0x600C2080 

#define GPIO_STATUS_INTERRUPT 0x600C2044
#define INTERRUPT_CORE0_CPU_INT_EIP_STATUS_REG 0x600C2110



// Funciones para alterar CSR
static inline uint32_t read_csr(uint32_t csr_addr) {
    uint32_t val;
    asm volatile ("csrr %0, %1" : "=r"(val) : "i"(csr_addr));
    return val;
}

static inline void write_csr(uint32_t csr_addr, uint32_t val) {
    asm volatile ("csrw %0, %1" :: "i"(csr_addr), "r"(val));
}

//Entrada
volatile uint32_t *gpio_disable_out_reg = (volatile uint32_t *)GPIO_ENABLE_W1TC_REG; //Deshabilita la salida (es decir,abre la entrada)
volatile uint32_t *gpio_in_reg = (volatile uint32_t *)GPIO_IN_REG; //Lee la entrada
//Salida
volatile uint32_t *gpio_enable_out_reg = (volatile uint32_t *)GPIO_ENABLE_W1TS_REG; //Habilita la salida
volatile uint32_t *gpio_out_w1ts_reg = (volatile uint32_t *)GPIO_OUT_W1TS_REG; //Enciende la salida
volatile uint32_t *gpio_out_w1tc_reg = (volatile uint32_t *)GPIO_OUT_W1TC_REG; //Apaga la salida


void initialize_led(void)
{
    *gpio_enable_out_reg = 1 << LED_GPIO; // Configura GPIO2 como salida
}

void initialize_button(void)
{
    *gpio_disable_out_reg = 1 << BUTTON_GPIO; // Configura GPIO0 como entrada
}

// I. Configuración del Pin GPIO (Capa IO MUX y GPIO Matrix)
int interrupt_button(void)
{
    *gpio_disable_out_reg = 1 << BUTTON_GPIO; // GPIO4 entrada

    // (1) Habilitar entrada digital en IO_MUX
    *((volatile uint32_t *)IO_MUX_GPIO4_REG) |= (1 << 9); // FUN_IE

    // (2) Configurar tipo de interrupción: flanco ascendente (1)
    uint32_t reg = *((volatile uint32_t *)GPIO_PIN4_REG);
    reg = (reg & ~(7 << 5)) | (1 << 5) | (1 << 13);
    *((volatile uint32_t *)GPIO_PIN4_REG) = reg;

    return 0;
}
// II.Asignar la fuente de interrupción (GPIO4) a la interrupción 4
void map_gpio_to_interrupt(uint8_t Num_P)
{
   if (Num_P < 1 || Num_P > 31) return; // Validación simple

    volatile uint32_t *reg = (volatile uint32_t *)INTERRUPT_CORE0_GPIO_INTERRUPT_PRO_MAP_REG;

    // Limpiar bits [4:0] y escribir Num_P
    uint32_t val = *reg;
    val &= ~0x1F;       // borrar bits 0-4
    val |= (Num_P & 0x1F); 
    *reg = val; 
}
/// III. Configuración de la Interrupción de la CPU (Controlador de Interrupciones del CPU)
void configure_cpu_interrupt(uint8_t Num_P, uint8_t edge_or_level, uint8_t priority) {
    if (Num_P < 1 || Num_P > 31) return;
    if (priority < 1) priority = 1;
    if (priority > 15) priority = 15;

    // --- Paso 1: Tipo de interrupción ---
    volatile uint32_t *type_reg = (volatile uint32_t *)INTERRUPT_CORE0_CPU_INT_TYPE_REG;
    if (edge_or_level) {
        *type_reg |= (1 << Num_P);  // Flanco
    } else {
        *type_reg &= ~(1 << Num_P); // Nivel
    }

    // --- Paso 2: Prioridad ---
    volatile uint32_t *pri_reg = (volatile uint32_t *)(INTERRUPT_CORE0_CPU_INT_PRI_BASE + (Num_P - 1) * 4);
    *pri_reg = priority;

    // --- Paso 3: Habilitar interrupción ---
    volatile uint32_t *enable_reg = (volatile uint32_t *)INTERRUPT_CORE0_CPU_INT_ENABLE_REG;
    *enable_reg |= (1 << Num_P);

    // --- Paso 4: Habilitación global MIE ---
    uint32_t mie;
    asm volatile ("csrr %0, mstatus" : "=r"(mie));
    mie |= (1 << 3); // Bit MIE = Machine Interrupt Enable
    asm volatile ("csrw mstatus, %0" :: "r"(mie));
    asm volatile ("fence"); // asegurar la propagación de escrituras
}
// (IV) Limpiar el estado
void clear_gpio_interrupt(uint8_t gpio_num, uint8_t cpu_num, uint8_t is_edge) {
    // --- Limpiar interrupción GPIO ---
    *((volatile uint32_t *)GPIO_STATUS_W1TC_REG) = (1 << gpio_num);

    // --- Limpiar interrupción CPU (si es flanco) ---
    if (is_edge) {
        volatile uint32_t *cpu_clear_reg = (volatile uint32_t *)INTERRUPT_CORE0_CPU_INT_CLEAR_REG;
        *cpu_clear_reg |= (1 << cpu_num);   // poner 1
        *cpu_clear_reg &= ~(1 << cpu_num);  // resetear a 0
    }
}


// int read_button(void)
// {
//     return ((*gpio_in_reg >> BUTTON_GPIO) & 0x1); // 1 si presionado, 0 si no
// }

//Modificar vector de interrupciones
// void gpio_isr(void){

// }


void toggle_led(void)
{
    *gpio_out_w1tc_reg = 1 << LED_GPIO; // Apaga LED
    for (volatile int i = 0; i < 100000; i++); // Delay simple
    *gpio_out_w1ts_reg = 1 << LED_GPIO; // Enciende LED
    for (volatile int i = 0; i < 100000; i++); // Delay simple
}
void GPIO4_ISR(void) {
    clear_gpio_interrupt(4, 4, 1); // Limpiar interrupción
    toggle_led(); // Alternar LED
}


int app_main(void)
{
    // 0 Inicialización hardware
    initialize_led();
    initialize_button();
    //---1 Configuración del pin GPIO4 como entrada con interrupción
    interrupt_button();
    //---2. Asignar GPIO4 a la interrupción 4
    map_gpio_to_interrupt(16); // Fuente 16 = GPIO_PROCPU_INTR
    //--3. Configurar la interrupción 4:
    // edge_or_level = 1 (flanco), priority = 1 (baja)
    configure_cpu_interrupt(16, 1, 1);
    printf("CONFIGURED\n");
    while (1) {
        //esp_task_wdt_reset(); // Resetear el watchdog
            volatile uint32_t *gpio_intr_status = (volatile uint32_t *)GPIO_STATUS_INTERRUPT; // registro R/W/SS
    uint32_t pending = *gpio_intr_status;
    esp_rom_printf("GPIO interrupt status: 0x%08lX\n", (unsigned long)pending);



        if (pending & (1 << 16)) {
            printf("GPIO4 interrupt pending!\n");
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
    }

    // while(1) {
    //     uint32_t pending_gpio = *((volatile uint32_t *)GPIO_STATUS_W1TC_REG);
    //     if (pending_gpio & (1 << 4)) {
    //         printf("GPIO4 interrupt pending at peripheral level\n");
    //     }

    // }

    return 0;
}
For now, i don't know
1. How can i see if i attach correctly the interrupt
2. how to create the ISR thah te ESP-IDF API does
Any comment is much apreciated

Who is online

Users browsing this forum: Baidu [Spider], Bing [Bot], ChatGPT-User, PerplexityBot and 9 guests