How to reduce UART RX Latency

yudopplyr
Posts: 2
Joined: Tue Dec 18, 2018 3:15 pm

How to reduce UART RX Latency

Postby yudopplyr » Tue Dec 18, 2018 4:33 pm

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);
  }
}

Sprite
Espressif staff
Espressif staff
Posts: 10636
Joined: Thu Nov 26, 2015 4:08 am

Re: How to reduce UART RX Latency

Postby Sprite » Wed Dec 19, 2018 2:55 am

I think you're running into the UART receive thresholds. You can configure them differently with uart_intr_config.

yudopplyr
Posts: 2
Joined: Tue Dec 18, 2018 3:15 pm

Re: How to reduce UART RX Latency

Postby yudopplyr » Thu Dec 20, 2018 3:15 pm

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
  };

User avatar
dg9ngf
Posts: 65
Joined: Mon Sep 16, 2019 6:49 pm
Location: Germany
Contact:

Re: How to reduce UART RX Latency

Postby dg9ngf » Tue May 27, 2025 5:56 pm

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.

MicroController
Posts: 2694
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: How to reduce UART RX Latency

Postby MicroController » Tue May 27, 2025 9:24 pm

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.

User avatar
ok-home
Posts: 157
Joined: Sun May 02, 2021 7:23 pm
Location: Russia Novosibirsk
Contact:

Re: How to reduce UART RX Latency

Postby ok-home » Wed May 28, 2025 1:36 am

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

User avatar
dg9ngf
Posts: 65
Joined: Mon Sep 16, 2019 6:49 pm
Location: Germany
Contact:

Re: How to reduce UART RX Latency

Postby dg9ngf » Sat May 31, 2025 9:46 am

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.

Who is online

Users browsing this forum: Bing [Bot] and 1 guest