Zephyr UART Asynchronous API continuous data receiving

Hi all, 

I'm having problems receiving data from UART using Zephyr Asynchronous API. I've read documentation from UART — Zephyr Project Documentation and I think I understand how UART API new buffer requests work, to have always a seamless data receiving.

I'm providing 40 bytes length buffers. When sending from 1 to 80 bytes, all works as expected, but when sending more than 80 bytes at once, some data is lost.

I'm using nRF Connect SDK v1.9.1 with Zephyr v2.7.99. Arduino Nicla Sense Me board (Custom board as it is not supported yet in Zephyr v2.7.99).

This is the Asynchronous API callback:

static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
    ARG_UNUSED(dev);

    struct uart_data_t *buf;
    static struct uart_data_t *req_buf;

    switch (evt->type) {
    case UART_TX_DONE:
        break;

    case UART_RX_RDY:
        buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data);
        buf->len = evt->data.rx.len;
        LOG_DBG("Rx %d bytes from id %d", buf->len, buf->uid);

        k_fifo_put(&fifo_uart_rx, buf);

        if (buf->len < UART_BUF_SIZE) {
            LOG_DBG("%s", "Manually disabling UART");      
            uart_rx_disable(uart);
        }

        break;

    case UART_RX_DISABLED:
        buf = k_malloc(sizeof(*buf));
        buf->uid = uid++;
        if (!buf) LOG_ERR("%s", "Not able to allocate buffer");
        LOG_DBG("RX_DISABLED. malloc %p, id %d", buf, buf->uid);
        uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_WAIT_FOR_RX_US);

        break;

    case UART_RX_BUF_REQUEST:
        req_buf = k_malloc(sizeof(*req_buf));
        req_buf->uid = uid++;
        if (!req_buf) LOG_ERR("%s", "Not able to allocate buffer");
        LOG_DBG("BUF_REQUEST. malloc %p, id %d", req_buf, req_buf->uid);
        uart_rx_buf_rsp(uart, req_buf->data, sizeof(req_buf->data));
       
        break;

    case UART_RX_BUF_RELEASED:  
        break;

    case UART_TX_ABORTED:
        break;

    default:
        break;
    }
}

Executing the program and sending, for example, 150 bytes at once, gives the following traces:

*** Booting Zephyr OS build v2.7.99-ncs1-1 ***
D: FIRST ENABLE. malloc 0x20001680, id 0
D: BUF_REQUEST. malloc 0x200016b8, id 1
D: Rx 40 bytes from id 0
D: BUF_REQUEST. malloc 0x200016f0, id 2
D: Rx 40 bytes from id 1
D: RX_DISABLED. malloc 0x20001728, id 3
D: Rx 1 bytes from id 3
D: Manually disabling UART
D: BUF_REQUEST. malloc 0x20001760, id 4
D: RX_DISABLED. malloc 0x20001798, id 5
D: BUF_REQUEST. malloc 0x200017d0, id 6

Why automatically goes to UART_RX_DISABLED event after receiving first 80 bytes? If I send the same data in packets of 80 bytes with a delay between them, all data is received and no UART_RX_DISABLED is reached until I manually call uart_rx_disable(uart);

Is there any way to continuous receive any number of bytes concatenating any number of necessary buffers?

If I increase the size of the buffer to 120 bytes and send 370 bytes at once, a different behaviour (also not as expected) is observed:

*** Booting Zephyr OS build v2.7.99-ncs1-1 ***
D: FIRST ENABLE. malloc 0x20001680, id 0
D: BUF_REQUEST. malloc 0x20001708, id 1
D: Rx 120 bytes from id 0
D: BUF_REQUEST. malloc 0x20001790, id 2
D: Rx 120 bytes from id 1
D: BUF_REQUEST. malloc 0x20001818, id 3
D: Rx 120 bytes from id 2
D: BUF_REQUEST. malloc 0x200018a0, id 4

Only 360 bytes are notified to be received. Then I send just 1 byte and receive also the missing 10 bytes:

D: Rx 1 bytes from id 3
D: Manually disabling UART
D: Rx 10 bytes from id 3
D: Manually disabling UART
D: RX_DISABLED. malloc 0x20001928, id 5
D: BUF_REQUEST. malloc 0x200019b0, id 6

Another strange thing is when I change buffer size to 256 or 512 bytes, nothing works. No data is received. But with buffer sizes of 255, 257, 511 or 513 it works (Not as I expect, but works) It's very strange that changing buffer size the behaviour changes.

Please note that this is only an example and I'm not caring about freeing the allocated memory

Thanks in advance!

Pedro.

  • I can answer to myself the last issue, about not working when defining 256 o 512 bytes buffers. RXD.MAXCNT register in UARTE0 (Maximum number of bytes in receive buffer) is 8 bits. When you write 256 o 512 to it, you are actually writing a 0 to that 8 bits.

    Surprisingly this is not detected as an error by uart_rx_enable(). Could it be possible to fix this for future releases?

    About the other problem, I've simplified a lot my code, but I'm still receiving less bytes.

    If the buffer is 40 bytes, and I send 90 bytes length string

    #abcdef-10,ghijkl-20,mnopqr-30,stuvwx-40,abcdef-50,ghijkl-60,mnopqr-70,stuvwx-80,abcdef-90

    I receive 3 UART_RX_RDY events:
    1st with #abcdef-10,ghijkl-20,mnopqr-30,stuvwx-40
    2nd with ,abcdef-50,ghijkl-60,mnopqr-70,stuvwx-80
    3rd with ,a (Missing 8 bytes)

    Then, if I continue sending chars, I start receiving the 'lost' chars, for example:
    I send 123, I receive bcd, then I send 4 and receive e and so on, until I fill the 40 bytes buffer and I receive all the 'delayed' data.

    What I'm doing wrong??

    Thanks again!

  • Hello Pedro,

    Have you tried to restart the UART to workaround this?

    Thanks.

    Best Regards,

    Kazi Afroza Sultana

  • Hello Kazi,

    Do you mean disabling and re-enabling UART?

    I tried disabling and re-enabling UART each time I receive something and some bytes are also lost.

    Thanks you.

  • Hi all,

    I've finally solved it. The problem was doing too many thins inside the interrupt handler of UART. When I reduce to minimum the things done in the interrupt handler, all work as expected.

    I hope this can help somebody.

    Regards,

Related