Counting pulses on ESP32 with ESP-IDF - Page 1
Forum section: electronics / general

author: reyntjensm
author_rank: Regular Contributor
timestamp: October 02, 2023, 09:17:04 pm
content: I want to measure incoming GPIO pulses and the time between each pulse. Max pulse rate is 15000 pulses/min so it's relatively slow. I already made the code for this with another micro controller and this is working fine with a simple polling strategy and interrupts. Right now i would like to use an ESP32 with freertos. As i don't have much experience with freertos i can't figure out what would be the best solution for this.I'm using the PCNT functionalities on the ESP32 to count the pulses. But i still need to give every pulse a timestamp. I guess using timers would be a better solution to this?What is the best way to signal my task (this task prints data to the terminal) for a new pulse/data available?

author: PlainName
author_rank: Super Contributor
timestamp: October 02, 2023, 09:40:56 pm
content: Are you triggering an interrupt with the GPIO? If so I think your best option in the ISR handler is to use xTaskNotifyFromISR() to let your task know there's something happening, and xTaskNotifyWait() in the task to wait for the signal.The time when you hit the ISR is more important than when the task runs, so these functions allow you to pass a 32-bit value, which can be the time offset in whatever scale you want to use. You can do stuff like reserve a bit in the notification value (so use 31-bits for the time) and use that to notify either end if the logging task missed a notify call.Timing: the standard FreeRTOS clock might not have enough resolution for your purposes, but if it does then the built-in TickType_t has useful functions and is reasonable slim. I though there was a higher-res clock source but I don't have access to the dox at the moment, so might be thinking of a different processor! Either way, you might need to trim the top bits to get everything into the 32-bit notify value. Also note that the task tick is pretty slow on the ESP-IDF, and that might affect how quickly the log task can deal with the ISR notify, although at 40ms between interrupts it should manage.

author: reyntjensm
author_rank: Regular Contributor
timestamp: October 02, 2023, 11:58:07 pm
content: Quote from: PlainName on October 02, 2023, 09:40:56 pmI'm not using interrupts right now. The PCNT module is some specific hardware used for counting pulses... It increments a certain register without interrupting the whole system. It's pretty useful for my application as it has glitch filters built in.https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32/api-reference/peripherals/pcnt.html#

author: nctnico
author_rank: Super Contributor
timestamp: October 03, 2023, 12:08:50 am
content: If the PCNT has a driver that can create a callback when an event happens, you can use esp_timer_impl_get_counter_reg() to get a 64 bit monotonic timestamp (you have to divide by 16000 to get milli-seconds on the ESP32S3).Another option is to use a timer interrupt callback and do polling from there:https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gptimer.html

author: PlainName
author_rank: Super Contributor
timestamp: October 03, 2023, 08:27:59 am
content: QuoteNot used it but had a quick look at your link. Your problem isn't the counting but timestamping, and the reason you want to do that determines how you could use this. For instance, if you don't need to log every transition but could go with an average, you could get the PCNT to interrrupt after, say, 5 transitions to trigger your log task. The interrupt times would then have a better average accuracy than doing them individually (actually, it would remove jitter caused by capture resolution), but the downside is it would be an average over 200ms.Re interrupts: I don't think you will be able to timestamp usefully unless you use interrupts but, again, it depends on why you want the timestamps. Perhaps you could expand on that?

author: reyntjensm
author_rank: Regular Contributor
timestamp: October 03, 2023, 07:49:02 pm
content: It's should be pretty straight forward. I have a wheel with some material running over it. 2 magnets in the wheel and a hall effect sensor. I want to plot speed variations so i need a timestamp for every pulse.

author: PlainName
author_rank: Super Contributor
timestamp: October 03, 2023, 10:43:51 pm
content: QuoteOK. I would suggest interrupts and something like the following (note that there are things you need to determine, such as the values of TASK_STAT_SIZE_CNT and TASK_PRIORITY_CNT,, and COUNT_IO which is the port number of your input pin. This is also completely untested, not even compiled, but should illustrate one way of doing it:Code:#define COUNT_IO (00) // Input port nummbervoidcountInit(void){xTaskCreate(CountTask, /* Pointer to task function */(const char * const)"CNT_TASK", /* Text name of task */TASK_STACK_SIZE_CNT, /* Stack size for this task (in words) */NULL, /* Task parameter(s) (not used) */TASK_PRIORITY_CNT, /* Task priority */NULL); /* Handle of created task (not used) */gpio_config_t pin_config = {.pin_bit_mask = BIT64(COUNT_IO),.mode = GPIO_MODE_INPUT,.pull_up_en = GPIO_PULLUP_ENABLE,.intr_type = GPIO_PIN_INTR_POSEDGE,};ESP_ERROR_CHECK(gpio_config(&pin_config));gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);gpio_isr_handler_add(COUNT_IO, cnt_gpio_isr_handler, (void *)COUNT_IO);}//---------------------------------------------------------------------------static void IRAM_ATTRcnt_gpio_isr_handler(void *arg){BaseType_t xHigherPriorityTaskWoken = pdFALSE;uint32_t timestamp = (uint32_t)<TBD>;xTaskNotifyFromISR(rngTaskHandle, timestamp, eSetBits, &xHigherPriorityTaskWoken);if (xHigherPriorityTaskWoken == pdTRUE) {portYIELD_FROM_ISR();}}//---------------------------------------------------------------------------voidCountTask(void *params){uint32_t event_time;while (1) {xTaskNotifyWait(0, -1, &event_time, portMAX_DELAY);/*Deal with received interrupt timestamp*/}}