nRF52840 UARTE0 EasyDMA receives more bytes than expected

Greetings!

I'm currently making a project that involves using UART in EasyDMA Mode (via Zephyr Async API).

I have noticed an interesting behavior when you try to receive via UARTE0 (provided by Interface/Debug chip VCOM).

For example, I'm enabling the reception with 

uart_rx_enable(uart, rx_buf, 10, SYS_FOREVER_US);

After that I'm sending >10, let's say 20 bytes. After 10 bytes received I get an UART_RX_RDY callback, everything is very fine, but if I will restart the reception with

uart_rx_enable(uart, rx_buf, 10, SYS_FOREVER_US);

I will instantly get one more UART_RX_RDY callback. Buffer will contain that second 10 bytes part.

My question is: Who is actually responsible for such a behavior? Is this some shadow UART buffer in RAM? If so, how can I control its size? Because I don't need it to buffer more than I have set up. Also I'm thinking about the Interface chip, probably if it has received something via VCOM, it stores some data inside in a hidden buffer.

If I switch to UARTE1, no such behavior is observed. Only UARTE0 via USB vcom.

Simple snippet for testing, some minimal editing may be needed:

#include <zephyr/kernel.h>
#include <zephyr/drivers/uart.h>
static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
switch (evt->type) {
case UART_RX_RDY:
printk("RX READY\n");
printk("%d \n", evt->data.rx.len);
break;
}
}

static uint8_t rx_buf[10] = {0}; //A buffer to store incoming UART data

const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(uart0));
int main(void)
{
int err = 0;
if (!device_is_ready(uart)) {
return err;
}
err = uart_callback_set(uart, uart_cb, NULL);
if (err) {
return err;
}
uart_rx_enable(uart, rx_buf, 10, SYS_FOREVER_US);
while (1)
{
k_msleep(5000); // Mash your keyboard here, make > 10 bytes in these 5 seconds
uart_rx_enable(uart, rx_buf, 10, SYS_FOREVER_US);
}
return 0;
}

Thanks in advance,

BR

Vlad

Parents
  • Hi Vlad

    After that I'm sending >10, let's say 20 bytes. After 10 bytes received I get an UART_RX_RDY callback, everything is very fine, but if I will restart the reception

    I do not fully understand your setup. You are sending and receiving, or receiving at the other end?

    But if it is that it works for uarte1 and you see a problem with uart0, maybe you can send me a minimal project that compiles and I can test here. 

    Please do mention the SDK version you are using, and any other details regarding the setup if any.

    Regards,
    Naeem

  • Hi Naeem,

    are there any additional updates regarding this situation?

    BR

  • Hi Vlad,

    I am sorry for the delayed response due to high workload during this period.

    I have tried your code, but was not able to make it work. It would not go as you have described, and things were breaking (I have tried to debug as well but it would go into idle after entering callback set function).

    However, I have tried with the echo-bot sample. I was able to receive the exact amount as set in BUF_SIZE even if I press a lot of characters, only the specified amount is received.

    I have also seen that you are calling uart_rx_enable() in the while(1) loop, why?

    this function is supposed to be called only once and then the call back will be called as per events generated.

    Regards,
    Naeem

  • Hi Naeem,

    sorry, took a long vacation without visiting web. It is really strange that my code is not working on your side since I tested this simple example several times, but OK, I can make more confident example. uart_rx_enable() in loop was just needed to "restart" UART automatically. Bad example, agree.

    Echo bot works very fine since it explicitly drops all data (custom logic implemented in interrupt). I use ASYNC driver so "under the hood" reception is not available for me.

    I have rewritten the code for it to work more correctly, could you please try it to see if you can reproduce my behavior? Now I have added BUTTON 1 to work. I use nRF52840-DK to run this application.

    What you can do to reproduce this issue:

    Variant 1:

    Connect to serial port and see *** Booting nRF Connect SDK v3.5.99-ncs1-1 *** message (or your version).

    Write some > 10 bytes message from your keyboard. You can just mash buttons.

    Press Button 1 on the board.

    Expected to see: UART IS RESTARTED.

    I see: 

    UART IS RESTARTED
    RX READY
    Received: 10 bytes
    RX Stopped
    RX Disabled

    So as you can see, uart_rx wasn't even turned on while some data came in. Then you have turned it on by the push of the button and it INSTANTLY got some data you have entered while uart_rx was disabled. And I'm interested in how could it possibly function so nRF52 somehow still stores data inside even with uart_rx is off, how can I determine this buffer and prevent this behavior?

    Variant 2:

    Connect to serial port and see *** Booting nRF Connect SDK v3.5.99-ncs1-1 *** message (or your version).

    Press Button 1 on the board. Observe UART IS RESTARTED line.

    Write some data. See how after 10 bytes you get

    RX READY
    Received: 10 bytes
    RX Stopped
    RX Disabled

    Now uart_rx is off. Now continue writing something while uart is off. Write at least 10 bytes.

    Press Button 1 on the board once again. 

    Observe:

    RX READY
    Received: 10 bytes
    RX Stopped
    RX Disabled

    Hopefully this code will work better since I've upgraded it to rely only on button, not on some delays.

    #include <zephyr/kernel.h>
    #include <zephyr/drivers/uart.h>
    #include <zephyr/drivers/gpio.h>
    #include <stdbool.h>
    
    static bool is_uart_enabled = false;
    static uint8_t rx_buf[10] = {0};
    const struct device* uart = DEVICE_DT_GET(DT_NODELABEL(uart0));
    const static struct gpio_dt_spec button = GPIO_DT_SPEC_GET(DT_NODELABEL(button0), gpios);
    
    
    static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
    {
    
        switch (evt->type) {
    
        case UART_TX_DONE:
            break;
    
        case UART_TX_ABORTED:
            break;
    
        case UART_RX_RDY:
            printk("RX READY\n");
            printk("Received: %d bytes\n", evt->data.rx.len);
            break;
    
        case UART_RX_BUF_REQUEST:
            break;
    
        case UART_RX_BUF_RELEASED:
            break;
    
        case UART_RX_DISABLED:
            printk("RX Disabled\n");
            is_uart_enabled = false;
            break;
    
        case UART_RX_STOPPED:
            printk("RX Stopped\n");
            break;
    
        default:
            break;
        }
    }
    
    
    int main(void)
    {
        int err = 0;
    
        if (!device_is_ready(uart)) {
            return err;
        }
    
        if (!gpio_is_ready_dt(&button)) {
            return err;
        }
    
        gpio_pin_configure_dt(&button, (GPIO_INPUT | GPIO_PULL_UP | GPIO_ACTIVE_LOW));
    
        err = uart_callback_set(uart, uart_cb, NULL);
        if (err) {
            return err;
        }
    
        while (1)
        {
            if (gpio_pin_get_dt(&button) == 1)
            {
                if (is_uart_enabled == 0)
                {
                    printk("UART IS RESTARTED\n");
                    uart_rx_enable(uart, rx_buf, 10, SYS_FOREVER_US);
                    is_uart_enabled = true;
                }
            }
            k_msleep(500);
        }
    
        return 0;
    }
    prj.conf:
    CONFIG_GPIO=y
    CONFIG_SERIAL=y
    CONFIG_UART_ASYNC_API=y
    Best regards,
    Vlad
  • Hi Vlad,

    Naeem is away and wont be back soon. I was assigned to this thread. I will try soon to replicate the issue using the instruction you gave below. Will come back to you when I have some new observations.

Reply Children
Related