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.

  • 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.

Reply Children
  • Hi Malcolm,

    Ah, I see. That is why I did not find this. The Serial port library has some issues and was removed from SDK 17.x.x. I would recommend that you use libuarte instead, which is it's replacement. This is also present in SDK 16 (though I would consider migrating to 17.1.0).

    Einar

  • Hi Einor

    Whilst there may be isues in V16 I have not encountered, and we are close to release of software for a pjoect so I do not want to do a complete migration to a new version unless absolutely essential as I have hacked some of the code.

    The issue that remains appears to be that characters are not being buffered in a FIFO and are lost or corrupted by missing parts. I can send an image of a capture from a scope that best illustrates. I cannot upload the image as it pdf file and not allowed.

    I think the main issue for me is that code somewhere disables interrupts for an extended period, and I must find and solve that problem.

  • I see. I am not able to say what the issue is based on what I know, but an important limitation of the serial port library you are using is that it does not do EasyDMA double buffering. So when the buffer is full, you get a NRF_SERIAL_EVENT_RX_DATA event, and only after that has been handled the library start Rx again (using the same old Rx buffer). You can see this from the implementation of uart_event_handler() in nrf_serial.c. So if you spend too long time handling the received bytes, or something else prevents the library from starting Rx again, you can loose incoming data. (This is one of the things that has improved in libuarte).

  • I appear to have encountered a second problem with missing interrupts and characters. I am sure there is not hardware FIFO for the uarte, rather it relies on direct DMA transfer to a buffer. In my case I must process every incoming character. Therefore I will need to ensure that DMA double buffering is configured. I will try libuart to determine if it is a fix.

  • I have ported LIBUARTE into my application and it is now working when I test the uart in isolation, However when I enable the BLE stack I receive an initialisation error of 0x1001, which indicates a NRF_ERROR_SDM_INCORRECT_INTERRUPT_CONFIGURATION. As I am using the default values from LIBUART and ble init, what would cause this conflict and how to correct?

Related