espressif / esp-idf

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

gpio_install_isr_service fails if gpio is at ground (IDFGH-11615) #12731

Open juantxorena opened 10 months ago

juantxorena commented 10 months ago

Answers checklist.

IDF version.

v5.1.2

Espressif SoC revision.

ESP32-C6FH4 (QFN32) (revision v0.0)

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-c6-devkitm-1

Power Supply used.

USB

What is the expected behavior?

I want to initialize a interrupt for GPIO, no matter its initial value.

What is the actual behavior?

I have a switch connected to GPIO 14 and ground with an interrupt and a handler. If the switch is closed when booting, everything it works as expected. If it's open (so GPIO will be connected to ground), the watchdog gets triggered.

Steps to reproduce.

Here's the relevant code:

    water_level_event_group = xEventGroupCreate();

    gpio_config_t io_conf;
    io_conf.intr_type = GPIO_INTR_HIGH_LEVEL;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pin_bit_mask = (1ULL << GPIO_WATER_LEVEL_GPIO);
    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
    io_conf.pull_up_en = GPIO_PULLUP_ENABLE;

    gpio_config(&io_conf);
    gpio_wakeup_enable(GPIO_WATER_LEVEL_GPIO, GPIO_INTR_HIGH_LEVEL);

    gpio_install_isr_service(0);
    gpio_isr_handler_add(GPIO_WATER_LEVEL_GPIO, water_level_switch_isr_handler, (void *)water_level_event_group);
    gpio_intr_enable(GPIO_WATER_LEVEL_GPIO);

    xTaskCreate(&water_level_switch_handler, "water_level_switch_handler_task", 4096, NULL, configMAX_PRIORITIES-1, NULL);
    xTaskCreate(&restart_water_level, "restart_water_level", 4096, NULL, configMAX_PRIORITIES-2, NULL);
void IRAM_ATTR water_level_switch_isr_handler(void *arg)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    if (!interrupt_executed) {
        xEventGroupSetBitsFromISR((EventGroupHandle_t)arg, EMPTY_WATER_TANK_EVENT_BIT, &xHigherPriorityTaskWoken);
        interrupt_executed = true;
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
        gpio_intr_disable(GPIO_WATER_LEVEL_GPIO);
    }
}

The line gpio_install_isr_service(0); fails. I put a log after it, and it never logs.

Debug Logs.

E (5425) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (5425) task_wdt:  - IDLE (CPU 0)
E (5425) task_wdt: Tasks currently running:
E (5425) task_wdt: CPU 0: Tmr Svc
E (5425) task_wdt: Print CPU 0 (current core) registers
Core  0 register dump:
MEPC    : 0x4080b9e2  RA      : 0x4080b9ca  SP      : 0x40823760  GP      : 0x40817774  
0x4080b9e2: vPortYield at /home/juantxorena/Projects/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:404
 (inlined by) vPortYield at /home/juantxorena/Projects/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:387

0x4080b9ca: vPortYield at /home/juantxorena/Projects/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:404
 (inlined by) vPortYield at /home/juantxorena/Projects/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c:387

TP      : 0x407fe948  T0      : 0x00000000  T1      : 0x00000000  T2      : 0x00000000  
S0/FP   : 0x4081f000  S1      : 0x4081f000  A0      : 0x00000000  A1      : 0x408237ec  
A2      : 0x00000001  A3      : 0x00000000  A4      : 0x600c5000  A5      : 0x00000001  
A6      : 0x00000000  A7      : 0x00000000  S2      : 0x00000008  S3      : 0x00000005  
S4      : 0x4081f000  S5      : 0x00000009  S6      : 0x00000003  S7      : 0x4081f000  
S8      : 0x00000001  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000  
T3      : 0x00000000  T4      : 0x00000000  T5      : 0x00000000  T6      : 0x00000000  
MSTATUS : 0x00000008  MTVEC   : 0x4081f000  MCAUSE  : 0x4081f000  MTVAL   : 0x4080c5f8  
0x4080c5f8: prvProcessTimerOrBlockTask at /home/juantxorena/Projects/esp/esp-idf/components/freertos/FreeRTOS-Kernel/timers.c:673
 (inlined by) prvTimerTask at /home/juantxorena/Projects/esp/esp-idf/components/freertos/FreeRTOS-Kernel/timers.c:619

