espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.8k stars 7.31k forks source link

Using gpio_isr_register crashes the app on esp32-c3 (IDFGH-10626) #11860

Open okhsunrog opened 1 year ago

okhsunrog commented 1 year ago

Answers checklist.

IDF version.

5.1 release

Operating System used.

Linux

How did you build your project?

VS Code IDE

If you are using Windows, please specify command line type.

None

Development Kit.

ESP32-C3 luat os dev board

Power Supply used.

USB

What is the expected behavior?

gpio_isr_register registers an ISR which works as expected

What is the actual behavior?

The app crashed

Steps to reproduce.

  1. Create a project from https://github.com/espressif/esp-idf/tree/release/v5.1/examples/peripherals/gpio/generic_gpio
  2. Replace
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
    gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);

    with

    gpio_isr_register(gpio_isr_handler, (void*) GPIO_INPUT_IO_1, ESP_INTR_FLAG_DEFAULT, NULL);
  3. Run the app on esp32-c3. The app crashed on interrupt

Debug Logs.

cnt: 30
cnt: 31
cnt: 32
E (37410) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (37410) task_wdt:  - IDLE (CPU 0)
E (37410) task_wdt: Tasks currently running:
E (37410) task_wdt: CPU 0: gpio_task_examp
E (37410) task_wdt: Print CPU 0 (current core) registers
Core A6 register dump:
MEPC    : 0x403826ca  RA      : 0x40385edc  SP      : 0x3fc90b20  GP      : 0x3fc8b200  
0x403826ca: esp_crosscore_int_send_yield at /home/okhsunrog/esp/esp-idf/components/esp_system/crosscore_int.c:158

0x40385edc: vPortYield at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:404 (discriminator 3)

TP      : 0x3fc884b0  T0      : 0x00000000  T1      : 0x00000000  T2      : 0x00000000  
S0/FP   : 0x3fc90350  S1      : 0x00000000  A0      : 0x00000001  A1      : 0x00000001  
A2      : 0x0000000f  A3      : 0x00000004  A4      : 0x00000001  A5      : 0x600c0000  
A6      : 0x00000000  A7      : 0x00000000  S2      : 0x00000001  S3      : 0x3fc90b7c  
S4      : 0x00000000  S5      : 0x00000000  S6      : 0x00000000  S7      : 0x00000000  
S8      : 0x00000000  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000  
T3      : 0x00000000  T4      : 0x00000000  T5      : 0x00000000  T6      : 0x00000000  
MSTATUS : 0x40385d20  MTVEC   : 0x00000000  MCAUSE  : 0x3fc90350  MTVAL   : 0x403844e0  
0x40385d20: vPortTaskWrapper at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:198

0x403844e0: xQueueReceive at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/queue.c:1603

MHARTID : 0x3fc884b0  
E (42410) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (42410) task_wdt:  - IDLE (CPU 0)
E (42410) task_wdt: Tasks currently running:
E (42410) task_wdt: CPU 0: gpio_task_examp
E (42410) task_wdt: Print CPU 0 (current core) registers
Core A6 register dump:
MEPC    : 0x403826ca  RA      : 0x40385edc  SP      : 0x3fc90b20  GP      : 0x3fc8b200  
0x403826ca: esp_crosscore_int_send_yield at /home/okhsunrog/esp/esp-idf/components/esp_system/crosscore_int.c:158

0x40385edc: vPortYield at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:404 (discriminator 3)

TP      : 0x3fc884b0  T0      : 0x00000000  T1      : 0x00000000  T2      : 0x00000000  
S0/FP   : 0x3fc90350  S1      : 0x00000000  A0      : 0x00000001  A1      : 0x00000001  
A2      : 0x0000000f  A3      : 0x00000004  A4      : 0x00000001  A5      : 0x600c0000  
A6      : 0x00000000  A7      : 0x00000000  S2      : 0x00000001  S3      : 0x3fc90b7c  
S4      : 0x00000000  S5      : 0x00000000  S6      : 0x00000000  S7      : 0x00000000  
S8      : 0x00000000  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000  
T3      : 0x00000000  T4      : 0x00000000  T5      : 0x00000000  T6      : 0x00000000  
MSTATUS : 0x40385d20  MTVEC   : 0x00000000  MCAUSE  : 0x3fc90350  MTVAL   : 0x403844e0  
0x40385d20: vPortTaskWrapper at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:198

0x403844e0: xQueueReceive at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/queue.c:1603

