UART Peripheral Interrupts

_K00CT_
Posts: 7
Joined: Thu Feb 23, 2023 9:18 am

UART Peripheral Interrupts

Postby _K00CT_ » Thu Feb 23, 2023 9:30 am

I want to execute an interrupt every time the UART peripheral finishes sending all the bytes in the TX buffer.

I know I can use an event queue to receive an event when this occurs, but for my application, I need to trigger an interrupt routine immediately, rather than poll a queue.

I have written the following code within PIO.

Code: Select all

#include <Arduino.h>
#include <driver/uart.h>
#include "TimingInterface.hpp"

#define UART_PORT UART_NUM_2
#define UART_PIN 5
#define UART_USE_DEFAULT_PIN -1
#define UART_BAUD 2400
#define UART_TX_BUFFER_SIZE 256
#define UART_RX_BUFFER_SIZE 256
#define UART_EVENT_QUEUE_SIZE 0
#define UART_EVENT_ALLOCATE_INTERRUPT false
#define UART_RX_READ_TIMEOUT 1
#define UART_RTS_DISABLED -1
#define UART_CTS_DISABLED -1


TimingInterface sendPeriodTimer;

uint8_t buffer[255];
uint8_t bufferIndex;

volatile int interruptCounter;
char count;
const int LENGTH = 1;

void IRAM_ATTR uartIsr(void *args){
  interruptCounter++;
}

void setup() {
  Serial.begin(115200);
  Serial.println("serial to terminal has been started");

  interruptCounter = 0;
  count = 0;

  uart_config_t uart_config = {
    .baud_rate = UART_BAUD,
    .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(UART_PORT, &uart_config));
    ESP_ERROR_CHECK(uart_driver_install(UART_PORT, UART_TX_BUFFER_SIZE, UART_RX_BUFFER_SIZE, UART_EVENT_QUEUE_SIZE, NULL, UART_EVENT_ALLOCATE_INTERRUPT));
    ESP_ERROR_CHECK(uart_set_rx_timeout(UART_PORT, UART_RX_READ_TIMEOUT));
    ESP_ERROR_CHECK(uart_set_pin(UART_PORT, UART_PIN, UART_USE_DEFAULT_PIN, UART_RTS_DISABLED, UART_CTS_DISABLED));
    ESP_ERROR_CHECK(uart_isr_register(UART_PORT, uartIsr, NULL, ESP_INTR_FLAG_IRAM, NULL));
    ESP_ERROR_CHECK(uart_enable_tx_intr(UART_PORT, true, 0));
}

void loop() {
  uart_write_bytes(UART_PORT, &count, LENGTH);
  count++;
  Serial.printf("char value %d, current interrupt counter value: %d\n", count, interruptCounter);
  vTaskDelay(200);
}
I am using the following PIO configuration (with espressif32 V3.5.0)

Code: Select all

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_extra_dirs = ~/git/olarm-libraries/
targets = upload, monitor
monitor_speed = 115200
monitor_filters = esp32_exception_decoder, time
upload_port = ${sysenv.UPLOAD_PORT}
monitor_port = ${sysenv.UPLOAD_PORT}
test_port = ${sysenv.TEST_PORT}
However, at runtime I am getting an esp error due to the function call uart_isr_register.

The following output is generated

Code: Select all

