ESP IDF get GPIO level at time of interrupt

Xtensa2C
Posts: 15
Joined: Sat May 30, 2020 2:39 pm

ESP IDF get GPIO level at time of interrupt

Postby Xtensa2C » Sat May 30, 2020 3:32 pm

Hello,

I just wanted to quickly add a button with some timed debounce logic to my project when I came across a kind of unusal behavior.
To debounce my button I wanted an input pin to trigger at every edge and then read the logic level.
If the level is HIGH then it must be a LOW-HIGH edge and when the level is LOW it must be a HIGH-LOW edge as far as my simple logic goes.

Therefore when I simply print out the pin level at the time an interrupt has been invoked it should be something like 101010101010101 because auf the button bounce.
Unfortunately it looks more like 101000101111011001011100. This is kind of unexpected since I am triggering on the edges so there must be a change in logic levels. My guess would be that the signal ist too fast and changes between the interrupt invokation and the read out of the digital pin.
But is there a workaround for that? Maybe a way to freeze the input registers for the time of the interrupt?

Here is some code to reproduce the problem:
[EDIT]: The code below provides a rudimentary digital output with 0.5 HZ. If the output pin is connected to the button input pin the signal looks like the expected 101010... But if the actual button is used instead of the digital signal the problem occurs due to the fast bounces.
  1. #include <string.h>
  2. #include "time.h"
  3. #include "freertos/FreeRTOS.h"
  4. #include "freertos/task.h"
  5. #include "freertos/queue.h"
  6. #include "esp_err.h"
  7. #include "esp_log.h"
  8. #include "driver/gpio.h"
  9.  
  10.  
  11. static const int btn_number = 32;
  12. static const int sig_number = 33;
  13. QueueHandle_t test_queue_handle = NULL;
  14. int btn_lvl;
  15. static const char *TAG = "button";  
  16. static bool ready = false;
  17. static bool output = false;
  18.  
  19. static void IRAM_ATTR gpio_isr_handler(void* arg)
  20. {
  21.     int btn = *(int*) arg;
  22.     int current_level = gpio_get_level(btn);
  23.     BaseType_t ContextSwitchRequest = pdFALSE;
  24.  
  25.     xQueueSendToBackFromISR(test_queue_handle,(void*)&current_level,&ContextSwitchRequest);
  26.    
  27.     if(ContextSwitchRequest){
  28.         taskYIELD();
  29.     }
  30.  
  31. }
  32.  
  33. static void main_task(void *arg){
  34.  
  35.  
  36.     gpio_install_isr_service(0);
  37.     test_queue_handle = xQueueCreate(10,sizeof(btn_lvl));
  38.  
  39.     gpio_config_t io_conf;
  40.     io_conf.intr_type = GPIO_PIN_INTR_ANYEDGE;
  41.     io_conf.mode = GPIO_MODE_INPUT;
  42.     io_conf.pull_down_en = false;
  43.     io_conf.pull_up_en = false;
  44.     io_conf.pin_bit_mask = (1ULL<<btn_number);
  45.     gpio_config(&io_conf);
  46.  
  47.     io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
  48.     io_conf.mode = GPIO_MODE_OUTPUT;
  49.     io_conf.pull_down_en = false;
  50.     io_conf.pull_up_en = false;
  51.     io_conf.pin_bit_mask = (1ULL<<sig_number);
  52.     gpio_config(&io_conf);
  53.  
  54.  
  55.     //attach isr handler
  56.     gpio_isr_handler_add(btn_number, gpio_isr_handler, (void*) &btn_number);
  57.     ready = true;
  58.     vTaskDelete(NULL);
  59. }
  60.  
  61. void app_main(void)
  62. {
  63.     xTaskCreate(main_task, "Main Task", 1024 * 2, (void *)0, 10, NULL);
  64.    
  65.     while(1){
  66.         if(ready && uxQueueMessagesWaiting(test_queue_handle)){
  67.             printf("=======================================================\n");
  68.             while(uxQueueMessagesWaiting(test_queue_handle)){
  69.                 int lvl;
  70.                 xQueueReceive(test_queue_handle,(void*)&lvl,10);
  71.                 ESP_LOGI(TAG,"state: %d",lvl);
  72.             }  
  73.         }
  74.         gpio_set_level(sig_number, output);
  75.         output = !output;
  76.         vTaskDelay(pdMS_TO_TICKS(1000));
  77.     }
  78. }
Last edited by Xtensa2C on Sun May 31, 2020 9:18 am, edited 1 time in total.

ESP_Sprite
Posts: 9040
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP IDF get GPIO level at time of interrupt

Postby ESP_Sprite » Sun May 31, 2020 9:15 am

There's no real characterization possible for bounce noise, as it's partially an analog process... all you can know is that it's usually over after a few ms... so best you can do is start a timer in the interrupt (and disable the GPIO interrupt) and read the value of the button when the timer expired. Or just go 'screw it' and have a periodic timer to poll the buttons every 50mS or so.

Xtensa2C
Posts: 15
Joined: Sat May 30, 2020 2:39 pm

Re: ESP IDF get GPIO level at time of interrupt

Postby Xtensa2C » Sun May 31, 2020 9:25 am

Okay got it and that would be my goto solution too but nevertheless it has to be possible to determine if a positive or negative edge triggered the interrupt right? I mean when using ANYEDGE.

Because it is not possible to attach POSEDGE and NEGEDGE to the same pin. So the workaround would be to physically attach the same signal to two digital pins and let one trigger on POSEDGE and the other on NEGEDGE.
But honestly that sounds like a very amateur way to do it.

ESP_Sprite
Posts: 9040
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP IDF get GPIO level at time of interrupt

Postby ESP_Sprite » Sun May 31, 2020 9:27 am

Not if the signal jitters back and forth with a frequency that's higher than the latency between the interrupt and the time the GPIO gets read out, no.

Xtensa2C
Posts: 15
Joined: Sat May 30, 2020 2:39 pm

Re: ESP IDF get GPIO level at time of interrupt

Postby Xtensa2C » Sun May 31, 2020 9:56 am

Well that sounds like a shortcoming.

But technically the edge detection inside the CPU stores the values in a register somewhere and compares them to figure out if an edge occured between cycles.
But I'd guess that it is impossible to read that register quickly enough to get the value after the ISR invokation.
https://0x04.net/~mwk/doc/xtensa.pdf (Page 109)

On the other hand is there a way to get the state of an IO pin faster than with the blown up gpio_get_level() method?

Who is online

Users browsing this forum: djixon, MicroController and 147 guests