MHARTID : 0x3fc884b0  
E (47410) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (47410) task_wdt:  - IDLE (CPU 0)
E (47410) task_wdt: Tasks currently running:
E (47410) task_wdt: CPU 0: gpio_task_examp
E (47410) task_wdt: Print CPU 0 (current core) registers
Core A6 register dump:
MEPC    : 0x403826ca  RA      : 0x40385edc  SP      : 0x3fc90b20  GP      : 0x3fc8b200  
0x403826ca: esp_crosscore_int_send_yield at /home/okhsunrog/esp/esp-idf/components/esp_system/crosscore_int.c:158

0x40385edc: vPortYield at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:404 (discriminator 3)

TP      : 0x3fc884b0  T0      : 0x00000000  T1      : 0x00000000  T2      : 0x00000000  
S0/FP   : 0x3fc90350  S1      : 0x00000000  A0      : 0x00000001  A1      : 0x00000001  
A2      : 0x0000000f  A3      : 0x00000004  A4      : 0x00000001  A5      : 0x600c0000  
A6      : 0x00000000  A7      : 0x00000000  S2      : 0x00000001  S3      : 0x3fc90b7c  
S4      : 0x00000000  S5      : 0x00000000  S6      : 0x00000000  S7      : 0x00000000  
S8      : 0x00000000  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000  
T3      : 0x00000000  T4      : 0x00000000  T5      : 0x00000000  T6      : 0x00000000  
MSTATUS : 0x40385d20  MTVEC   : 0x00000000  MCAUSE  : 0x3fc90350  MTVAL   : 0x403844e0  
0x40385d20: vPortTaskWrapper at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:198

0x403844e0: xQueueReceive at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/queue.c:1603

MHARTID : 0x3fc884b0  
E (52410) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (52410) task_wdt:  - IDLE (CPU 0)
E (52410) task_wdt: Tasks currently running:
E (52410) task_wdt: CPU 0: gpio_task_examp
E (52410) task_wdt: Print CPU 0 (current core) registers
Core A6 register dump:
MEPC    : 0x403826ca  RA      : 0x40385edc  SP      : 0x3fc90b20  GP      : 0x3fc8b200  
0x403826ca: esp_crosscore_int_send_yield at /home/okhsunrog/esp/esp-idf/components/esp_system/crosscore_int.c:158

0x40385edc: vPortYield at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:404 (discriminator 3)

TP      : 0x3fc884b0  T0      : 0x00000000  T1      : 0x00000000  T2      : 0x00000000  
S0/FP   : 0x3fc90350  S1      : 0x00000000  A0      : 0x00000001  A1      : 0x00000001  
A2      : 0x0000000f  A3      : 0x00000004  A4      : 0x00000001  A5      : 0x600c0000  
A6      : 0x00000000  A7      : 0x00000000  S2      : 0x00000001  S3      : 0x3fc90b7c  
S4      : 0x00000000  S5      : 0x00000000  S6      : 0x00000000  S7      : 0x00000000  
S8      : 0x00000000  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000  
T3      : 0x00000000  T4      : 0x00000000  T5      : 0x00000000  T6      : 0x00000000  
MSTATUS : 0x40385d20  MTVEC   : 0x00000000  MCAUSE  : 0x3fc90350  MTVAL   : 0x403844e0  
0x40385d20: vPortTaskWrapper at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:198

0x403844e0: xQueueReceive at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/queue.c:1603

MHARTID : 0x3fc884b0  
E (57410) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (57410) task_wdt:  - IDLE (CPU 0)
E (57410) task_wdt: Tasks currently running:
E (57410) task_wdt: CPU 0: gpio_task_examp
E (57410) task_wdt: Print CPU 0 (current core) registers
Core A6 register dump:
MEPC    : 0x403826ca  RA      : 0x40385edc  SP      : 0x3fc90b20  GP      : 0x3fc8b200  
0x403826ca: esp_crosscore_int_send_yield at /home/okhsunrog/esp/esp-idf/components/esp_system/crosscore_int.c:158

0x40385edc: vPortYield at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:404 (discriminator 3)

