BLE scan and UART data reception issue

Hello, All:

I have a problem, when I turn on bluetooth scanning, my UART can't process the received data in time.

When I only use UART to send and receive data, it is normal, but after turning on the Bluetooth scanning function (after turning on the scanning, the scanned data will be transmitted to the peer device in the scanning callback), I found that the UART data cannot be received in time.

The data from the peer end will not be reported until 7 minutes or 8 minutes later. Does the data transfer process of the scan callback part have any effect on the UART?

This problem does not appear immediately. It will appear occasionally after working for a period of time, and the probability of appearing is still quite large.

This delay can have a big impact.

I am using an nRF52840 with a baud rate of 1000000 and flow control turned on.

Here's my configuration and part of the uart driver I'm using:

.overlay file:

&uart0 {
	compatible = "nordic,nrf-uarte";
	status = "okay";
	current-speed = <1000000>;
	hw-flow-control;
	tx-pin = <6>;
	rx-pin = <4>;
	rts-pin = <41>;
	cts-pin = <8>;
};

UART driver .c file:

#include <drivers/uart.h>
#include <kernel.h>

#include <logging/log.h>
LOG_MODULE_REGISTER(mt_uart, CONFIG_MT_UART_LOG_LEVEL);

#define UART_NAME "UART_0"

struct uart_data {
    void *   fifo_reserved;
    uint8_t  data[CONFIG_MT_UART_FIFO_SIZE];
    uint16_t len;
};

static const struct device *   m_uart;
static struct k_work_delayable uart_work;
static void (*m_cb)(uint8_t *data, uint16_t length);

static K_SEM_DEFINE(uart_init_sem, 0, 1);
static K_FIFO_DEFINE(fifo_uart_tx);
static K_FIFO_DEFINE(fifo_uart_rx);

static void uart_work_handler(struct k_work *item)
{
    struct uart_data *buf = k_malloc(sizeof(*buf));
    if (buf) {
        buf->len = 0;
    } else {
        LOG_WRN("Not able to allocate UART receive buffer");
        k_work_reschedule(&uart_work, K_MSEC(CONFIG_MT_UART_RX_TIMEOUT));
        return;
    }

    uart_rx_enable(m_uart, buf->data, sizeof(buf->data), 100);
}

static void _uart_tx_abort(struct uart_event *evt, uint8_t **ta_buf,
                           size_t *ta_len)
{
    struct uart_data *buf;

    if (!*ta_buf) {
        *ta_buf = (uint8_t *)evt->data.tx.buf;
    }

    *ta_len += evt->data.tx.len;
    buf = CONTAINER_OF(*ta_len, struct uart_data, data);

    (void)uart_tx(m_uart, &buf->data[*ta_len], buf->len - *ta_len,
                  SYS_FOREVER_MS);
}

static void _uart_tx_done(struct uart_event *evt, uint8_t **ta_buf,
                          size_t *ta_len)
{
    struct uart_data *buf;

    if ((evt->data.tx.len == 0) || (!evt->data.tx.buf)) {
        LOG_WRN("Invalid UART_TX_DONE event.");
        return;
    }

    if (*ta_buf) {
        buf     = CONTAINER_OF(*ta_buf, struct uart_data, data);
        *ta_buf = NULL;
        *ta_len = 0;
    } else {
        buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data, data);
    }

    k_free(buf);

    buf = k_fifo_get(&fifo_uart_tx, K_NO_WAIT);
    if (!buf) {
        LOG_DBG("Uart tx fifo is empty.");
        return;
    }

    if (uart_tx(m_uart, buf->data, buf->len, SYS_FOREVER_MS)) {
        LOG_ERR("Failed to send data over UART");
    }
}

