NCS - Unreliable UART_RX_RDY event using UART 1MBaud

Hi there,

I am currently facing a problem where the communication on the nRF5340DK using VCOM1 and UART 1MBaud is not working reliably. (PCA10095 V2.0.0 and NCS: V2.4.0) This occurs in both, debug and non-debug builds.

To be more precise, the configured UART callback function is sometimes called too early (?) with the UART_RX_RDY event set. It seems, that the provided int32 timeout in uart_rx_enable() is not always awaited before the UART_RX_RDY event  is triggered.
Looking at the uart_event *evt, I can see that evt->data.rx.len does not match the actually transmitted amount of bytes. Yet, checking the memory viewer, I can see that the frame has been fully received. This occurs for arbitrary data and frame lengths.

Running the same code at 115'200Baud, works as expected.
In the nRF5340 Product Specification, chapter UARTE, the first Note states: "The external crystal oscillator must be enabled to obtain sufficient clock accuracy for stable communication."

With this information, I also started the HFCLK using the HFXO. Unfortunately, the above behavior persists.

I also varied the timeout duration and tried configuring the 1MBaud with an overlay file, instead of configuring it during runtime. Still, the same problem occurs.

Below is a stripped version of the code used:

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
#include <zephyr/drivers/uart.h>

// Define the receiving timeout period in us
#define RECEIVE_TIMEOUT 1000

// Get the device pointer of the CLOCK hardware
const struct device *clock = DEVICE_DT_GET(DT_NODELABEL(clock));

// Get the device pointer of the UART hardware
const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(uart0));

// Define the UART receive and transmission buffer
static uint8 tx_buf[1024] = {0};
static uint8 rx_buf[1024] = {0};

static struct uart_event_rx gRxEvt;
static bool gEnableRx = false;

// -----------------------------------------------------------------------------
// Callback function for UART
static void uart_cb(
    const struct device *dev,
    struct uart_event *evt,
    void *user_data)
{
    uint16 frame_len;
    uint16 check;
    uint16 crc;

    switch (evt->type) {

    case UART_RX_RDY:
        // Stop the UART reception 
        uart_rx_disable(uart);

        // Get the rx frame length
        frame_len = (evt->data.rx.buf[0] << 8) + evt->data.rx.buf[1];
        // Check received frame for validity
        if ((frame_len + 2) == evt->data.rx.len)
        {
            // Valid frame received
            gRxEvt.buf = evt->data.rx.buf;
            gRxEvt.len = evt->data.rx.len;
            gRxEvt.offset = evt->data.rx.offset;
        }
        else {
            // Invalid frame received. Set flag to reenable the UART reception
            // once the UART_RX_DISABLED event is handled.
            gEnableRx = true;
        }
        break;

    case UART_RX_DISABLED:
        if (gEnableRx) {
            // Clear the flag and reenable the UART reception
            gEnableRx = false;
            uart_rx_enable(uart, rx_buf, sizeof rx_buf, RECEIVE_TIMEOUT);
        }
        break;

	case UART_TX_DONE:
		// Reenable the UART reception once the tx frame has been sent
        uart_rx_enable(uart, rx_buf, sizeof rx_buf, RECEIVE_TIMEOUT);
		break;

	default:
		break;
	}
}

// -----------------------------------------------------------------------------
int32 main(void)
{
    int32 ret;
    struct uart_config cfg = {
        1000000UL,
        UART_CFG_PARITY_NONE,
        UART_CFG_STOP_BITS_1,
        UART_CFG_DATA_BITS_8,
        UART_CFG_FLOW_CTRL_NONE
    };
    
    // Verify that the clock is ready
    if (!device_is_ready(clock)){
		goto exception;
    }
    
    // The HFXO is configured by default.
    // Enable the HFCLK for accurate UART timings. See nRF5340_PS chapter UARTE.
    ret = clock_control_on(clock, CLOCK_CONTROL_NRF_TYPE_HFCLK);
	if (ret) {goto exception;}
    
    // Verify that the UART device is ready
	if (!device_is_ready(uart)){
		goto exception;
	}
	
    // Configure the UART settings
    ret = uart_configure(uart, &cfg);
    if (ret) {goto exception;}

    // Register the UART callback function
	ret = uart_callback_set(uart, uart_cb, NULL);
    if (ret) {goto exception;}

    // Start data reception by calling uart_rx_enable() and pass it the address 
    // of the receive buffer
	ret = uart_rx_enable(uart, rx_buf, sizeof rx_buf, RECEIVE_TIMEOUT);
	if (ret) {goto exception;}
    
    while (1) {
        // Check if valid rx frame has been received
        // ...
    }
    
    // ...
}

The following Kconfig settings are used:

# Include GPIO drivers in system config
CONFIG_GPIO=y

# Enable the serial driver in asynchronous mode and allow configuration during
# runtime
CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=y
CONFIG_UART_USE_RUNTIME_CONFIGURE=y

# Increase the main stack size from default value 1024 to 5120
CONFIG_MAIN_STACK_SIZE=5120

# Enable the clock control to configure the HFXO during runtime
CONFIG_CLOCK_CONTROL=y

On the corresponding GPIO P0.22, the received signal looks good and clean.

Am I configuring something wrong or is such a behavior known to you?

Thank you in advance!

Regards,
Pascal

Parents Reply Children
  • Hi Vidar

    Thank you for the quick reponse and the solution to my problem!

    Setting the suggested configurations below, UART with 1MBaud works as expected now.

    CONFIG_UART_0_NRF_HW_ASYNC=y
    CONFIG_UART_0_NRF_HW_ASYNC_TIMER=1

    The largest packets that I am sending back and forth are 4096 bytes. Currently I am just trying to get the UART Interface running with all baudrates using a simple command and answer flow. Thus no double buffering.

    Regards,
    Pascal

Related