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

app_uart_fifo & rx length

Hi,

Sorry if the answer is obvious but I'm not getting it :-)

I looked at ble_app_uart, more specifically the uart_event_handle function and its interactions with app_uart_fifo's FIFO. I cannot see how more than one byte in the RX FIFO could be used since the uart_event_handle is called in interrupt context. By unrolling the whole code we basically have

app_uart_event_handler:
  app_fifo_put(c);
  if(FIFO_LENGTH() == 1) uart_event_handle();

uart_event_handle:
  app_fifo_get(c); // So FIFO length is back to 0 (thus 1 on next byte rvcd)

Can someone please enlighten me?

Regards

  • Hi Sébastien

    That is good observation !

    That is what I observe as well. I suspect the thought behind the implementation is to attempt to extract byte from the FIFO and transfer it to BLE. If the BLE transfer is not successful, the byte would remain in the FIFO. Also, if there are multiple bytes in the FIFO, inserting into the FIFO and extract from it again will make bytes be transmitted in the correct order.

    The bad thing about the implementation of the ble_app_uart in nRF5 SDK 11.0.0 is that it asserts when the softdevice buffers are full, i.e. when the BLE transfer is slower than incoming data from UART. I have attempted to improve this, and presented my preliminary result on my answer on this thread. I anyway plan to work further on this improved ble_app_uart example and provide better documentation. Ill post updates on the linked thread.

  • FWIW, I switched to app_scheduler to avoid having too much code in the IRQ handler part of code. This way the FIFO is really useful (well… in fact I currently don't have anything to put on the back of my device's UART so maybe theory won't match realty). The UART relevant code is now

    APP_UART_FIFO_INIT( &comm_params,
                       UART_RX_BUF_SIZE,
                       UART_TX_BUF_SIZE,
                       uart_event_schedule,
                       APP_IRQ_PRIORITY_LOW,
                       err_code);
    
    void uart_event_schedule(app_uart_evt_t * p_event) {
        uint32_t err_code;
        
        switch (p_event->evt_type) {
            case APP_UART_DATA_READY:
                // We can unconditionally post the event since this function is only
                // called on first byte put into FIFO
                err_code = app_sched_event_put(NULL, 0, uart_event_handler);
                APP_ERROR_CHECK(err_code);
                break;
    
            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;
        }
    }
    
    static void uart_event_handler(void * p_event_data, uint16_t event_size) {
            // Real processing, outside of IRQ handler with app_uart_get()
    }
    

    That way, the only job of the UART IRQ is to queue a call to the UART event handler which will then be executed in main context. That mean that if we get a burst of data on UART, handler will be executed after IRQ (because it is now interruptible), and FIFO is now really useful.

    Haven't dug into SDK code but at a first glance it seems that using this method, I'll have to guard app_uart_get() with a critical section since we could run into a race condition where we are interrupted by IRQ which will fill FIFO, while getting data from the fifo and things could go wrong.

    Regards

Related