MHARTID : 0x00000000

More Information.

Edit Sorry, I put the description wrong, the error happens when the switch is open, so not at ground, not the other way around.

nopnop2002 commented 10 months ago

The second parameter of gpio_isr_handler_add() is an ISR handler function, not a task entry function.

ISR handler function cannot be launched as a task.

https://github.com/espressif/esp-idf/blob/master/examples/peripherals/gpio/generic_gpio/main/gpio_example_main.c

juantxorena commented 10 months ago

Ok, I don't know what does have to do. In the code example that I pasted the second parameter of gpio_isr_handler_add() is a ISR handler function, not a task.

nopnop2002 commented 10 months ago

Learn using this official sample.

https://github.com/espressif/esp-idf/tree/master/examples/peripherals/gpio/generic_gpio

juantxorena commented 10 months ago

I used the official example. As I said in the description, the whole things works perfectly, as long as the switch connected to the GPIO is closed.

songruo commented 10 months ago

Hi @juantxorena, this is probably because the interrupt starts to get triggered before the ISR handler is added. Please try with configure as GPIO_INTR_DISABLE first in the gpio_config. And use gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_HIGH_LEVEL) after gpio_isr_handler_add is called.

juantxorena commented 10 months ago

Hi @songruo, sorry for the delay, I haven't got a lot of time for testing this. Thanks for the suggestion, unfortunately it doesn't help, same exception. Here's the code, maybe I implemented something wrong:

    water_level_event_group = xEventGroupCreate();

    gpio_config_t io_conf;
    //io_conf.intr_type = GPIO_INTR_HIGH_LEVEL;
    io_conf.intr_type = GPIO_INTR_DISABLE;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pin_bit_mask = (1ULL << GPIO_WATER_LEVEL_GPIO);
    io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
    io_conf.pull_up_en = GPIO_PULLUP_DISABLE;

    gpio_config(&io_conf);
    gpio_wakeup_enable(GPIO_WATER_LEVEL_GPIO, GPIO_INTR_HIGH_LEVEL);

    int switch_state = gpio_get_level(GPIO_WATER_LEVEL_GPIO);
    ESP_LOGE(TAG, "Switch state: %d", switch_state);
    water_tank_empty = switch_state;

    gpio_install_isr_service(0);
    gpio_isr_handler_add(GPIO_WATER_LEVEL_GPIO, water_level_switch_isr_handler, (void *)water_level_event_group);
    gpio_intr_enable(GPIO_WATER_LEVEL_GPIO);

    gpio_set_intr_type(GPIO_WATER_LEVEL_GPIO, GPIO_INTR_HIGH_LEVEL);

    xTaskCreate(&water_level_switch_handler, "water_level_switch_handler_task", 3072, NULL, configMAX_PRIORITIES-1, NULL);
    xTaskCreate(&restart_water_level, "restart_water_level", 3072, NULL, configMAX_PRIORITIES-2, NULL);
songruo commented 10 months ago

@juantxorena The problem of your latest code is due to the line of gpio_intr_enable after the gpio_isr_handler_add being called. Indeed, the interrupt has been enabled internally inside the gpio_isr_handler_add function. The interrupt is triggered as soon as the interrupt is enabled, and goes into your ISR, where you disable the interrupt. After it returns from the interrupt, the program starts to do the next line, which is gpio_intr_enable. The interrupt is enabled again, this is undesired, because your ISR only disables interrupt based on the interrupt_executed variable. Now the interrupt gets triggered again and again, and eventually triggers task watchdog.

Overall, you don't need to call an extra gpio_intr_enable after the gpio_isr_handler_add.

juantxorena commented 9 months ago

@songruo sorry for the delay, I was on holidays and I didn't have access to the hardware.

Your suggestion doesn't solve it. I removed the gpio_intr_enable line, and it has the same problem.