Printf over UART at high rate

Hi Nordic, 

I am using the SDK 17, s140 and the nrf52840 , with a modified multilink NUS central example. The intention is to print over UART as fast as possible all data received from the BLE. But I get sometimes errors on the UART transmission -> while using cat in my terminal, there are several packages that are not transmitted complete.

I initialize the uart like this:

#define UART_TX_BUF_SIZE        16384 
#define UART_RX_BUF_SIZE       16384
static void uart_init(void)
{
    ret_code_t err_code;

    const app_uart_comm_params_t comm_params =
          {
              RX_PIN_NUMBER,
              TX_PIN_NUMBER,
              RTS_PIN_NUMBER,
              CTS_PIN_NUMBER,
			  APP_UART_FLOW_CONTROL_ENABLED, 
              false,
			  NRF_UART_BAUDRATE_1000000
          };

        APP_UART_FIFO_INIT(&comm_params,
                             UART_RX_BUF_SIZE,
                             UART_TX_BUF_SIZE,
                             uart_error_handle,
							 APP_IRQ_PRIORITY_HIGH, 
                             err_code);

        APP_ERROR_CHECK(err_code);
}

To print, i use printf() with the RETARGET_ENABLED enabled. 

Theoretically, dma should be enabled by #define UART0_CONFIG_USE_EASY_DMA 1

I'm only printing from one place of the application. Is there something else i could do to receive properly the packages? 

Maybe to use a better function than printf(). 

Any help is welcome. Thank you very much! 

  • Hi,

    app_uart (which is used by printf with RETARGET_ENABLED) is not the best library to use if you need high throughput. app_uart will only send/receive a single byte at a time, meaning the CPU will be very busy handling all the interrupts from the UART(E) peripheral when sending/receiving much data. The app_uart library was originally written for nRF51, which did not have support for EasyDMA, meaning there was always a need to update the data pointer for each byte being transmitted/received anyways. App_uart also does not implement any timeouts, meaning you would see delays in receiving data chunks if larger buffers are used.

    I would rather recommend you to switch to using the libUARTE library, which can handle larger buffers, automatic buffer swapping, and timeouts. I posted a modified version of ble_app_uart in this ticket, which replaces app_uart with libUARTE.

    Best regards,
    Jørgen

  • Hi Jorgen, thank you for your reply.

    I used the library but still don't get good uart transmissions.

    I changed the uart speed to 1Mbps and increased the timeout to 1ms to test if this could bring better results but no.
    I also constantly get the NRF_LIBUARTE_ASYNC_EVT_ERROR error.

    I'm using the nrf_libuarte_async_tx() fcn to transmit the messages.

    Is there something else that i need to change?  what's the maximum Throughput that we can obtain with that library?

    Thanks in advance!

  • The library should be able to handle throughputs up to 1M baud if configured correctly, but this depends on buffer sizes and CPU availability in the application.

    Can you post the code you used to initialize and use the library?

    Are you only seeing issues with TX operations, or are you also having issues with RX operations?

  • I only do TX, i don't receive anything via UART.

    I configured the initialization to only use the app_timer since I'm using the RTC and TIMER to some other applications. At the beginning, when it was not properly configured, i got compilation errors+ fatal errors but once configured properly, i could use it without any compile-test issues.

    NRF_LIBUARTE_ASYNC_DEFINE(libuarte,
    		0,  //_uarte_idx
    		1, //_timer0_idx: TIMER instance used by libuarte for bytes counting
    		NRF_LIBUARTE_PERIPHERAL_NOT_USED, //2, //_rtc1_idx: RTC instance used for timeout:If NRF_LIBUARTE_PERIPHERAL_NOT_USED then RTC instance is used or app_timer
    		NRF_LIBUARTE_PERIPHERAL_NOT_USED, //_timer1_idx  TIMER instance used for timeout
    		255,  //rx_buf_size Size of single RX buffer
    		3); 
    		
    
    ....
    
    static void uart_init(void)
    {
        uint32_t                     err_code;
    
        nrf_libuarte_async_config_t nrf_libuarte_async_config = {
                .tx_pin     = TX_PIN_NUMBER,
                .rx_pin     = RX_PIN_NUMBER,
                .baudrate   = UARTE_BAUDRATE_BAUDRATE_Baud1M,
                .parity     = NRF_UARTE_PARITY_EXCLUDED,
                .hwfc       = NRF_UARTE_HWFC_DISABLED,
                .timeout_us = 100,
                .int_prio   = APP_IRQ_PRIORITY_HIGH
        };
    
        err_code = nrf_libuarte_async_init(&libuarte, &nrf_libuarte_async_config, uart_event_handler, (void *)&libuarte);
    
        APP_ERROR_CHECK(err_code);
    
        nrf_libuarte_async_enable(&libuarte);
    
        static uint8_t text[] = "Start Example\r\n";
        static uint8_t text_size = sizeof(text);
    
        err_code = nrf_libuarte_async_tx(&libuarte, text, text_size);
        APP_ERROR_CHECK(err_code);
    }
    		

    The issue that I'm having is that some lines are corrupted, for example, if i should get in the terminal something like:

    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    

    what i actually get is:

    A B C D EA B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D EA B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    A B C D E F G H 
    

    And This affects badly my file parsing. I basically need to print something like this at a high rate, every ~2-5ms. At 1Mbps the speed seems to be enough, but the problem is that line not printed complete. It seems like a pattern, like it cuts the buffer at some point. Any idea?

  • You won't be able to achieve that with the SDK.  Use the IOsonata library.  The driver implementation is much faster.  The UART drive non DMA version already 10% faster than DMA version of the nrfx.  DMA version is 20% faster.  It is also a lot simpler to use.  There are no 10000 defines to set.  UART printf is builtin as well.  

    usage exemple : https://github.com/IOsonata/IOsonata/blob/master/exemples/uart/uart_prbs_tx.cpp

    Use printf : 

    In C : UARTprintf(&g_UartDev, ...);

    In C++ object : g_Uart.printf(...); 

    There is also a UART BLE Central example code : 

    https://github.com/IOsonata/IOsonata/blob/master/ARM/Nordic/exemples/UartBleCentralDemo.cpp

Related