Issue Using Interrupt Driven UART Callback in nRFConnect To Transmit Data

I am having trouble getting the data (7 ASCII characters) transmitted via UART using the interrupt driven mode.  The uart_fifo_fill function returns a positive number (the exact number of characters I requested to send) but it actually doesn't transmit those characters because it doesn't trigger the oscilloscope I have tied to the tx line nor does it turn off an LED in response to that command.

I am using nRF Connect SDK v2.0.2.  Within Visual Studio Code, I have used GUIConfig to uncheck Asynchronous UART API and check UART Interrupt support under Serial Drivers in the Device Drivers section and saved the settings.  The .config file in the build's zephyr folder contains the following:

# Capabilities
#
CONFIG_SERIAL_HAS_DRIVER=y
CONFIG_SERIAL_SUPPORT_ASYNC=y
CONFIG_SERIAL_SUPPORT_INTERRUPT=y
# CONFIG_UART_LOG_LEVEL_OFF is not set
# CONFIG_UART_LOG_LEVEL_ERR is not set
# CONFIG_UART_LOG_LEVEL_WRN is not set
CONFIG_UART_LOG_LEVEL_INF=y
# CONFIG_UART_LOG_LEVEL_DBG is not set
CONFIG_UART_LOG_LEVEL=3
CONFIG_UART_USE_RUNTIME_CONFIGURE=y
# CONFIG_UART_ASYNC_API is not set
# CONFIG_UART_LINE_CTRL is not set
# CONFIG_UART_DRV_CMD is not set
# CONFIG_UART_WIDE_DATA is not set

#
# Serial Drivers
#
# CONFIG_UART_ITE_IT8XXX2 is not set
CONFIG_UART_NRFX=y
CONFIG_UART_0_NRF_UARTE=y
CONFIG_UART_0_ENHANCED_POLL_OUT=y
CONFIG_UART_0_INTERRUPT_DRIVEN=y
# CONFIG_UART_0_NRF_PARITY_BIT is not set
CONFIG_UART_0_NRF_TX_BUFFER_SIZE=32
CONFIG_UART_1_NRF_UARTE=y
CONFIG_UART_1_INTERRUPT_DRIVEN=y
CONFIG_UART_1_ENHANCED_POLL_OUT=y
# CONFIG_UART_1_NRF_PARITY_BIT is not set
CONFIG_UART_1_NRF_TX_BUFFER_SIZE=32
CONFIG_UART_ENHANCED_POLL_OUT=y
CONFIG_NRF_UARTE_PERIPHERAL=y
# CONFIG_UART_ALTERA_JTAG is not set
# CONFIG_UART_RTT is not set
# CONFIG_UART_XLNX_UARTLITE is not set

My code looks like this:

static K_SEM_DEFINE(tx_sem, 0, 1); // Semaphore to signal UART data transmitted

static uint8_t* tx_data;
static size_t tx_data_length;

static void uart_irq_callback(const struct device *dev, void *user_data)
{
    ARG_UNUSED(user_data);
    static int tx_data_idx;
    if (!uart_irq_update(dev))
    {
            LOG_ERR("Couldn't update IRQ\n");
            return;
    }
    if (uart_irq_tx_ready(dev) && tx_data_idx < tx_data_length)
    {
        int tx_send = MIN(CONFIG_UART_1_NRF_TX_BUFFER_SIZE, tx_data_length - tx_data_idx);
        int tx_sent = uart_fifo_fill(dev, (uint8_t *)&tx_data[tx_data_idx], tx_send);
        if (tx_sent <= 0)
        {
             LOG_ERR("Error %d sending data over RS485 bus\n", tx_sent);
             return;
        }
        tx_data_idx += tx_sent;
        if (tx_data_idx == tx_data_length)
        {
            uart_irq_tx_disable(dev);
            tx_data_idx = 0;
            k_sem_give(&tx_sem);
        }
    }

 }

int uart_irq_tx(const struct device *dev, const uint8_t *buf, size_t len)

{

    tx_data = buf;
    tx_data_length = len;
    uart_irq_callback_set(dev, uart_irq_callback);
    uart_irq_tx_enable(dev);
    k_sem_take(&tx_sem, SYS_FOREVER_MS);
    LOG_INF("UART data transmitted\n");
    return len; 

}

