This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

UART max 80 bytes on receive is possible

Hi,

got a question about UART and maximum bytes it can receive in one burst or "packet".

Using nRF52382 and SDK16.

Recently I've been developing a new app in which UART baudrate needs to be 576000. Everything is set and Rx/Tx works. Legacy mode is turned off so only easy dma is enabled in the driver.

When nRF needs to receive more than 30 bytes problems occur and APP_UART_COMMUNICATION_ERROR with code 1 keeps turning up. I've enabled hardware control for test and nRF is able to receive exactly 80 bytes correctly. If I send 81 bytes, I loose the last byte, if I send 82 bytes I loose the last 2 bytes and so on. UART FIFO is set at 256 bytes for both Rx and Tx.

 Is 80 bytes some theoretical limit or is it defined somewhere in the sdk? When hardware flow is enabled I don't get APP_UART_COMMUNICATION_ERRORs, but bytes are still missing. Anyone knows why?

And 2 bonus questions.

1.) Is libuarte really only option for faster UART and can it solve the problem above?

2.) Where to start if I wish to implement my own UART driver?

Thanks!

Parents
  • Is 80 bytes some theoretical limit or is it defined somewhere in the sdk?

     No. It shouldn't be. As you may already have checked, the APP_UART_COMMUNICATION_ERROR with code 1 means OVERRUN:

    (screenshot from infocenter)

    How have you set up your UART? Do you have an event handler that handles the incoming bytes? Do you use the app_uart_fifo.c? If so, do you call nrf_drv_uart_rx() in the NRF_DRV_UART_ET_RX_DONE event?

    Remember that the UART FIFO is a separate buffer, reading from the UART peripheral. 

    Is there some way for me to reproduce this on an nRF52DK? How do you send your data?

    BR,
    Edvin

  • Hi Edvin,

    thanks for replaying.

    This is the uart init function:

    void UART_Initialize(void)
    {
        uint32_t err_code;
        app_uart_comm_params_t const comm_params =
        {
            .rx_pin_no    = UART_RX_PIN,
            .tx_pin_no    = UART_TX_PIN,
            .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
            .use_parity   = false,
            .baud_rate    = UART_BAUDRATE_BAUDRATE_Baud250000
        };

        APP_UART_FIFO_INIT(&comm_params, UART_BUFFER_SIZE, UART_BUFFER_SIZE, uart_event_handle, UART_PRIORITY, err_code);
        APP_ERROR_CHECK(err_code);
    }

    UART_BUFFER_SIZE = 256

    UART_PRIORITY = APP_IRQ_PRIORITY_HIGHEST // Tried every possible option

    #define UART_EASY_DMA_SUPPORT 1

    #define UART_LEGACY_SUPPORT 0

    This is how I process Rx data, this function is called in main.c, in the main while(1) loop(UART_BUFFER_SIZE = 512):

    static void process_uart(void)
    {
        if (!uartWorker.enabled)
        {
            return;
        }

        if (uartWorker.bleBufferAvailable)
        {
            if (app_uart_get(&uartWorker.byte) == NRF_SUCCESS)
            {
                BOARD_LedRxOn();
                //NRF_LOG_INFO("Rx: %d", uartWorker.byte);

                if (uartWorker.byteIndex < UART_BUFFER_SIZE)
                {
                    uartWorker.buffer[uartWorker.byteIndex] = uartWorker.byte;
                    uartWorker.byteIndex++;
                }

                if (uartWorker.byte == 0)
                {

    #ifdef PRINT_UART_COMM
                    NRF_LOG_INFO("-> BLE(1): %d", uartWorker.byteIndex);
                    NRF_LOG_HEXDUMP_INFO(uartWorker.buffer, uartWorker.byteIndex);
    #endif
                    if (dongleInfo.verified)
                    {
                        uartWorker.bleBufferAvailable = ble_rws_try_to_send(&m_rws, uartWorker.buffer, &uartWorker.byteIndex);
                        if (uartWorker.bleBufferAvailable)
                        {
                            NRF_LOG_INFO("-> BLE(1) SENT");
                            uartWorker.byteIndex = 0;
                        }
                    }
                    else
                    {
                        uartWorker.byteIndex = 0;
                    }
                }
            }
        }

        if (uartWorker.txComplete)
        {

    #ifdef PRINT_UART_COMM
            NRF_LOG_INFO("-> BLE(2)");
            NRF_LOG_HEXDUMP_INFO(uartWorker.buffer, uartWorker.byteIndex);
    #endif

            if (dongleInfo.verified)
            {
                uartWorker.txComplete = 0;
                uartWorker.bleBufferAvailable = ble_rws_try_to_send(&m_rws, uartWorker.buffer, &uartWorker.byteIndex);
                if (uartWorker.bleBufferAvailable)
                {
                    NRF_LOG_INFO("-> BLE(2) SENT");
                    uartWorker.byteIndex = 0;
                }
            }
        }
    }

    I don't use NRF_DRV_UART_ET_RX_DONE. My uart event handling function does not do much:

    void uart_event_handle(app_uart_evt_t * p_event)
    {
        switch (p_event->evt_type)
        {
            case APP_UART_DATA_READY:
            {

            } break;

            case APP_UART_COMMUNICATION_ERROR:
            {
                NRF_LOG_ERROR("APP_UART_COMMUNICATION_ERROR");// | Code: 0x%x", p_event->data.error_code);
                uartWorker.byteIndex = 0;
            } break;

            case APP_UART_FIFO_ERROR:
            {
                NRF_LOG_ERROR("APP_UART_FIFO_ERROR");
                uartWorker.byteIndex = 0;
            } break;
                
            default:
            {
            
            } break;
        }
    }

    Pretty much no matter how I configure the UART it's the same behaviour.

    BAUDRATE: 115200 & 250000 -> Maximum 80 bytes is received correctly and printed in:

    NRF_LOG_INFO("-> BLE(1): %d", uartWorker.byteIndex);

    NRF_LOG_HEXDUMP_INFO(uartWorker.buffer, uartWorker.byteIndex);

    If I send 81 bytes, 81st is lost, send 82, last 2 are lost.

    For baudrate 576000 this is 34. So if I send 36 bytes, I loose last 2.

    Please do take in account I've also implemented libuarte, the only difference is that now I can receive 80 bytes on baudrate of 576000.

    You should be able to get this behavior from default ble_app_uart example since my UART implementation is the same as it is there. I can always send you my entire project.

    This is the code we're using for a looong time, problem occurred when our custom device sent more than 34 bytes on UART and nRF didn't get it. I've developed a small UART app for windows so now we're testing with that.

    Please try with ble_app_uart, if there are no problems I will send you my entire project. I'm still looking for an error in my project and debugging, but I can't find nothing obvious especially since libuarte is also behaving the same.

    Thanks!

  • What's your process_uart() purpose?

    Why don't you receive the data fifo from uart_event_handle()?

    It's must be something wrong, when you get uart data in  process_uart() .

    Normally it's handle in event (interrupt). 

    You may try the following example.

    void uart_event_handle(app_uart_evt_t * p_event)
    {
    static uint8_t data_array[256];
    static uint8_t uartWorker.byteIndex = 0;

    switch (p_event->evt_type)
    {
    /**@snippet [Handling data from UART] */
    case APP_UART_DATA_READY:
    //UNUSED_VARIABLE(app_uart_get(&data_array[uartWorker.byteIndex]));
    app_uart_get(&data_array[uartWorker.byteIndex]);
     uartWorker.byteIndex++;

    if ((data_array[uartWorker.byteIndex - 1] == '\n') || (uartWorker.byteIndex >=UART_BUFFER_SIZE ))
    {
    data_array[uartWorker.byteIndex]=0;
     uartWorker.byteIndex = 0;
    memcpy(uartWorker.buffer,data_array,sizeof(data_array));
    }
    break;
    /**@snippet [Handling data from UART] */
    case APP_UART_COMMUNICATION_ERROR:
    APP_ERROR_HANDLER(p_event->data.error_communication);
    break;

    case APP_UART_FIFO_ERROR:
    APP_ERROR_HANDLER(p_event->data.error_code);
    break;

    default:
    break;
    }
    }

    Good luck!

  • Hi,

    uart_process() function only transfers receiving the data into while loop. According to my testing handling it in while loop makes the receiving data more stable, basically without errors in receiving. At least till now...

    I already tried handling it similar like you mentioned, behaves the same. I will try again, perhaps I missed something the first time I tried.

    I believe there is an error on my side, obviously, but what confuses me that I also get the same behavior using libuarte and with libuarte I'm handling the data in libuarte event handler (uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)) with same results.

    So any way I go I get to this 80 bytes max in a single burst which makes this UART unusable for our use case. I wish I defined 80 somewhere but I didn't. Very weird behavior.

