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! 

Parents
  • 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

  • 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

  • Not sure how that could happen. nrf_libuarte_async_tx() will start a transfer, and you won't be able to start a new one until the previous is done, that will only return NRF_ERROR_BUSY. You need to post the code you use to send the data to give some more context to what is happening.

    Are you sure that the issue is on the transmitter side and not on the receiver side? Have you checked with a logic analyzer that this is also what is output on the UART lines?

Reply
  • Not sure how that could happen. nrf_libuarte_async_tx() will start a transfer, and you won't be able to start a new one until the previous is done, that will only return NRF_ERROR_BUSY. You need to post the code you use to send the data to give some more context to what is happening.

    Are you sure that the issue is on the transmitter side and not on the receiver side? Have you checked with a logic analyzer that this is also what is output on the UART lines?

Children
No Data
Related