TP      : 0x3fc884b0  T0      : 0x00000000  T1      : 0x00000000  T2      : 0x00000000  
S0/FP   : 0x3fc90350  S1      : 0x00000000  A0      : 0x00000001  A1      : 0x00000001  
A2      : 0x0000000f  A3      : 0x00000004  A4      : 0x00000001  A5      : 0x600c0000  
A6      : 0x00000000  A7      : 0x00000000  S2      : 0x00000001  S3      : 0x3fc90b7c  
S4      : 0x00000000  S5      : 0x00000000  S6      : 0x00000000  S7      : 0x00000000  
S8      : 0x00000000  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000  
T3      : 0x00000000  T4      : 0x00000000  T5      : 0x00000000  T6      : 0x00000000  
MSTATUS : 0x40385d20  MTVEC   : 0x00000000  MCAUSE  : 0x3fc90350  MTVAL   : 0x403844e0  
0x40385d20: vPortTaskWrapper at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:198

0x403844e0: xQueueReceive at /home/okhsunrog/esp/esp-idf/components/freertos/FreeRTOS-Kernel/queue.c:1603

MHARTID : 0x3fc884b0

More Information.

I found the issue a day before with my project, using esp-idf installed manually, from cli. Today I removed esp-idf and installed fresh one in VS Code, tested with an official gpio example. Interrrupts work fine with gpio_isr_handler_add but everithing breaks with gpio_isr_register. Maybe I'm not using it the right way? There aren't any examples for this function

igrr commented 1 year ago

It's a bit difficult to tell what the issue is without seeing the source code of your gpio_isr_handler function.

Perhaps your gpio_isr_handler doesn't clear the GPIO interrupt status register? This would lead to the interrupt triggering again and again, until the system is reset by the interrupt watchdog timer.

okhsunrog commented 1 year ago

Oh, so I should clear it manually? in this example https://github.com/espressif/esp-idf/tree/release/v5.1/examples/peripherals/gpio/generic_gpio it's just like this:

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
        xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
okhsunrog commented 1 year ago

that's the full code I was testing with

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/gpio_filter.h"

#define GPIO_OUTPUT_IO_0    CONFIG_GPIO_OUTPUT_0
#define GPIO_OUTPUT_IO_1    CONFIG_GPIO_OUTPUT_1
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))

#define GPIO_INPUT_IO_0     CONFIG_GPIO_INPUT_0
#define GPIO_INPUT_IO_1     CONFIG_GPIO_INPUT_1
#define GPIO_INPUT_PIN_SEL  ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))

#define ESP_INTR_FLAG_DEFAULT 0

static QueueHandle_t gpio_evt_queue = NULL;

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
        xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

static void gpio_task_example(void* arg)
{
    uint32_t io_num;
    for(;;) {
        if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
            printf("GPIO[%"PRIu32"] intr, val: %d\n", io_num, gpio_get_level(io_num));
        }
    }
}

void app_main(void)
{
    gpio_config_t io_conf = {};
    io_conf.intr_type = GPIO_INTR_DISABLE;
    io_conf.mode = GPIO_MODE_OUTPUT;
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 0;
    gpio_config(&io_conf);

    io_conf.intr_type = GPIO_INTR_POSEDGE;
    io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);

    gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);

    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

    gpio_isr_register(gpio_isr_handler, (void*) GPIO_INPUT_IO_1, ESP_INTR_FLAG_DEFAULT, NULL);

    int cnt = 0;
    while(1) {
        printf("cnt: %d\n", cnt++);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2);
        gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
    }
}
igrr commented 1 year ago

Oh, so I should clear it manually? in this example https://github.com/espressif/esp-idf/tree/release/v5.1/examples/peripherals/gpio/generic_gpio it's just like this:

The example uses per-GPIO handlers, via gpio_install_isr_service and gpio_isr_handler_add.

gpio_install_isr_service already implements things such as clearing interrupt status, therefore the handler code in the application is simpler.

https://github.com/espressif/esp-idf/blob/d2471b11e78fb0af612dfa045255ac7fe497bea8/components/driver/gpio/gpio.c#L447-L470

If you implement the whole ISR yourself, then you have write this code yourself.

okhsunrog commented 1 year ago

Thanks! Now I see where is the problem. I'd be really grateful if you show me a working example of an ISR for gpio_isr_register.

songruo commented 1 year ago

Hi @okhsunrog, may I ask your reason why you want to use gpio_isr_register, instead of calling gpio_install_isr_service, which helps to clear the interrupt status bits internally inside the default ISR?

I have seen the difficulty in using gpio_isr_register, since the driver doesn't provide public functions to get and clear the GPIO interrupt status in the first place (and probably not a good idea to public these two functions). A bit hacky way is to make use of the HAL functions to get and clear the GPIO interrupt status directly in your application.