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

UART data loss on nRF52840DK

Hi all,

Project description and what works:

in my project I have one nRF52840DK as central and three nRF52DK as peripheral. The code on the central is based on the "nrf52-ble-app-uart-c-multilink-master" example and the code on the peripherals is based on the "ble_app_uart" example. Each peripheral sends data with a data-rate of 5,125 kByte/s (41 kBit/s) to the central. To be more precise, each peripheral sends 205 Bytes of data every 40ms. This works like a charm, without errors. (:

Problem:

At the moment I am having trouble with the UART on the central side. The data of all three data links has to be written via UART to a PC (virtual COM port). So the UART has to handle 3*41kBit/s = 123kBit/s. The order or the exact time, when each NUS paket gets written into the virtual COM port does not matter. But no data should be lost in this process. The problem is that the UART dropps data at this data rate. The loss of data is very inconsistent and random.

Test Setup:

To verify the function of the UART each peripheral sends consistently a 205 byte long array (as a NUS-paket) to the central. The NUS paket of each peripheral contains different characters:

Peripheral 1:  !!!!!!!!!!!!!!!!!!!!!!! .... (205 elements)

Peripheral 2: """""""""""""""""" .... (205 elements)

Peripheral 3: ############ .... (205 elements)

I use Putty to view the data which is written in the virtual COM port.

Test Results:

As we can see, UART drops data. Normally I should see 205 elements of each character.

Questions

1. First of all, is the usage of putty in this case reliable? Or did just windows cant keep up with printing this chars in this console?

2. Is there a other/better way to evaluate this?

3. What are the correct parameters in this case for ...

3.1 ... UART_TX_BUF_SIZE and UART_RX_BUF_SIZE? (Actual: 32768)

3.2 ... priority in APP_UART_FIFO_INIT? (Actual: APP_IRQ_PRIORITY_MID)

3.3 ... baudrate in uart_init()? (Actual: UART_BAUDRATE_BAUDRATE_Baud460800)

4. Do I have to change the chosen baudrate in the windows device manager (COM) as well?

5. Can you please provide me any suggestions or help on how to solve this issue?

Any kind of feedback is appreciated. Thank you so much in advance.

Kind regards,

Maria

  • Was this while using baudrate = 1 000 000?

    Yes.

    Is your only option to use UART?

    This product is going to be communicating with a computer. So "UART over USB" of the DK would be the only option?

    How is that data sent?

    From BLE interrupts. The data is sent whenever there is a BLE_NUS_C_EVT_NUS_TX_EVT. This is the snippet of the ble_nus_c_evt_handler:

            case BLE_NUS_C_EVT_NUS_TX_EVT:
                ble_nus_chars_received_uart_print(p_ble_nus_evt->p_data, p_ble_nus_evt->data_len);

    Can you try to use your RX events to just copy the data into separate buffers, and use some flag to print these buffers over UART and see if that helps on the situation?

    To be honest, that sounds a bit complicated, but I will see if I can do that.

    Are you using the DKs programmer to transfer the UART data, or another external UART device?

    I am using the DKs programmer. 

    look into flow control

    I have a few questions regarding the flow control:

    1. General question: The nRF52840DK has a UART<-> USB bridge. Can I use flow control (RTS/CTS) in this case? Is there flow control available? (RTS and CTS would be extra pins on a RS-232 interface)

    2. Can I achieve flow control with the following steps?:

    # .flow_control = APP_UART_FLOW_CONTROL_ENABLED

    # Enable flow control on my pc´s application with RTS/CTS

    3. Do I need more to proper use flow control?

    Thank you very much in advance.

    Maria

  • Without being able to pinpoint exactly what is happening, I believe it is related to all your data coming in chunks, and not in a streamlined flow. If two (or possibly more) chunks are too close to one another in time, then it will start to struggle. What will happen if you receive data too fast? If you are processing the data in the BLE interrupts, you may loose some interrupts, and if you process them from the main context, then you will risk the buffers being overflown. 

    Basically, you can't rely on remaining in an interrupt for too long if you expect a lot of interrupts in your application, so you need some way to offload the UART handling to your main context, e.g. by copying the data to some other buffers in the events, and then process these buffers (send them over UART) in your main loop. 

    I am not sure how you do all your tests, and what your test scripts are doing. You say that they are looking for 200 something of the same character. Perhaps you are just trying to print 200 of the same character from the interrupt? What about doing something like this

     (pseudo code):

    #define MAX_CONNECTIONS 5
    #define MAX_BUFFER_LEN 247
    uint8_t all_my_data[MAX_CONNECTIONS][MAX_BUFFER_LEN];
    uint8_t buffer_free[MAX_CONNECTIONS];
    
    static void nus_event_handler(p_ble_evt p_evt)
    {
        // Assuming that the length of an incoming buffer is always 247. If not, keep a separate table where you specify the length.
        uint8_t conn_handle = p_ble_evt->evt...conn_handle;
        if (buffer_free[conn_handle] = true)
        {
            buffer_free[conn_handle] = false;
            for (uint8_t i=0; i<247 /*length*/; i++)
            {
                all_my_data[conn_handle][i] = p_evt->evt...p_data[i];   //copying the buffer.
            }
        }
        else
        {
            NRF_LOG_INFO("Not able to process data on conn_handle %d", conn_handle);
            //figure out what to do in this scenario.
        }
    }
    
    static void process_uart(void)
    {
        ret_code_t err_code;
        for(uint8_t i=0; i<MAX_CONNECTIONS; i++)
        {
            if (buffer_free[i] == false)
            {
                for(uint8_t j=0; j<247 /*length*/; j++)
                {
                    do
                    {
                        err_code = app_uart_put(all_my_data[i][j]);
                        if (err_code != NRF_ERROR_BUSY)
                        {
                            APP_ERROR_CHECK(err_code);
                        }
                    }while (err_code == NRF_ERROR_BUSY);
                }
                //done with one buffer.
                buffer_free[i] = true;
            }
        }
    }
    
    int main(void)
    {
        init_everything();
        ...
        
        while(true)
        {
            process_uart();
            sd_app_evt_wait();
        }
    }

    By default, the ble_app_uart_c example doesn't really handle this that thoroughly, because it is not intended for this. It is merely a simple example showing how to pass data over BLE.

    BR,
    Edvin

Related