Hi,
I'm attempting to use the UART in a high speed configuration without flow control but I get regularely get two kinds of errors in the output:
- Lost characters
- Reversed characters
Expected UART test output:
...
0123456789
0123456789
0123456789
0123456789
0123456789
0123456789
...
Sample of actual UART output:
...
123456798
0123465789
0132456789
123456798
0123465789
0132456789
123456798
0123546789
0213456789
0123457689
...
The output is visible on different hardware:
- Our own nrf51822 boards
- pca10028 when not running the UART through the Interface chip/USB
Pseudo code for my setup:
- No softdevice used
- Only RX/TX, HWFC disabled and RTS/CTS lines disconnected
- UART_BAUDRATE_BAUDRATE_Baud921600
#define LENGTH 24 int main(void) { char buf[LENGTH] = "0123456789\r\n0123456789\r\n"; while (true) { for( int i = 0; i < LENGTH; i++) { while(app_uart_put(buf[i]) != NRF_SUCCESS); } } }
I have found potential problem sources in app_uart_fifo.c. To me, the underlying problem seems to be that calling app_uart_put() in rapid succession results in a race condition between direct calls to nrf_drv_uart_tx() in app_uart_put() and the emptying of the fifo also done with calls to nrf_drv_uart_tx() in uart_event_handler(). The former calls are made from thread mode and the later from interrupt (LOW) mode.
Example:
Say that the app uart tx fifo contains data that is being processed due to a previous call to app_uart_put(). A new call to app_uart_put() calls nrf_drv_uart_tx() precisely after a byte has been sent by the UART driver, but before the TXDRDY signal is serviced. Then:
- Two bytes could be sent in the reversed order, i.e. a byte not in the fifo is sent to the uart driver before a byte is taken from the fifo, or
- A byte is silently dropped as uart_event_handler() calls nrf_drv_uart_tx() without checking the results. What if nrf_drv_uart_tx() returns NRF_ERROR_BUSY due to the race condition above?
I modified app_uart_fifo.c successfully by doing the following:
- In app_uart_fifo.c initialize a SWI with the same priority as the UART driver (LOW)
- In app_uart_put() call app_fifo_put() and then nrf_drv_swi_trigger(). Never call nrf_drv_uart_tx() directly.
- In both uart_event_handler() and swi_event_handler() call a common function: app_uart_tx()
static void app_uart_tx(void) { app_uart_evt_t app_uart_event; // Get next byte from FIFO. if (app_fifo_get(&m_tx_fifo, tx_buffer) == NRF_SUCCESS) { while(nrf_drv_uart_tx(tx_buffer,1) != NRF_ERROR_BUSY); } if (FIFO_LENGTH(m_tx_fifo) == 0) { // Last byte from FIFO transmitted, notify the application. app_uart_event.evt_type = APP_UART_TX_EMPTY; m_event_handler(&app_uart_event); } }
This is the same logic used in uart_event_handler() today with the addition of looping the nrf_drv_uart_tx() until success.
The changes above result in stable UART output as expected, i.e. no missing or reversed characters.
Is there some way of achieving the same results without using a SWI? I tried NVIC_DisableIRQ(UART0_IRQn) together with _DSB and _ISB but never got it to work properly.
/Pablo