ESP_Timer or "gpio_isr_handler_add" sometimes gets delayed and interrupt misses rising edge.
Posted: Wed Jan 12, 2022 10:21 pm
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:
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.
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);
}