Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

UARTE/Zephyr ASYNC API

Hi,

I am using an nRF52 DK board to perform some very simple tests. All I need to do is receive a line of text over serial, perform some calculations and return another line of text. I decided it would be least effort to use the Zephyr async API (and hence the UARTE).  My plan was to:

- Use a buffer large enough to accept my largest command

- Use uart_rx_enable() to kick off the Rx activity, and then wait on a semaphore

- In the uart callback, catch the UART_RX_RDY event and signal (with the semaphore) that the buffer is now full

However, when I try this, the callback is executed very quickly, with an unexpected (to me at least!) BUF_RELEASED event (with no data received), followed by an RX_BUF_REQUEST event (which I expected, but will ignore as I have no need of another buffer). 

I have instrumented the uarte_nrfx_isr_async() handler with this line:

    printk("uarte_nrfx_isr_async: Err:%d Rx: Rdy%d End%d Strt%d TO%d  Tx:End%d Stop%d\n",
        nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ERROR),
        nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXDRDY),
        nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX),
        nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED),
        nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXTO),
        nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX),
        nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED));

Producing:

uarte_nrfx_isr_async: Err:0 Rx: Rdy0 End1 Strt1 TO0 Tx:End1 Stop1

Two UARTE events are set: NRF_UARTE_EVENT_ENDRX and  NRF_UARTE_EVENT_RXSTARTED. The ENDRX event triggers the endrx_isr(), which is what produces the BUF_RELEASED event.

As far as I can see from the API docs, this is nearly how things should work.. except for the ENDRX event. What is causing this immediately on enabling the UART receive?  I've been doing embedded systems for 25 years from bare-metal to Linux, but this is my first time with Zephyr and the Nordic platform, so I have no illusions that I can have read every piece of available documentation in complete detail Slight smile

Any ideas on what I've done wrong, or what I can do to debug this greatly appreciated!


Thanks,

Martin

Hello World! nrf52dk_nrf52832
UART.. device 0x6ae0
UART.. Buffer: 0x2000048c
main..  waiting for data
UARTRX..  setting up for data into buffer 0x2000048c
uarte_nrfx_isr_async: Err:0 Rx: Rdy0 End1 Strt1 TO0  Tx:End1 Stop1
UART Callback 4 0x2000048c
UART RX Buf 0x2000048c released, no data
UART Callback 3    (nil)
UART RX Buf request-ignoring
0 UARTRX SEM..  sem take
uarte_nrfx_isr_async: Err:0 Rx: Rdy0 End0 Strt0 TO1  Tx:End1 Stop1
UART Callback 5    (nil)
UART RX Disabled
0 UARTRX SEM..  sem timeout -11
1 UARTRX SEM..  sem take
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>

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

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// UART Hello World
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define UART0 DT_NODELABEL(uart0)
const struct device *const uart_dev = DEVICE_DT_GET(UART0);
struct k_sem uart_rxdrdy;
#define RX_BUFLEN (256)

static volatile char rx_buffer[RX_BUFLEN];
void ASSERT(int x)
{
    if (x)
        return;

    printk("ASSERTION FAILED\n");
    while(1);
}

void uart_callback(const struct device *dev, struct uart_event *evt, void *user_data)
{
    printk("UART Callback %d %8p\n", evt->type, evt->data.rx.buf);
    switch (evt->type)
    {
        case UART_RX_RDY:
            printk("UART RX Ready - notify receiving function\n");
            k_sem_give(&uart_rxdrdy);
            break;
        case UART_RX_BUF_REQUEST:
            printk("UART RX Buf request-ignoring\n");
            break;
        case UART_RX_BUF_RELEASED:
            if (evt->data.rx.len == 0) {
                printk("UART RX Buf %p released, no data\n", evt->data.rx.buf);
            }
            else{
                printk("UART RX Buf released\n");
            }
            break;
        case UART_RX_DISABLED:
            printk("UART RX Disabled\n");
            break;
        case UART_RX_STOPPED:
            printk("UART RX Stopped\n");
            break;
        default:
            printk("UNhandled UART event: %d\n", evt->type);
            break;
    }
}

uint8_t *uart_rx(void)
{
    /* This is run in the context of the main thread */

    /*  We are not expecting to need more than one buffer at a time as we send in a while CW instruction and
    then make a response. The inputs are all well controlled.    */
    for (int j = 0; j < RX_BUFLEN; j++)
    {
        rx_buffer[j] = 0;
    }
    uint8_t *buf = (uint8_t *)rx_buffer;
    printk("UARTRX..  setting up for data into buffer %p\n", buf);
    int ret = uart_rx_enable(uart_dev, buf, RX_BUFLEN, 30*1000*1000);
    ASSERT (ret >= 0);
    int sem_taken = 0;
    int i = 0;
    do{
        printk("%d UARTRX SEM..  sem take\n", i);
        sem_taken = k_sem_take(&uart_rxdrdy, K_MSEC(10000));
        switch (sem_taken)
        {
            case 0:
                printk("%d UARTRX SEM..  sem taken %d\n",i, sem_taken);
                break;
            case -EAGAIN:
                printk("%d UARTRX SEM..  sem timeout %d\n",i, sem_taken);
                break;
            case -EBUSY:
                printk("%d UARTRX SEM..  sem busy %d\n",i, sem_taken);
                break;
        }
        i++;
    } while (sem_taken != 0);

    return buf;
}

