Page 1 of 1

ESP_Timer or "gpio_isr_handler_add" sometimes gets delayed and interrupt misses rising edge.

Posted: Wed Jan 12, 2022 10:21 pm
by Mahonroy
I am doing some serial communication (1200 baud) on a single GPIO (transmitting and receiving on the same line), and I am running into an issue with the GPIO ISR Handler and the ESP_Timer. I am using the esp_timer for the actual reading of data, and I am using the interrupt for detecting when data has started to be received.

Basically what I am doing is after transmitting some characters, I switch over to receiving, and create a rising edge interrupt. There is a start bit that indicates data is comming. This triggers the rising edge interrupt, so I then remove the interrupt from firing again, and I then reset the esp_timer to 1250 micro seconds so it takes readings directly in the middle of a bit (1.5 bits later). I read in that data bit, and then reset the timer again to 833 micro seconds (1200 baud). I then continue reading the rest of the data, and take in readings exactly in the center of each bit. Each serial character is 10 bits total (1 start bit, 7 data bits, 1 even parity bit, and 1 stop bit). So after reading the bits, I create another rising edge interrupt, and I repeat, until all of the characters are read in.

This works perfectly for the first 2 characters I read in. When getting ready to read in the 3rd character, the interrupt is created like usual, and after it triggers, it resets the clock and reads in a bit. The problem is it always misses the first start bit. To me, it seems to be one of these problems, but I could definately use your expertise in it:
1. Occationally "gpio_isr_handler_add" takes extra long to actually create the interrupt, so it ends up missing the first bit
2. "gpio_isr_handler_add" works like normal, but the ISR Handler delays the execution of the function occationally.
3. It might have something to do with the timer. In order to reset the period of the timer, I call "esp_timer_stop(timer);", then I call "esp_timer_start_periodic(timer, newPeriod);". I wonder if the timer is not stopping and stopping fast enough? It always malfunctions on the 3rd character, and then randomly throughout the remaining message.

The reason behind this is that it allows me to sync up on that 1200 baud rate clock after every 10 bits. I did create a workaround where I don't use the interrupts and don't reset the period of the timer, and instead just continuously read in the data with the 833 micro second timer. Using the oscilliscope I can see that the clock is not exactly 833 micro seconds, so the data ends up getting messed up on longer transmissions. Using the above interrupt method solves this problem since I can resync the clock after every character.

Here is how I am handling the creation of interrupts and resetting timers:

Code: Select all

bool attachInterrupt(uint8_t pin, gpio_isr_t handler, InterruptMode mode)
{
    gpio_config_t gpioConfig;
	gpioConfig.pin_bit_mask = (BIT(pin));
	gpioConfig.mode         = GPIO_MODE_INPUT;
	gpioConfig.pull_up_en   = GPIO_PULLUP_DISABLE;
	gpioConfig.pull_down_en = GPIO_PULLDOWN_DISABLE;

    if(mode == RISING)
    {
        gpioConfig.intr_type    = GPIO_INTR_POSEDGE;
    }else{
        gpioConfig.intr_type    = GPIO_INTR_NEGEDGE;
    }
    
	gpio_config(&gpioConfig);

	// Make sure gpio_install_isr_service(0); is called at some point prior to adding the ISR handle below
    gpio_isr_handler_add((gpio_num_t)pin, handler, NULL	);

    return true;
}

bool detachInterrupt(uint8_t pin)
{
    gpio_isr_handler_remove((gpio_num_t)pin);

    return true;
}

Code: Select all

void IntervalTimer::resetPeriod_SIT(intPeriod newPeriod, bool scale)
{
    esp_timer_stop(timer);
    esp_timer_start_periodic(timer, newPeriod);
}
Any help or advice on the problem is greatly appreciated. It seems to be a problem with the interrupt getting executing fast enough, or the timer not resetting like I'm expecting it to, but I am not really sure.

Re: ESP_Timer or "gpio_isr_handler_add" sometimes gets delayed and interrupt misses rising edge.

Posted: Thu Jan 13, 2022 1:09 am
by ESP_Sprite
Not entirely sure what the specific issue with esp_timer is here, but I think you may be better off rewriting this to use a hardware timer that unblocks your own task... the reason is that all esp_timer callbacks are called from one single task, which means that if e.g. the WiFi driver schedules a callback right before your callback, your callback will get 'pushed back' until the WiFi callback is done. As there are no guidelines on how long esp_timer callbacks can take, there is no guarantee on how long it will get pushed back. If you run your logic in your own task, you can at least increase the priority to 'override' anything else that is going on.

Re: ESP_Timer or "gpio_isr_handler_add" sometimes gets delayed and interrupt misses rising edge.

Posted: Thu Jan 13, 2022 5:08 pm
by Mahonroy
ESP_Sprite wrote:
Thu Jan 13, 2022 1:09 am
Not entirely sure what the specific issue with esp_timer is here, but I think you may be better off rewriting this to use a hardware timer that unblocks your own task... the reason is that all esp_timer callbacks are called from one single task, which means that if e.g. the WiFi driver schedules a callback right before your callback, your callback will get 'pushed back' until the WiFi callback is done. As there are no guidelines on how long esp_timer callbacks can take, there is no guarantee on how long it will get pushed back. If you run your logic in your own task, you can at least increase the priority to 'override' anything else that is going on.
Thank you for the response. I originally did this with a hardware timer (ISR based timer), and was instructed that I should be using the ESP_Timer instead and to not use the ISR based timer:
https://www.esp32.com/viewtopic.php?f=1 ... 437#p89469

So I switched over to ESP_Timer.
I also have this running at the very beginning of the program, and don't have anything else running - no wifi no nothing.

Re: ESP_Timer or "gpio_isr_handler_add" sometimes gets delayed and interrupt misses rising edge.

Posted: Fri Jan 14, 2022 3:21 am
by ESP_Sprite
Yeah, sorry, that was me giving that advise... I didn't get that your thing would be so sensitive to timing latency. Best solution here is best-of-both-worlds: use a hardware ISR but don't do much more there than unblock (using a semaphore or something) a high-priority task. Optionally, you could read the GPIO in the ISR, but don't try to do anything more fancy there.

Not sure about the actual issue you have with the timer... you are calling a bunch of functions that aren't necessarily written for low latency (creating/destroying type functions are usually not) but I don't know enough about that to tell you what the exact problem could be.