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

Nordic UART Service custom flow control based on RX buffer size

Hi,

We have a question regarding a custom implementation of flow control for nrf52840 using SDK16 w/ SoftDevice 7.0.1. Currently, our sensor's primary micro transfers data via UART to a nrf52840 module running the peripheral ble_app_uart firmware. This issue is that we cannot use the standard uart flow control implementation within ble_app_uart (.flow_control = APP_UART_FLOW_CONTROL_ENABLED) because our micro sends bursts of data (512 bytes chunks) to the BLE module and cannot react to RTS instantaneously. 

That being said, our RX/TX buffer are set to a size which is >> the data chunks.

Ex. #define UART_RX_BUF_SIZE                8192

We were wondering if there was a way to track the current size of the RX buffer as its being fed data over UART so that we can manually deassert the RTS flag when the buffer is, for example, 75% full and re-assert RTS when the RX buffer is 25% full. This way, we will not have to worry about reacting instantaneously to RTS as I see is required by my logic analyzer when using APP_UART_FLOW_CONTROL_ENABLED.

Looking at ble_app_uart_pca10056_s140, I added some pseudo code to the following function which aims to check the current size of RX receiving buffer and assert/deassert RTS.

void uart_event_handle(app_uart_evt_t * p_event)
{
    static uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
    static uint8_t index = 0;
    uint32_t       err_code;

    switch (p_event->evt_type)
    {
        case APP_UART_DATA_READY:
          if (CURRENT_RXBUFFER_SIZE < (0.75 * UART_RX_BUF_SIZE))
          {
              **(set RTS GPIO low - enable UART data transmission from micro)**

              UNUSED_VARIABLE(app_uart_get(&data_array[index]));
              index++;

              if ((data_array[index - 1] == '\n') ||
                  (data_array[index - 1] == '\r') ||
                  (index >= m_ble_nus_max_data_len))
              {
                  if (index > 1)
                  {
                      NRF_LOG_DEBUG("Ready to send data over BLE NUS");
                      NRF_LOG_HEXDUMP_DEBUG(data_array, index);

                      do
                      {
                          uint16_t length = (uint16_t)index;
                          err_code = ble_nus_data_send(&m_nus, data_array, &length, m_conn_handle);
                          if ((err_code != NRF_ERROR_INVALID_STATE) &&
                              (err_code != NRF_ERROR_RESOURCES) &&
                              (err_code != NRF_ERROR_NOT_FOUND))
                          {
                              APP_ERROR_CHECK(err_code);
                          }
                      } while (err_code == NRF_ERROR_RESOURCES);
                  }

                  index = 0;
              }
            break;
          }
          else {
            **(set RTS GPIO high - disable UART data transmission from micro)**
          }
        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;
    }
}

Thoughts?

As I mentioned above, we cannot react instantly to RTS using the built in flow control so we'd like to trigger based on current BLE RX buffer size so as to not trigger the APP_UART_FIFO_ERROR when we transmit too much data too fast.

Thank you for your assistance.

Best,

Robert

  • Hi Robert

    The documentation you refer to is for the legacy UART peripheral that didn't have an easy DMA controller, and in this case it is true that you could lose data if the peer device sent too many bytes in one go. 

    As long as you use the newer UARTE peripheral this limit doesn't apply, and you are only limited by the size of the buffer you assign to the easy DMA controller. Additionally the easy DMA pointer is double buffered, allowing you to set up a second buffer while the UART is busy writing to the first. 

    Selecting between the two peripherals (UART vs UARTE) is normally done through the UART_EASY_DMA_SUPPORT define sdk_config.h, when using a driver that supports both (such as the app_uart library). 

    nrf_libuarte can only use the UARTE peripheral, as the name implies. 

    Best regards
    Torbjørn

  • Thank you, that was a very helpful clarification. 

    Now understanding that, I could easily solve my problem by expanding the UART_RX_BUF_SIZE to 131072 (2^17) to hold the max packet size that I'd ever send to the BLE module at a time.

    Based on app_uart.h. 131072 bytes should be an acceptable value to set.

    /**@brief UART buffer for transmitting/receiving data.
     */
    typedef struct
    {
        uint8_t * rx_buf;      /**< Pointer to the RX buffer. */
        uint32_t  rx_buf_size; /**< Size of the RX buffer. */
        uint8_t * tx_buf;      /**< Pointer to the TX buffer. */
        uint32_t  tx_buf_size; /**< Size of the TX buffer. */
    } app_uart_buffers_t;

    However, when I run the ble_app_uart example with anything >= 65536, the module immediately throws an error.

    #define UART_TX_BUF_SIZE        65536                                     /**< UART TX buffer size. */
    #define UART_RX_BUF_SIZE        65536                                     /**< UART RX buffer size. */

    How can I rectify this?

     Thank you,

    Robert

  • Hi Robert

    I believe the app_fifo library, which is used by the app_uart_fifo implementation to store TX and RX data, is limited to a size of 65536. 

    That being said it shouldn't be necessary to create such a large buffer to receive the data safely. As I mentioned in my previous reply you should be able to receive data continuously, as long as you are able to process the incoming data quicker than it is sent over the UART.  

    Unless you are using a high baudrate, and trying to send the data immediately over a slow BLE link, I wouldn't expect this to be an issue. 

    If storing the entire 128kB update in RAM is necessary you could always create a static buffer in the application, use smaller buffers for the app_uart_fifo driver, and simply copy the data into the larger buffer as it is being forwarded by the app_uart library. 

    Best regards
    Torbjørn

Related