ESP32 GPIO Interrupts Missing

BivvyCoder
Posts: 17
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 1696 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: 1734
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: 17
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 1637 times

MicroController
Posts: 1734
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: 17
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: 1734
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: 17
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: 1734
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).

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

Re: ESP32 GPIO Interrupts Missing

Postby BivvyCoder » Sat Nov 09, 2024 5:55 pm

So...
I've managed to get the pulse encoders working, but now I've hit a problem with the (cheap) encoder on the motor I'm using.

Looking at the attached oscilloscope trace
Encoder - Clockwise.jpg
Encoder - Clockwise.jpg (281.43 KiB) Viewed 609 times
The rising edge on channel A and the falling edge of channel B are nicely aligned with the middle of the high level on the other channel.
However the falling edge on Ch A and the rising edge of Ch B are very close together. This is messing up the pulse counting on some of the motors I have as I cannot guarantee which order the edges occur.
Not a problem if I set up the pulse counter to ignore these edges and just use the ones that are clearly separated.

The problem occurs when the motor spins the other way:
Encoder - Anticlockwise.jpg
Encoder - Anticlockwise.jpg (290.7 KiB) Viewed 609 times
Now it's the falling edge of Ch B and the rising edge of Ch A that are aligned.

I can't see a way to handle this using the ESP32 pulse count module.
Previous attempts to use interrupts failed as the interrupts were missed.
I'm open to any suggestions!

Thanks

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

Re: ESP32 GPIO Interrupts Missing

Postby MicroController » Sat Nov 09, 2024 11:24 pm

This is messing up the pulse counting on some of the motors I have as I cannot guarantee which order the edges occur.
I think the PCNT hardware should actually be able to discern the couple of microseconds which appear to still be there between the edges of A and B. (Haven't found definitive info though, only that it's clocked from the APB clock @ 80MHz default.)
What's the actual shortest time you see between the A and B edges?

Maybe the signals' rise/fall times are too slow, and/or there's a bit of noise, and/or some bouncing, causing multiple edges to be detected where there's actually only one.
You can try fiddling with the PCNT's glitch filter, or try e.g. using a Schmitt trigger.

Who is online

Users browsing this forum: No registered users and 42 guests