11:24:44.777 > ets Jul 29 2019 12:21:46
11:24:44.777 > 
11:24:44.777 > rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
11:24:44.777 > configsip: 0, SPIWP:0xee
11:24:44.777 > clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
11:24:44.777 > mode:DIO, clock div:2
11:24:44.777 > load:0x3fff0018,len:4
11:24:44.777 > load:0x3fff001c,len:1044
11:24:44.777 > load:0x40078000,len:10124
11:24:44.777 > load:0x40080400,len:5828
11:24:44.777 > entry 0x400806a8
11:24:44.885 > serial to terminal has been started
11:24:44.901 > ESP_ERROR_CHECK failed: esp_err_t 0x105 (ESP_ERR_NOT_FOUND) at 0x4008543c
11:24:44.901 > file: "src/main.cpp" line 50
11:24:44.901 > func: void setup()
11:24:44.901 > expression: uart_isr_register(UART_PORT, uartIsr, NULL, ESP_INTR_FLAG_IRAM, NULL)
11:24:44.917 > 
11:24:44.917 > ELF file SHA256: 0000000000000000
11:24:44.917 > 
11:24:44.917 > Backtrace: 0x40084f2c:0x3ffb1f20 0x4008543f:0x3ffb1f40 0x400d0e9c:0x3ffb1f60 0x400d2242:0x3ffb1fb0 0x40086e11:0x3ffb1fd0
11:24:44.990 >   #0  0x40084f2c:0x3ffb1f20 in invoke_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/panic.c:715
11:24:44.990 >   #1  0x4008543f:0x3ffb1f40 in _esp_error_check_failed at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/panic.c:721
11:24:44.990 >   #2  0x400d0e9c:0x3ffb1f60 in setup() at src/main.cpp:47 (discriminator 2)
11:24:44.990 >   #3  0x400d2242:0x3ffb1fb0 in loopTask(void*) at /Users/justin/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp:18
11:24:44.990 >   #4  0x40086e11:0x3ffb1fd0 in vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c:355 (discriminator 1)
11:24:44.990 > 
11:24:44.990 > Rebooting...
Any assistance with understanding why the function call throws an error would be greatly appreciated!

ESP_Sprite
Posts: 9606
Joined: Thu Nov 26, 2015 4:08 am

Re: UART Peripheral Interrupts

Postby ESP_Sprite » Fri Feb 24, 2023 1:32 am

The reason is that the driver already uses that interrupt to handle the blocking read and write calls. Also note that queue reads are not 'polled'; the RTOS suspends the reading task until something (e.g. the UART interrupt) pushes something onto it, at which point (dependent on priority) it immediately unblocks it; in other words, queue reads generally have a reasonably short and determinable latency. Do you already know that reading the queue is going to be too slow, or are you just guessing?

_K00CT_
Posts: 7
Joined: Thu Feb 23, 2023 9:18 am

Re: UART Peripheral Interrupts

Postby _K00CT_ » Fri Feb 24, 2023 7:30 am

ESP_Sprite wrote:
Fri Feb 24, 2023 1:32 am
The reason is that the driver already uses that interrupt to handle the blocking read and write calls. Also note that queue reads are not 'polled'; the RTOS suspends the reading task until something (e.g. the UART interrupt) pushes something onto it, at which point (dependent on priority) it immediately unblocks it; in other words, queue reads generally have a reasonably short and determinable latency. Do you already know that reading the queue is going to be too slow, or are you just guessing?
Thank you for your response. I did have a sneaking suspicion that the driver's own configuration might cause an issue. I'm surprised that it does not allow overwriting of the currently set interrupt though (even if that does break things).

I am not worried about the queue reading time. I am trying to execute a routine as soon as the UART peripheral has finished sending the last byte, all while running a number of other tasks. If I continuously poll/check the queue, I will experience non-negligible delays between each time I check the queue. I was hoping to resolve this by having an interrupt trigger, removing the need to monitor a queue for new events and allowing me to execute my routine as soon as possible after the final byte has been sent.

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

Re: UART Peripheral Interrupts

Postby MicroController » Fri Feb 24, 2023 11:23 am

What "tx buffer" are you referring to? The hardware fifo buffer? Or the UART driver's tx ringbuffer? Notice that one "message" you send via the UART driver may internally be split into multiple fills of the HW buffer. Do you want to receive an interrupt each time the HW buffer is sent? Or only a notification once your whole "message" is done?
For the former, I think you have basically two viable options:
a) roll your own ISR for the UART, which then needs to do all the things the UART interrupt is supposed to handle, including transfering data between UART and RAM in both directions, manage the fifos and any queues/ringbuffers, and generally control the UART peripheral. You'd basically do away with the supplied UART driver and build your own. Or,
b) initialize the UART driver, then unregister the driver's ISR and register your own one. In your own ISR, you'd then check the source of the interrupt (e.g. RX FIFO full), react to it as you want, and then call the original driver's ISR to deal with all the rest.

And, of course, you can still just go with the standard queue approach unless it actually proves to be too slow for your use case or you want an event which is not available as such, like the not-very-useful "one HW buffer worth of data sent".

Who is online

Users browsing this forum: MicroController and 90 guests