static void uart_cb(const struct device *dev, struct uart_event *evt,
                    void *user_data)
{
    const char *s[] = { "TX_DONE",    "TX_ABORTED", "RX_READY", "RX_BUF_REQ",
                        "RX_BUF_REL", "RX_DISABLE", "RX_STOP" };
    LOG_INF("Evt %s.", s[evt->type]);

    /* Uart tx aborted buffer. */
    static uint8_t *ta_buf = NULL;
    static size_t   ta_len = 0;

    static uint8_t *current_buf;
    static bool     buf_release;

    struct uart_data *buf;

    switch (evt->type) {
        case UART_TX_DONE:
            _uart_tx_done(evt, &ta_buf, &ta_len);
            break;
        case UART_TX_ABORTED:
            _uart_tx_abort(evt, &ta_buf, &ta_len);
            break;
        case UART_RX_RDY:
            buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data, data);
            buf->len = evt->data.rx.len + evt->data.rx.offset;

            if (buf->len < CONFIG_MT_UART_FIFO_SIZE) {
                current_buf = evt->data.rx.buf;
                buf_release = true;
                uart_rx_disable(m_uart);
            } else if (!buf_release) {
                k_fifo_put(&fifo_uart_rx, buf);
            }
            break;
        case UART_RX_BUF_REQUEST:
            buf = k_malloc(sizeof(*buf));
            if (buf) {
                buf->len = 0;
                uart_rx_buf_rsp(m_uart, buf->data, sizeof(buf->data));
            } else {
                LOG_WRN("Not able to allocate UART receive buffer");
            }
            break;
        case UART_RX_BUF_RELEASED:
            buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data, data);

            if (buf_release) {
                if (current_buf != evt->data.rx_buf.buf) {
                    k_free(buf);
                    buf_release = false;
                    current_buf = NULL;
                } else {
                    k_fifo_put(&fifo_uart_rx, buf);
                }
            }
            break;
        case UART_RX_DISABLED:
            uart_work_handler(NULL);
            break;
        case UART_RX_STOPPED:
            // Nothing to do here.
            LOG_WRN("Uart rx stop reason: %d.", evt->data.rx_stop.reason);
            break;
        default:
            break;
    }
}

int mt_uart_tx(void *data, uint16_t len)
{
    __ASSERT(data != NULL, "Invalid input.");
    __ASSERT(len != 0, "Invalid input.");

    for (uint16_t pos = 0; pos != len;) {
        struct uart_data *tx = k_malloc(sizeof(*tx));

        if (!tx) {
            LOG_WRN("Unable to allocate UART sending buffer.");
            return -1;
        }

        int ofs = len - pos;
        tx->len = (ofs > CONFIG_MT_UART_FIFO_SIZE) ? CONFIG_MT_UART_FIFO_SIZE : ofs;

        memcpy(tx->data, (uint8_t *)data + pos, tx->len);
        pos += tx->len;

        int err = uart_tx(m_uart, tx->data, tx->len, SYS_FOREVER_MS);
        if (err) {
            k_fifo_put(&fifo_uart_tx, tx);
        }
    }

    return 0;
}

void mt_uart_init(void (*cb)(uint8_t *, uint16_t))
{
    __ASSERT(cb != NULL, "Invalid input.");
    int err = 0;

    m_cb   = *cb;
    m_uart = device_get_binding(UART_NAME);
    __ASSERT(m_uart != NULL, "Uart not found.");

    err = uart_callback_set(m_uart, uart_cb, NULL);
    __ASSERT(err == 0, "Uart callback set fail %d.", err);

    k_work_init_delayable(&uart_work, uart_work_handler);
    k_sem_give(&uart_init_sem);

    struct uart_data *buf = k_malloc(sizeof(*buf));
    __ASSERT(buf != NULL, "Not able to allocate UART receive buffer");
    buf->len = 0;

    err = uart_rx_enable(m_uart, buf->data, sizeof(buf->data), 50);
    __ASSERT(err == 0, "Uart failed to enable receive %d.", err);

    LOG_INF("Uart init success.");
    return;
}

static void uart_dpt_thread(void)
{
    k_sem_take(&uart_init_sem, K_FOREVER);

    for (;;) {
        struct uart_data *buf = k_fifo_get(&fifo_uart_rx, K_FOREVER);
        m_cb(buf->data, buf->len);
        k_free(buf);
    }
}

K_THREAD_DEFINE(uart_dpt_thread_id, CONFIG_MT_UART_THREAD_STACK_SIZE,
                uart_dpt_thread, NULL, NULL, NULL, CONFIG_MT_UART_THREAD_PRIORITY,
                0, 0);