void init_uart(void)
{
    k_sem_init(&uart_rxdrdy, 0, 1);
    printk("UART.. device %p\n", uart_dev);
    ASSERT (uart_dev != NULL);

    printk("UART.. Buffer: %p\n", rx_buffer);
    int ret = uart_callback_set(uart_dev, uart_callback, NULL);
    ASSERT (ret == 0);
}


void main(void)
{
	printk("Hello World! %s\n", CONFIG_BOARD);
    init_uart();
	while(1)
	{
		printk("main..  waiting for data\n");
		char *buf = uart_rx();
		printk("main..  have received '%s'\n", buf);
	}
}

 

Parents
  • Some progress with the original problem using the Zephyr API: don't make your Rx buffer too big... 256 bytes does not work (I think because the hardware only has an 8-bit counter - an error for this from the driver would be... helpful!) . Now I don't get the extra END event. but the READY events are now not getting through to my handler... 

  • Hi again

    I agree the Zephyr drivers *should* work, I was suggesting to try the nrfx driver mostly to see if you got any more helpful debug/error messages out of them.

    Martin Thompson said:
    It does not link, presumably I have to tell the tools I need another library.

    I would suspect that perhaps you'll have to enable your nrfx_uarte peripheral in prj.conf for it to link?

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/kconfig/index.html#CONFIG_NRFX_UARTE0

    Or it could be that you're seemingly using the hardware access layer driver instead of the actual uarte driver, which can be found at /v2.2.0/zephyr/drivers/serial/uart_nrfx_uarte.c

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrfx/drivers/uarte/index.html

    Martin Thompson said:
    Some progress with the original problem using the Zephyr API: don't make your Rx buffer too big... 256 bytes does not work

    Glad to see you made progress with the Zephyr API, the 8-bit limitation comes from the maximum size of the uarte receive buffer:

    https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fuarte.html&cp=5_2_0_34_9_12&anchor=register.RXD.MAXCNT

    I'm not sure off the top of my head why you wouldn't get the READY events through to your handler, but I'll look into it.

    Martin Thompson said:
    the combination of confusions between SDK/rfconnect, Zephyr/non-zephyr, new/old UART, UARTE/UART drivers makes it *incredibly* difficult to find answers for yourself.

    I agree this is intimidating when getting into this environment, but I think it mostly comes down to the documentation not always making it obvious what is new/old or common/fringe.

    SDK/nRFConnect: The nRF5 SDK is old and outdated, don't use it. NCS (nRF Connect SDK) with Zephyr is what our developers are working on these days and will always be more up to date.

    Zephyr/non-Zephyr: I would recommend always using Zephyr's drivers, and would only suggest using our drivers by themselves for debugging as they are being used under the hood when using Zephyr.

    UARTE/UART: UARTE is simply UART with EasyDMA, meaning data is sent directly from/to ram without cpu intervention. UART is normal good old UART. UARTE is default, and I would recommend using it, especially if you're using BLE, as using EasyDMA can help you avoid BLE and UART fighting for CPU priority.

    Best regards,

    Einar

Reply
  • Hi again

    I agree the Zephyr drivers *should* work, I was suggesting to try the nrfx driver mostly to see if you got any more helpful debug/error messages out of them.

    Martin Thompson said:
    It does not link, presumably I have to tell the tools I need another library.

    I would suspect that perhaps you'll have to enable your nrfx_uarte peripheral in prj.conf for it to link?

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/kconfig/index.html#CONFIG_NRFX_UARTE0

    Or it could be that you're seemingly using the hardware access layer driver instead of the actual uarte driver, which can be found at /v2.2.0/zephyr/drivers/serial/uart_nrfx_uarte.c

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrfx/drivers/uarte/index.html

    Martin Thompson said:
    Some progress with the original problem using the Zephyr API: don't make your Rx buffer too big... 256 bytes does not work

    Glad to see you made progress with the Zephyr API, the 8-bit limitation comes from the maximum size of the uarte receive buffer:

    https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fuarte.html&cp=5_2_0_34_9_12&anchor=register.RXD.MAXCNT

    I'm not sure off the top of my head why you wouldn't get the READY events through to your handler, but I'll look into it.

    Martin Thompson said:
    the combination of confusions between SDK/rfconnect, Zephyr/non-zephyr, new/old UART, UARTE/UART drivers makes it *incredibly* difficult to find answers for yourself.

    I agree this is intimidating when getting into this environment, but I think it mostly comes down to the documentation not always making it obvious what is new/old or common/fringe.

    SDK/nRFConnect: The nRF5 SDK is old and outdated, don't use it. NCS (nRF Connect SDK) with Zephyr is what our developers are working on these days and will always be more up to date.

    Zephyr/non-Zephyr: I would recommend always using Zephyr's drivers, and would only suggest using our drivers by themselves for debugging as they are being used under the hood when using Zephyr.

    UARTE/UART: UARTE is simply UART with EasyDMA, meaning data is sent directly from/to ram without cpu intervention. UART is normal good old UART. UARTE is default, and I would recommend using it, especially if you're using BLE, as using EasyDMA can help you avoid BLE and UART fighting for CPU priority.

    Best regards,

    Einar

Children
Related