Page 1 of 1

How to reduce UART RX Latency

Posted: Tue Dec 18, 2018 4:33 pm
by yudopplyr
Hi guys, I am implementing an interrupt handler for reception of data through the UART of the ESP32. Ive measured the response by sending the same data I recieve through the TX output of the UART. The problem is that I want to reduce the current latency time I have (2 ms). Is there any way of getting the UART data at less than 300 microseconds (for a baud rate of 38.4 kbs)?

Note: In general I need to respond to the incoming uart data in less or equal time of 11/baud_rate (which is the duration of sending a uart frame)

Image

Code: Select all

/*
  UART Interrupt Example
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/portmacro.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "esp_intr_alloc.h"


#define EX_UART_NUM UART_NUM_1

#define BUF_SIZE (66)
uint8_t rxbuf[BUF_SIZE]; //Buffer for incoming data from UART
const int tx_GPIO = 22;
const int rx_GPIO = 21;

// Both definition are same and valid
//static uart_isr_handle_t *handle_console;
static intr_handle_t handle_console;

static void IRAM_ATTR uart_intr_handle(void *arg)
{
  uint16_t rx_fifo_len, status;
  status = UART1.int_st.val; // read UART interrupt Status
  rx_fifo_len = UART1.status.rxfifo_cnt; // read number of bytes in UART buffer
  for (int i = 0; i < rx_fifo_len; i++)
    rxbuf[i] = UART1.fifo.rw_byte; // read all bytes
  // after reading bytes from buffer clear UART interrupt status
  uart_clear_intr_status(EX_UART_NUM, UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR);
  //Echo recieved buffer
  uart_write_bytes(EX_UART_NUM, (const char*) rxbuf, rx_fifo_len);
}


void app_main(void)
{
  uart_config_t uart_config = {
    .baud_rate = 38400,
    .data_bits = UART_DATA_8_BITS,
    .parity = UART_PARITY_EVEN,
    .stop_bits = UART_STOP_BITS_1,
    .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
  };

  ESP_ERROR_CHECK(uart_param_config(EX_UART_NUM, &uart_config));
  //Set UART pins (using UART0 default pins ie no changes.)
  ESP_ERROR_CHECK(uart_set_pin(EX_UART_NUM, tx_GPIO, rx_GPIO, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
  //Install UART driver, and get the queue.
  ESP_ERROR_CHECK(uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, 0, 0, NULL, 0));
  // release the pre registered UART handler/subroutine
  ESP_ERROR_CHECK(uart_isr_free(EX_UART_NUM));
  // register new UART subroutine
  ESP_ERROR_CHECK(uart_isr_register(EX_UART_NUM, uart_intr_handle, NULL, ESP_INTR_FLAG_IRAM, &handle_console));
  // enable RX interrupt
  ESP_ERROR_CHECK(uart_enable_rx_intr(EX_UART_NUM));
  while (1)
  {
    vTaskDelay(1000);
  }
}

Re: How to reduce UART RX Latency

Posted: Wed Dec 19, 2018 2:55 am
by Sprite
I think you're running into the UART receive thresholds. You can configure them differently with uart_intr_config.

Re: How to reduce UART RX Latency

Posted: Thu Dec 20, 2018 3:15 pm
by yudopplyr
Thanks for the info it worked, I am getting an interrupt after 8 us approx for each byte recieved. For future reference I added this to my code

Code: Select all

  uart_intr_config_t uart_intr = {
      .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M
                          | UART_RXFIFO_TOUT_INT_ENA_M
                          | UART_FRM_ERR_INT_ENA_M
                          | UART_RXFIFO_OVF_INT_ENA_M
                          | UART_BRK_DET_INT_ENA_M
                          | UART_PARITY_ERR_INT_ENA_M,
      .rxfifo_full_thresh = 1,
      .rx_timeout_thresh = 10,
      .txfifo_empty_intr_thresh = 10
  };

Re: How to reduce UART RX Latency

Posted: Tue May 27, 2025 5:56 pm
by dg9ngf
While this thread is very old, it still seems to be perfectly valid today. I'm doing the same, trying to solve the same problem. ChatGPT has pointed me to this function (yes, it's 2025 😉) but of course failed to provide working code or explain it well (maybe in 10 years). Unfortunately, the official documentation also fails to explain things. So I have to search through the internet and try to find code snippets from every corner of the web. Still, not much explanation and certainly no recommendations.

I have questions about the proposed code snippet:

Which header files am I supposed to include for these interrupt flags (UART_RXFIFO_FULL_INT_ENA_M etc.)? Other sources show flags in the form of UART_INTR_RXFIFO_TOUT which seem to be defined as the same values. Google has found them in hal/uart_hal.h and they also seem to make the other symbols available – coincidentally? Docs don't help here, please fix that. This is only C and the compiler is as stupid as it gets. (Sorry, I'm used to more productive languages like C#.)

Which interrupt flags should I pass to the function? What's the default if I'm not calling the function? Docs don't help here. Actually, I'm only concerned about making received bytes available much faster than the default. I don't mean to change anything else for now. I wouldn't understand that anyway (at least I'm not concerned about it now, so I'd prefer not to learn all of the other internal details of the hardware, OS and framework that I don't need).

Apparently, I don't need to set all fields of the uart_intr_config_t structure if I'm not setting all interrupt flags. Docs don't help here. Especially the txfifo_empty_intr_thresh field is of unknown purpose to me and only seems to be regarded when also setting the UART_INTR_TXFIFO_EMPTY flag.

All in all, the documentation merely mentions the available names but completely omits any description of the behaviour, consequences, limits, preconditions and just about anything else needed to understand how to use ESP-IDF. â˜šī¸ I can find the source code on GitHub and infer some behaviour from that but that's not very helpful.

Re: How to reduce UART RX Latency

Posted: Tue May 27, 2025 9:24 pm
by MicroController
The threshold registers are documented in the Technical Reference Manual.
If you're writing your own UART interrupt handler, you'll have to familiarize yourself with the TRM some more.
If not, you can also use uart_set_rx_full_threshold() and/or uart_set_rx_timeout().
In short, the RX full threshold defines how many bytes the UART will store in its internal RX buffer before it triggers an interrupt. Complementing this, a non-0 RX timeout ("TOUT") threshold tells the UART to also generate an interrupt if there's some data in the buffer (less than the RX full threshold) and no new data was received for a duration of TOUT bytes.
If you set the RX full threshold to 1, the CPU will receive one interrupt every time one byte is received - lowest possible latency but highest overhead and CPU load; good for small, intermittent transfers.
At the other end, an RX full threshold of 128 means as little as one interrupt to handle for every 128 bytes received - biggest potential latency (127 byte times) but lowest overhead and CPU load; good for large, continuous, high-bitrate transfers.

Re: How to reduce UART RX Latency

Posted: Wed May 28, 2025 1:36 am
by ok-home
Hi
Which header files am I supposed to include for these interrupt flags (UART_RXFIFO_FULL_INT_ENA_M etc.)? Other sources show flags in the form of UART_INTR_RXFIFO_TOUT which seem to be defined as the same values. Google has found them in hal/uart_hal.h and they also seem to make the other symbols available – coincidentally? Docs don't help here, please fix that. This is only C and the compiler is as stupid as it gets. (Sorry, I'm used to more productive languages like C#.)
Note that uart_isr_register() was removed in the latest versions of idf and
interrupts are handled via event, this is a much more convenient mechanism than writing your own interrupt handlers.
https://github.com/espressif/esp-idf/bl ... ple_main.c
Enable or disable RX_FULL & RX_TIMEOUT INTERRUPT
via esp_err_t uart_enable_rx_intr(uart_port_t uart_num)
https://docs.espressif.com/projects/esp ... art_port_t

Re: How to reduce UART RX Latency

Posted: Sat May 31, 2025 9:46 am
by dg9ngf
If not, you can also use uart_set_rx_full_threshold() and/or uart_set_rx_timeout().
Thank you, I did that. However, I cannot see a difference yet. For testing, I set it to trigger the interrupt after only 3 bytes while receiving messages of around 20 bytes. With this, I can immediately see all bytes coming in (calling uart_read_bytes first for 1 byte to detect activity, then 4 more to determine the message length, and finally for the rest of the message). Maybe my issue is that I'm also logging each step with ESP_LOGI and that might delay the code until the log text has been sent out. Both communication and logging run at 115200 baud.