Kconfig file:

config MT_UART_LOG_LEVEL
    int "log level of uart module"
    range 0 4
    default 3
    help
      Indicate log level.

config MT_UART_FIFO_SIZE
    int "Uart fifo size"
    range 64 16384
    default 64

config MT_UART_RX_TIMEOUT
    int "Uart receive timeout"
    range 50 1000
    default 50

config MT_UART_THREAD_PRIORITY
    int "thread priority"
    default 7

config MT_UART_THREAD_STACK_SIZE
	int "Thread stack size"
	default 1024
	help
	  The stack size used by the thread.

prj.conf file:

CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=y
CONFIG_UART_0_NRF_TX_BUFFER_SIZE=64
CONFIG_UART_0_NRF_HW_ASYNC=y
CONFIG_UART_0_NRF_HW_ASYNC_TIMER=2
CONFIG_MT_UART_FIFO_SIZE=512

Is there any problem?

Hope to get your reply soon, thanks!

Parents
  • Hello Kenny,

    It is hard to say what may cause the issue.

    Could you enable logging to see if you get some more information about the fault? Assert? Hard fault? Error?

    I can see that you have shared a lot of files, but would you be able to share a minimal project, including main.c that reproduces the issue, and provide some detailed steps how to reproduce it? That would make it much easier to debug on my side.

    Best regards,

    Simon

Reply
  • Hello Kenny,

    It is hard to say what may cause the issue.

    Could you enable logging to see if you get some more information about the fault? Assert? Hard fault? Error?

    I can see that you have shared a lot of files, but would you be able to share a minimal project, including main.c that reproduces the issue, and provide some detailed steps how to reproduce it? That would make it much easier to debug on my side.

    Best regards,

    Simon

Children
  • Here is my test demo.

    Originally, I used nRF9160 to communicate with it. The nRF9160 sends data through key control. Now it is connected to the PC and tested through the serial port tool.

    The device runs normally at the beginning, wait for a period of time (a few minutes or more) and then send data through the serial port assistant on the PC side, the device cannot receive it immediately, and prompts to receive data after a long delay.

  • Thanks for uploading the project. I will take a look at it tomorrow

    Best regards,

    Simon

  • Sorry for the delay on this, I've been quite busy lately. I will priotize your issue tomorrow

    Best regards,

    Simon

  • I'm not able to reproduce it. I did the following:

    • Modifed the file nrf52840dk_nrf52840.overlay like shown below, so uart0 is connected to the interface MCU and the computer

    &uart0 {
    	compatible = "nordic,nrf-uarte";
    	status = "okay";
    	current-speed = <1000000>;
    //	hw-flow-control;
    	/*tx-pin = <6>;
    	rx-pin = <4>;
    	rts-pin = <41>;
    	cts-pin = <8>;*/
    };
    

    • Built and flashed the sample to an nRF52840DK v3.0.0
    • Opened RTT Viewer and a Termite instance with the following settings

    • I saw that the program started by looking at the RTT logs
    • Waited around 7 minutes and sent the following through termite: "1234567890" and got the following response in RTT 1-2 seconds later:

    00> [00:07:45.290,069] <inf> mt_uart: Evt RX_READY.
    00> [00:07:45.290,191] <inf> mt_uart: Evt RX_BUF_REL.
    00> [00:07:45.290,222] <inf> mt_uart: Evt RX_BUF_REL.
    00> [00:07:45.290,252] <inf> mt_uart: Evt RX_DISABLE.
    00> [00:07:45.290,313] <inf> mt_uart: Evt RX_BUF_REQ.
    00> [00:07:45.290,435] <inf> event_manager: [0038] UART_RECV_REPORT
    00> [00:07:45.290,527] <inf> main: UART received data:
    00>                                31 32 33 34 35 36 37 38  39 30 0a                |12345678 90.

    • I waited 10 more minutes and sent a new string, but I got the response immediately

    Maybe it is because I'm in home office without much advertising around, and the scanner is not too busy parsing packets. I will try in the office on Monday.

    Best regards,

    Simohn

Related