ESP32 GPIO Interrupts Missing

BivvyCoder
Posts: 10
Joined: Sun Jan 28, 2024 5:20 pm

ESP32 GPIO Interrupts Missing

Postby BivvyCoder » Thu Oct 03, 2024 10:40 am

I'm working on a problem encountered with a interrupt handler that should trigger on rising and falling edges on a gpio pin configured as as an input.
The original code appeared to be missing some edges, so I've stripped it down to the bare minimum and wired up the ESP32 to 1kHz square wave generator to try to rule out as many external factors as possible.

Here's the waveform recorded on my scope:
IMG20241003111456.jpg
IMG20241003111456.jpg (638.89 KiB) Viewed 422 times
The yellow trace is the 1kHz square wave from the signal generator
The blue trace is from an ESP32 pin configured as an output which should toggle on each edge of the square wave.

For some reason I'm missing 1-3 edges in what appears to be a very regular pattern.

Here's the code (Yes - I know I've programmed the GPIO input and output ports in different ways...)

Code: Select all

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_log.h"

// Define GPIO pins for the encoder
#define ENCODER_PIN_A GPIO_NUM_5
#define OUTPUT_PIN_A GPIO_NUM_7
#define DEBOUNCE_DELAY 10 // 100 us debounce delay

volatile uint32_t lastDebounceTime = 0;
volatile int outputValue=0;

void IRAM_ATTR handleEncoder(void* pvArg) {
    uint32_t currentTime = esp_timer_get_time(); // Get current time in us
    if ((currentTime - lastDebounceTime) > DEBOUNCE_DELAY) {
        if (outputValue++ >1) outputValue=0;
        gpio_set_level(OUTPUT_PIN_A,outputValue);
        lastDebounceTime = currentTime;
    }
}


void app_main() {
    [Codebox=c file=Untitled.c][/Codebox]// Configure GPIO
    gpio_config_t io_conf;
    io_conf.intr_type = GPIO_INTR_POSEDGE;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pin_bit_mask = (1ULL << ENCODER_PIN_A) ;
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 0;
    gpio_config(&io_conf);

    gpio_reset_pin(OUTPUT_PIN_A);
    gpio_set_direction(OUTPUT_PIN_A,GPIO_MODE_OUTPUT);

    // Install ISR service
    gpio_install_isr_service(0);
    gpio_isr_handler_add(ENCODER_PIN_A, handleEncoder, (void*) ENCODER_PIN_A);

    while (1) {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

I've experimented with changing / removing the debounce, and searched for answers - most of which refer to slow rise/fall times on the input signal but I've ruled that out by using the signal generator.

Any suggestions on what I'm doing wrong or how to fix this very much appreciated

Thanks
Last edited by BivvyCoder on Thu Oct 03, 2024 12:01 pm, edited 1 time in total.

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

Re: ESP32 GPIO Interrupts Missing

Postby MicroController » Thu Oct 03, 2024 11:17 am

To capture both edges, use GPIO_INTR_ANYEDGE instead of GPIO_INTR_POSEDGE.

And instead of

Code: Select all

if (outputValue++ >1) outputValue=0;
gpio_set_level(OUTPUT_PIN_A,outputValue);
Try

Code: Select all

outputValue = outputValue ^ 1;
gpio_set_level(OUTPUT_PIN_A,outputValue);

BivvyCoder
Posts: 10
Joined: Sun Jan 28, 2024 5:20 pm

Re: ESP32 GPIO Interrupts Missing

Postby BivvyCoder » Thu Oct 03, 2024 12:00 pm

Thanks
In my attempt to get to the bottom of the problem I tried POS / NEG / ANY and had forgotten to change the code back before I posted.
With ANYEDGE I'm still getting missed interrupts. Very repeatable - every third edge is missed.
IMG20241003125657.jpg
IMG20241003125657.jpg (426.79 KiB) Viewed 363 times

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

Re: ESP32 GPIO Interrupts Missing

Postby MicroController » Thu Oct 03, 2024 12:39 pm

You're not missing interrupts. Your handler code just doesn't toggle the output pin on one interrupt out of every three.

BivvyCoder
Posts: 10
Joined: Sun Jan 28, 2024 5:20 pm

Re: ESP32 GPIO Interrupts Missing

Postby BivvyCoder » Thu Oct 03, 2024 12:54 pm

I'm sorry - I don't understand why that should happen.
The original code was handling a rotary shaft encoder - this was miss-counting the number of revolutions of the shaft, which also appeared to be because the interrupt handler was not being called.

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

Re: ESP32 GPIO Interrupts Missing

Postby MicroController » Thu Oct 03, 2024 1:22 pm

To efficiently count edges, you may want to look into the Pulse Counter peripheral.

BivvyCoder
Posts: 10
Joined: Sun Jan 28, 2024 5:20 pm

Re: ESP32 GPIO Interrupts Missing

Postby BivvyCoder » Thu Oct 03, 2024 5:52 pm

Thanks - I've used this code as a starting point and now have a reasonably consistent pulse count per revolution
https://github.com/espressif/esp-idf/tr ... ry_encoder

Still puzzled why the interrupts didn't work and I need to think hard about how the counters operate, particularly these four lines which handle the two directions of the rotary encoder

Code: Select all

    ESP_LOGI(TAG, "set edge and level actions for pcnt channels");
    ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_a, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
    ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
    ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE));
    ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
Thanks again for your support.

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

Re: ESP32 GPIO Interrupts Missing

Postby MicroController » Thu Oct 03, 2024 7:15 pm

To get the maximum resolution out of an encoder the example counts all 4 edges that occur for each full step of the encoder.
To do this, it sets up two channels. One channel counts the edges of signal A and uses the corresponding level of signal B at every edge of A to determine the direction. The other channel counts the edges of B and uses A for direction information.
Because, without a change in rotational direction, there is always a level change on signal B (A) between two edges on signal A (B), the rising edge of A (B) will always see the opposite level of B (A) than the falling edge of A (B), so for one edge of A (B) the interpretation of B (A) needs to be the inverse of what is used for the opposite edge of A (B).

Who is online

Users browsing this forum: Bing [Bot] and 42 guests