Losing uart characters - activating UART in place of UARTE in nRF52840

I am receiving characters on the UART of nRF52840. In my application I receive characters at 115 kbaud. As I must inspect each character I set the the receive buffer size to 1 so I receive an interrupt for each character received. I then set a flag of "interrupt pending" and put the work into the scheduler for a task to process the data. I do not need to flag every interrupt, as the task will read all the characters that have arrived in the fifo and processes.

However I am missing interrupts for some characters that arrive and I receive a uart error, and sometimes  miss characters altogther, so my program fails when expecting specific data.

I am assuming that when using easy_dma, there is no fifo for the uart, rather characters are transferred to a memory buffer by dma, an interrupt is generated, and the data is moved from the memory buffer to the fifo buffer; the memory buffer is then set for the next character. This works if all interrupts occur as data is moved to the fifo. and my task will read.

What I really need is to set a memory buffer that is sufficiently large not to miss characters, and will generate an interrupt when data has arrived into the memory buffer and I can read all arrived characters.

This would be the behaviour of the UART, which has 6 deep hardware fifo, and would capture all the incoming characters and release them each time RX is read.

My question: Does the UARTE also use the hardware fifo to capture incoming characters that can be transferred to memory by DMA and interrupt generated for each character?

How do I use UART in place of UARTE, as all the code forces UARTE even if NRF_SERIAL_MODE_IRQ is selected,

#if defined(UARTE_PRESENT) && defined(UART_PRESENT)
    drv_config.use_easy_dma = (p_config->mode == NRF_SERIAL_MODE_DMA);
#endif

Here is my configuration code. I increase IRQ to try to ensure I capture interrupts. I am assuming interrupts are disabled somewhere, but not sure where,

#define SERIAL1_FIFO_TX_SIZE 64

#define SERIAL1_FIFO_RX_SIZE 64

NRF_SERIAL_QUEUES_DEF(serial1_queues, SERIAL1_FIFO_TX_SIZE, SERIAL1_FIFO_RX_SIZE);

#define SERIAL1_BUFF_TX_SIZE 16
#define SERIAL1_BUFF_RX_SIZE 1

NRF_SERIAL_BUFFERS_DEF(serial1_buffs, SERIAL1_BUFF_TX_SIZE, SERIAL1_BUFF_RX_SIZE);

NRF_SERIAL_CONFIG_DEF(serial1_config, NRF_SERIAL_MODE_DMA,
                      &serial1_queues, &serial1_buffs, gprsUartEventHandler, NULL);

NRF_SERIAL_UART_DEF(serial1_uarte, 1);

#define GPRS_UART_CONFIG_IRQ_PRIORITY 2

I have some oscilloscope traces to show enter/exit of the interrupt routine and the task is this helps to understand the issue

Parents
  • Hi,

    This would be the behaviour of the UART, which has 6 deep hardware fifo, and would capture all the incoming characters and release them each time RX is read.

    You are rigth that the UART RX FIFO is 6 bytes. The UARTE use DMA however, so the data is written directly to a buffer that is as large as needed, offloading the CPU. This means that for most real applications (where the application is not only handlig UART), the UARTE is better, and I struggle to see why you want to use the deprecated UART peripheral instead?

    My question: Does the UARTE also use the hardware fifo to capture incoming characters that can be transferred to memory by DMA and interrupt generated for each character?

    The FIFO is still there, but you cannot get an interrupt for each received byte. That is a limitation in UARTE, and if you need to know the number of received bytes before the Rx buffer is full, you may want to have a look at libuarte, which use a TIEMR in counter mode and PPI to count received bytes.

    How do I use UART in place of UARTE, as all the code forces UARTE even if NRF_SERIAL_MODE_IRQ is selected

    I do not immediately recognize these code snippets. Where does it come from? That said, I can think of few applications where it would make sense to use the legacy UART peripheral.

Reply
  • Hi,

    This would be the behaviour of the UART, which has 6 deep hardware fifo, and would capture all the incoming characters and release them each time RX is read.

    You are rigth that the UART RX FIFO is 6 bytes. The UARTE use DMA however, so the data is written directly to a buffer that is as large as needed, offloading the CPU. This means that for most real applications (where the application is not only handlig UART), the UARTE is better, and I struggle to see why you want to use the deprecated UART peripheral instead?

    My question: Does the UARTE also use the hardware fifo to capture incoming characters that can be transferred to memory by DMA and interrupt generated for each character?

    The FIFO is still there, but you cannot get an interrupt for each received byte. That is a limitation in UARTE, and if you need to know the number of received bytes before the Rx buffer is full, you may want to have a look at libuarte, which use a TIEMR in counter mode and PPI to count received bytes.

    How do I use UART in place of UARTE, as all the code forces UARTE even if NRF_SERIAL_MODE_IRQ is selected

    I do not immediately recognize these code snippets. Where does it come from? That said, I can think of few applications where it would make sense to use the legacy UART peripheral.

