Page 1 of 1

Calling gptimer_start() from ISR crashes. Why?

Posted: Wed Sep 13, 2023 9:35 am
by vritzka
Hello,

I just spent 4 hrs trying to figure this out, but I don't know why my code crashes. Could someone please have a look?

I have a Pulsecounter running, reading pulses from GPIO21.
Also on GPIO21 is an Interrupt (positive edge).

The interrupt handler is supposed to start a timer.
However, I cannot start the timer.
It keeps crashing with error gptimer: gptimer_start(337): timer is not enabled yet
- I have created and enabled the timer.
- I enabled timer controls in IRAM and gptimer IRAM safe in menuconfig.
- I enabled gpio controls in IRAM and gpio IRAM safe in menuconfig.

Perhaps problem lies where I pass the timer handle to the interrupt handler: (LINE 12)

  1.    
  2. gpio_config_t io_conf = {};
  3. //interrupt of rising edge
  4.     io_conf.intr_type = GPIO_INTR_POSEDGE;
  5.     io_conf.pin_bit_mask = (1ULL<<GPIO_NUM_21);
  6.     //set as input mode
  7.     io_conf.mode = GPIO_MODE_INPUT;
  8.     //enable pull-down mode
  9.     io_conf.pull_down_en = 1;
  10.     gpio_config(&io_conf);    
  11.     gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
  12.     gpio_isr_handler_add(GPIO_NUM_21, gpio_isr_handler, &gpfiltertimer);

this is my interrupt handler:

  1. static void IRAM_ATTR gpio_isr_handler(void* arg)
  2. {
  3.     gptimer_handle_t gpfiltertimer = (gptimer_handle_t) arg;
  4.     gptimer_start(gpfiltertimer);   /////// CRASH: gptimer: gptimer_start(337): timer is not enabled yet
  5.     gpio_set_level(45, 1);
  6. }

All the code:

  1.  
  2. #include <math.h>
  3. #include "esp_err.h"
  4. #include "esp_log.h"
  5. #include "esp_system.h"
  6. #include "esp_timer.h"
  7. #include "freertos/FreeRTOS.h"
  8. #include "freertos/semphr.h"
  9. #include "freertos/task.h"
  10. #include "freertos/queue.h"
  11. #include "lvgl.h"
  12. #include <stdio.h>
  13. #include "driver/gpio.h"
  14. #include "driver/pulse_cnt.h"
  15. #include "driver/gptimer.h"
  16. #include "ui.h"
  17. #include "generics.h"
  18.  
  19. void rRpmCounter(void);
  20. void vStartRpmCounterTask(void);
  21. extern void init_rpm_counter();
  22.  
  23. static const char *TAG = "pulse counter";
  24. TaskHandle_t xRpmCounterTaskHandle;
  25.  
  26. static pcnt_unit_handle_t pcnt_unit = NULL;
  27. static pcnt_channel_handle_t pcnt_chan_a = NULL;
  28. static gptimer_handle_t gpfiltertimer = NULL;
  29.  
  30.  
  31.  
  32. static bool IRAM_ATTR filter_timer_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
  33. {
  34.     BaseType_t high_task_awoken = pdFALSE;
  35.     gpio_set_level(45, 0);
  36.    
  37.     return (high_task_awoken == pdTRUE);
  38. }
  39.  
  40.  
  41. static void IRAM_ATTR gpio_isr_handler(void* arg)
  42. {
  43.     gpfiltertimer = (gptimer_handle_t) arg;
  44.     //ESP_ERROR_CHECK(gptimer_start(gpfiltertimer));   /////// CRASH: gptimer: gptimer_start(337): timer is not enabled yet
  45.     gpio_set_level(45, 1);
  46. }
  47.  
  48. void vRpmCounter() {
  49.    
  50.     vTaskDelay(pdMS_TO_TICKS(500));
  51.        
  52.     ESP_LOGI(TAG, "install pcnt unit");
  53.     pcnt_unit_config_t unit_config = {
  54.         .high_limit = 6000,
  55.         .low_limit = -10,
  56.         .flags.accum_count = 0,
  57.     };
  58.    
  59.     ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &pcnt_unit));
  60.  
  61.     ESP_LOGI(TAG, "set glitch filter");
  62.     pcnt_glitch_filter_config_t filter_config = {
  63.         .max_glitch_ns = 10000,
  64.     };
  65.     ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_unit, &filter_config));
  66.  
  67.     ESP_LOGI(TAG, "install pcnt channels");
  68.     pcnt_chan_config_t chan_a_config = {
  69.         .edge_gpio_num = 21,
  70.         .level_gpio_num = 45,
  71.     };
  72.    
  73.     ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_a_config, &pcnt_chan_a));
  74.  
  75.     ESP_LOGI(TAG, "set edge and level actions for pcnt channels");
  76.    
  77.     ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_a, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
  78.     ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_a, PCNT_CHANNEL_LEVEL_ACTION_HOLD, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
  79.  
  80.  
  81.     ESP_LOGI(TAG, "enable pcnt unit");
  82.     ESP_ERROR_CHECK(pcnt_unit_enable(pcnt_unit));
  83.     ESP_LOGI(TAG, "clear pcnt unit");
  84.     ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit));
  85.     ESP_LOGI(TAG, "start pcnt unit");
  86.     ESP_ERROR_CHECK(pcnt_unit_start(pcnt_unit));
  87.    
  88.     vTaskDelay(pdMS_TO_TICKS(500));
  89.    
  90.    
  91.     ESP_LOGI(TAG, "Create RPM Filter timer handle");
  92.     gptimer_config_t filter_timer_config = {
  93.         .clk_src = GPTIMER_CLK_SRC_DEFAULT,
  94.         .direction = GPTIMER_COUNT_UP,
  95.         .resolution_hz = 1000000, // 1MHz, 1 tick=1us
  96.     };
  97.     ESP_ERROR_CHECK(gptimer_new_timer(&filter_timer_config, &gpfiltertimer));
  98.  
  99.     gptimer_event_callbacks_t filter_cbs = {
  100.         .on_alarm = filter_timer_cb,
  101.     };
  102.     ESP_ERROR_CHECK(gptimer_register_event_callbacks(gpfiltertimer, &filter_cbs, NULL));
  103.  
  104.     ESP_ERROR_CHECK(gptimer_enable(gpfiltertimer));
  105.  
  106.     gptimer_alarm_config_t filter_alarm_config = {
  107.         .alarm_count = 10000, //1000000 = 1s
  108.     };
  109.     ESP_ERROR_CHECK(gptimer_set_alarm_action(gpfiltertimer, &filter_alarm_config));
  110.    
  111.     vTaskDelay(pdMS_TO_TICKS(500));
  112.    
  113.    
  114.    gpio_config_t io_conf = {};
  115.  
  116.     //interrupt of rising edge
  117.     io_conf.intr_type = GPIO_INTR_POSEDGE;
  118.     io_conf.pin_bit_mask = (1ULL<<GPIO_NUM_21);
  119.     //set as input mode
  120.     io_conf.mode = GPIO_MODE_INPUT;
  121.     //enable pull-down mode
  122.     io_conf.pull_down_en = 1;
  123.     gpio_config(&io_conf);    
  124.     gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
  125.     gpio_isr_handler_add(GPIO_NUM_21, gpio_isr_handler, &gpfiltertimer);  ///// SETTING THE GPIO INTERRUPT
  126.    
  127.      
  128.     uint32_t rpm;
  129.     while (1) {
  130.         if (xQueueReceive(queue, &ele, pdMS_TO_TICKS(1000))) {
  131.                
  132.            // This Queue is used by another timer which I have left out for brevity
  133.         }  else {
  134.             //my_toba.rpm = 0;    
  135.         }
  136.     }
  137. }
  138.  
  139. void vStartRpmCounterTask(void) {
  140.   xTaskCreate(vRpmCounter, "RPM", 4096, NULL, 1, &xRpmCounterTaskHandle);
  141.   configASSERT(xRpmCounterTaskHandle);
  142. }