Reply
  • Hi,

    uart_process() function only transfers receiving the data into while loop. According to my testing handling it in while loop makes the receiving data more stable, basically without errors in receiving. At least till now...

    I already tried handling it similar like you mentioned, behaves the same. I will try again, perhaps I missed something the first time I tried.

    I believe there is an error on my side, obviously, but what confuses me that I also get the same behavior using libuarte and with libuarte I'm handling the data in libuarte event handler (uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)) with same results.

    So any way I go I get to this 80 bytes max in a single burst which makes this UART unusable for our use case. I wish I defined 80 somewhere but I didn't. Very weird behavior.

Children
  • Okay~

    I don't think that the bug is the HW/FW bug of SDK16 or SOC. Because I had done UART RX more than 128 bytes before(115200bps). I wonder that the possible case is the RX data format from your side. From your  uart_process()  function in while loop, it get the uart rx data fifo untile the data is zero. 

                if (uartWorker.byteIndex < UART_BUFFER_SIZE)
                {
                    uartWorker.buffer[uartWorker.byteIndex] = uartWorker.byte;
                    uartWorker.byteIndex++;
                }

                if (uartWorker.byte == 0)
                {

    Here. But if the 81th byte is zero and over 82 bytes are valid. Your data fifo will be 80 bytes only. So another way, you may check the TX data from your test pattern.

  • This is COBS, you put N bytes in Encode and get out N+3 bytes, last byte being 0 which marks the end of the packet. You can adapt COBS to your needs but 0 always marks start and/or end of the packet, in my case it marks the end.

    I've checked encoding function, I've checked the data in debug and with the oscilloscope and it's valid. Wasted few days on this actually.

    I will also check apply_old_config, maybe I've missed something!

  • If it's the COBS encode, I think it will never be zero until end of fifo. I agree the suggestion of Edvin.

    You may check the 0x480 (ERRORSRC) in your handle function. Maybe it's the overrun issue (TX signal issue) in this case.

  • "If it's the COBS encode, I think it will never be zero until end of fifo."

    Yes, it will never be zero until end of packet(not fifo!), but packet can be 40 bytes and everything works, go to 60 bytes, everything works, give it 80 bytes, works. Now give it 81 bytes, and 81st is lost!

    Yes, it's a overrun issue but why? I can see all the data in oscilloscope, I'm using FTDI cable to send data, everything is there, valid. FIFO size is 256 bytes for both Rx and Tx.

    Still looking...

  • Hello,

    Please note that we have an "insert" -> "code" for inserting code snippets. It is easier to read if the code snippets are long, because it maintains the formatting.

    My concern is:

            case APP_UART_DATA_READY:
            {
    
            } break;

    which does nothing.In itself, this should be fine as long as your fifo buffer is large enough (larger than 80 bytes), but I don't know how you set up your project. 

    What I believe is confusing you is the app_uart_fifo and the uart peripheral driver. These are not the same thing. The app_uart_fifo is the library that pulls data from the uart peripheral. Do you use app_uart_fifo.c in your project? If so, look at what is generating the APP_UART_DATA_READY event inside app_uart_fifo.c:

    static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)
    {
        app_uart_evt_t app_uart_event;
        uint32_t err_code;
    
        switch (p_event->type)
        {
            case NRF_DRV_UART_EVT_RX_DONE:
                // If 0, then this is a RXTO event with no new bytes.
                if(p_event->data.rxtx.bytes == 0)
                {
                   // A new start RX is needed to continue to receive data
                   (void)nrf_drv_uart_rx(&app_uart_inst, rx_buffer, 1);
                   break;
                }
    
                // Write received byte to FIFO.
                err_code = app_fifo_put(&m_rx_fifo, p_event->data.rxtx.p_data[0]);
                if (err_code != NRF_SUCCESS)
                {
                    app_uart_event.evt_type          = APP_UART_FIFO_ERROR;
                    app_uart_event.data.error_code   = err_code;
                    m_event_handler(&app_uart_event);
                }
                // Notify that there are data available.
                else if (FIFO_LENGTH(m_rx_fifo) != 0)
                {
                    app_uart_event.evt_type = APP_UART_DATA_READY;
                    m_event_handler(&app_uart_event);
                }
    
                // Start new RX if size in buffer.
                if (FIFO_LENGTH(m_rx_fifo) <= m_rx_fifo.buf_size_mask)
                {
                    (void)nrf_drv_uart_rx(&app_uart_inst, rx_buffer, 1);
                }
                else
                {
                    // Overflow in RX FIFO.
                    m_rx_ovf = true;
                }
    
                break;
    
            case NRF_DRV_UART_EVT_ERROR:
                app_uart_event.evt_type                 = APP_UART_COMMUNICATION_ERROR;
                app_uart_event.data.error_communication = p_event->data.error.error_mask;
                (void)nrf_drv_uart_rx(&app_uart_inst, rx_buffer, 1);
                m_event_handler(&app_uart_event);
                break;
    
            case NRF_DRV_UART_EVT_TX_DONE:
                // Get next byte from FIFO.
                if (app_fifo_get(&m_tx_fifo, tx_buffer) == NRF_SUCCESS)
                {
                    (void)nrf_drv_uart_tx(&app_uart_inst, tx_buffer, 1);
                }
                else
                {
                    // Last byte from FIFO transmitted, notify the application.
                    app_uart_event.evt_type = APP_UART_TX_EMPTY;
                    m_event_handler(&app_uart_event);
                }
                break;
    
            default:
                break;
        }
    }

    Does that function look exactly like this in your case? And is it being used?

    And for context, you say that the 81st byte is missing. Where do you read your bytes? 

    The function process_uart() with the empty APP_UART_DATA_READY: event handler. Where is it called from?

    Just a shot in the dark: Have you tried the ble_app_uart example from the SDK? Do you see the same behavior there if you don't modify anything?

    BR,

    Edvin

Related