Children
  • I have set SERIAL1_BUFF_RX_SIZE to 1, so this generates an interrupt for each character. However I do not get an interrupt for some characters, and instead I get uart error (no stop bit). I have scope trace to demonstrate the issue.

  • I see. But I don't really understand your config or what you are doing, partly because the code snippets refer to code that is not provided by us so I can only assume what the configs does. Can you elaborate more about your exact configuration and what you are seeing?

  • This is library code from Nordic. I add the full initialisation. I do not recall where this comes from originally, but it remains in the Nordic library.

    This code appears to work by accepting writes to the FIFO (circular buffer) and then copies into the BUFF_TX (linear buffer) for transmission. RX is similar and on an interrupt incoming characters are written to the FIFO_RX and can be taken out by a scheduled task - this keeps the interrupt routine short and allows interrupts to be reenabled.

    I am happy that this works as intended, except that it misses some characters when interrupts are disabled. I was expecting characters to be buffered in the FIFO and be released when interrupts are re-enabled.

    Regards

    Malcolm

    define RTS_PIN_DISCONNECTED NRF_UART_PSEL_DISCONNECTED
    #define    CTS_PIN_DISCONNECTED NRF_UART_PSEL_DISCONNECTED


    static void readGprsByteEvent(void * p_event_data, uint16_t event_size);
    static void writeGprsConfirm();
    static void gprsUartEventHandler(struct nrf_serial_s const * p_serial,
                                             nrf_serial_event_t event);

    #define SERIAL_GPRS_FIFO_TX_SIZE 64
    #define SERIAL_GPRS_FIFO_RX_SIZE 64

    NRF_SERIAL_QUEUES_DEF(serial_gprs_queues, SERIAL_GPRS_FIFO_TX_SIZE, SERIAL_GPRS_FIFO_RX_SIZE);

    #define SERIAL_GPRS_BUFF_TX_SIZE 16
    #define SERIAL_GPRS_BUFF_RX_SIZE 1

    NRF_SERIAL_BUFFERS_DEF(serial_gprs_buffs, SERIAL_GPRS_BUFF_TX_SIZE, SERIAL_GPRS_BUFF_RX_SIZE);

    NRF_SERIAL_CONFIG_DEF(serial_gprs_config, NRF_SERIAL_MODE_DMA,
                          &serial_gprs_queues, &serial_gprs_buffs, gprsUartEventHandler, NULL);

    #define UART_GPRS_INSTANCE_NUMBER 0

    NRF_SERIAL_UART_DEF(serial_gprs_uart, UART_GPRS_INSTANCE_NUMBER);

    #define GPRS_UART_CONFIG_IRQ_PRIORITY UART_DEFAULT_CONFIG_IRQ_PRIORITY

    //#define GPRS_UART_CONFIG_IRQ_PRIORITY 2

    static volatile bool gprsRxPending;
    static volatile bool gprsRxPendingCount;
    static volatile bool gprsUartError;
    //static uint8_t serial_gprs_buffer2;

    /***********************************************************************************
        Init GPRS, register GPRS callbacks.

        Parameters:
            none

        Return:
            none

     ***********************************************************************************/
    PROGMEM_DECLARE_CONST(char) gprsUartMsgOpenError[]={"*** GPRS uart open error\n"};

    void appStartGprsUsartManager(nrf_uart_baudrate_t baudrate)
    {
        ret_code_t ret;
        nrf_drv_uart_config_t m_uarte_gprs_drv_config;

        m_uarte_gprs_drv_config.baudrate = baudrate;
        m_uarte_gprs_drv_config.hwfc = NRF_UART_HWFC_DISABLED;
        m_uarte_gprs_drv_config.interrupt_priority = GPRS_UART_CONFIG_IRQ_PRIORITY;
        m_uarte_gprs_drv_config.parity = NRF_UART_PARITY_EXCLUDED;
        m_uarte_gprs_drv_config.pselcts = CTS_PIN_DISCONNECTED;
        m_uarte_gprs_drv_config.pselrts = RTS_PIN_DISCONNECTED;
        m_uarte_gprs_drv_config.pselrxd = UART_GPRS_RX_PIN;
        m_uarte_gprs_drv_config.pseltxd = UART_GPRS_TX_PIN;
        gprsRxPending = false;
        gprsRxPendingCount = 0;
        gprsUartError = false;
        ret = nrf_serial_init(&serial_gprs_uart, &m_uarte_gprs_drv_config, &serial_gprs_config);
        APP_ERROR_CHECK(ret);
    }

  • I have done a scope capture of the issue to explain the problem, but I do not know how I can upload.

    In the trace the first character is missed altogether. I receive 6 characters, but there are only 5 interrupts.

Related