Re: Calling gptimer_start() from ISR crashes. Why?

Posted: Wed Sep 13, 2023 3:04 pm
by MicroController
Perhaps problem lies where I pass the timer handle to the interrupt handler:
Yup. The curse of void-pointers ;-)
You have the driver pass a pointer to your timer handle as argument to the ISR (&gpfiltertimer), but in the ISR you cast that value directly to a timer handle: (gptimer_handle_t) arg;
Change the ISR to

Code: Select all

gptimer_handle_t gpfiltertimer = *((gptimer_handle_t*) arg);
(Since the original gpfiltertimer is a global variable anyway, the ISR could also just use it directly in this case, i.e. not using the void* arg at all.)

Re: Calling gptimer_start() from ISR crashes. Why?

Posted: Thu Sep 14, 2023 12:33 am
by vritzka
Thank you!

It still doesn't work tho :)

I changed my ISR. And also just used the global var.
Screenshot 2023-09-14 at 8.29.36 am.png
Screenshot 2023-09-14 at 8.29.36 am.png (271.49 KiB) Viewed 2231 times
Then I moved the ISR out of the IRAM. No avail.

Maybe my interrupts are coming in too fast at 10HZ?

Perhaps something to do with stack rise of interrupts?

Any other idea, please let me know.

I will now try the other timer, High resolution timer.

Thanks again

Re: Calling gptimer_start() from ISR crashes. Why?

Posted: Thu Sep 14, 2023 4:25 am
by vritzka
I solved it by using the High Resolution timer like:
  1.     const esp_timer_create_args_t oneshot_timer_args = {
  2.             .callback = &oneshot_timer_callback,
  3.             .name = "one-shot"
  4.     };
  5.    
  6.      ESP_ERROR_CHECK(esp_timer_create(&oneshot_timer_args, &oneshot_timer));
  7.  
  8.      esp_timer_start_once(oneshot_timer, 10000);
  9.  
  10.  
  11. static void oneshot_timer_callback(void* arg)
  12. {
  13.     gpio_set_level(GPIO_NUM_47, 0);
  14. }
  15.