I call uart_irg_tx to send data via the uart device I obtain from 
struct device *dev = device_get_binding(DT_LABEL(DT_NODELABEL(uart1)));
for a Fanstel EV-BT840 V4 board.
I find that when I single step through the uart_fifo_fill function, it takes me to that function in uart_nrfx_uarte.c which copies the 7 characters to the RAM buffer and eventually calls tx_start, but no data is actually transmitted even though it returns the length of 7.

Do you have any idea what is going on?
Thanks,
Alec
Parents
  • UART pin assignments are correct for the Fanstel board I'm using.  

    I found the problem.  It's in the callback:

            if (tx_data_idx == tx_data_length)
            {
                uart_irq_tx_disable(dev);
                tx_data_idx = 0;
                k_sem_give(&tx_sem);
            }
    The tx uart should not be disabled here because the data has apparently not yet been sent.  Evidently, the positive return value of uart_fifo_fill only indicates the number of characters that have been successfully written to the tx fifo, not the number successfully transmitted.  Consequently, I need to first wait until the transfer is complete before disabling the tx uart, but this should not be done in the interrupt service routine callback which could block subsequent interrupts, so I removed uart_irq_tx_disable from that conditional block and placed it after k_sem_take in uart_irq_tx.  There I tried polling with uart_irq_tx_complete, but that took over 900 ms for 7 characters at 115200 baud, 1 stop bit, even parity!  That prevented me from getting a response from the receiver via the uart (not shown here in the code) in time.  Do you know why that function takes so long?  If I simply use a k_usleep of 500 us, I receive the response, but I'd rather use  uart_irq_tx_complete which seems to be intended for that purpose.
  • Are you able to share a minimal version of your project that will allow me to replicate the issue here? I can make the ticket private if helps.

    DataMate said:
    The tx uart should not be disabled here because the data has apparently not yet been sent. 

    I don't understand why it fails when you disable the TX IRQ inside the UART ISR. While the data may not have been transmitted at that point, it has been loaded to the UARTE's DMA buffer.

    I have not been able to reproduce the issue on my side either (using SDK v2.1.0). I also tried to look for relevant issues in the Zephyr release notes, but could not seem to find anything.

    Not sure if it's worth mentioning or not, but uart_fifo_fill() will transmit up to 'CONFIG_UART_1_NRF_TX_BUFFER_SIZE' bytes in one go (implementation). This means the UART callback is only invoked once when the data to transmit is less than CONFIG_UART_1_NRF_TX_BUFFER_SIZE in length.

    DataMate said:
    There I tried polling with uart_irq_tx_complete, but that took over 900 ms for 7 characters at 115200 baud, 1 stop bit, even parity! 

    This function can only be used inside the UART ISR as noted here: https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/hardware/peripherals/uart.html#c.uart_irq_tx_complete

Reply
  • Are you able to share a minimal version of your project that will allow me to replicate the issue here? I can make the ticket private if helps.

    DataMate said:
    The tx uart should not be disabled here because the data has apparently not yet been sent. 

    I don't understand why it fails when you disable the TX IRQ inside the UART ISR. While the data may not have been transmitted at that point, it has been loaded to the UARTE's DMA buffer.

    I have not been able to reproduce the issue on my side either (using SDK v2.1.0). I also tried to look for relevant issues in the Zephyr release notes, but could not seem to find anything.

    Not sure if it's worth mentioning or not, but uart_fifo_fill() will transmit up to 'CONFIG_UART_1_NRF_TX_BUFFER_SIZE' bytes in one go (implementation). This means the UART callback is only invoked once when the data to transmit is less than CONFIG_UART_1_NRF_TX_BUFFER_SIZE in length.

    DataMate said:
    There I tried polling with uart_irq_tx_complete, but that took over 900 ms for 7 characters at 115200 baud, 1 stop bit, even parity! 

    This function can only be used inside the UART ISR as noted here: https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/hardware/peripherals/uart.html#c.uart_irq_tx_complete

Children
No Data
Related