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

Shutting down UARTE cleanly, stuck at NRF_UARTE_EVENT_TXSTOPPED

I need a UARTE driver that does a few things differently to the provided UARTE driver and hence I am writing my own based on nrfx_uarte.c.  At the moment I am testing the code which initialises and deinitialises my driver: no data transmission or reception has actually occurred, just a call of the init and deinit functions of my driver code.

The pattern in nrfx_uarte_uninit() is to disable interrupts, i.e.:

nrf_uarte_int_disable(pReg, NRF_UARTE_INT_ENDRX_MASK |
                            NRF_UARTE_INT_ENDTX_MASK |
                            NRF_UARTE_INT_ERROR_MASK |
                            NRF_UARTE_INT_RXTO_MASK  |
                            NRF_UARTE_INT_TXSTOPPED_MASK);
NRFX_IRQ_DISABLE(nrfx_get_irq_number((void *) (pReg)));

...and then do the following:

// Make sure all transfers are finished before UARTE is
// disabled to achieve the lowest power consumption
nrf_uarte_shorts_disable(pReg, NRF_UARTE_SHORT_ENDRX_STARTRX);
nrf_uarte_task_trigger(pReg, NRF_UARTE_TASK_STOPRX);
nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_task_trigger(pReg, NRF_UARTE_TASK_STOPTX);
while (!nrf_uarte_event_check(pReg, NRF_UARTE_EVENT_TXSTOPPED))
{}

In my test I find that my code hangs at the line:

while (!nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED))
{}

What might I be doing wrong?  I don't think it can be to do with my interrupt handler as interrupts have just been disabled but here is that code anyway (noting that my Tx is always blocking, hence there is no Tx data event forwarding).

// The interrupt handler: only handles Rx data as Tx is blocking.
static void irqHandler(NRF_UARTE_Type *pReg,
                       CellularPortUartBuffer_t *pRxBuffer)
{
    size_t length;
    bool wasRxEvent = false;

    if (nrf_uarte_event_check(pReg, NRF_UARTE_EVENT_ERROR)) {
        // Clear any errors
        nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_ERROR);
        nrf_uarte_errorsrc_get_and_clear(pReg);
        wasRxEvent = true;
    } else if (nrf_uarte_event_check(pReg, NRF_UARTE_EVENT_ENDRX)) {
        // Clear Rx event and grab bytes used
        nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_ENDRX);
        pRxBuffer->bytesUsed += nrf_uarte_rx_amount_get(pReg);
        wasRxEvent = true;
    }

    if (nrf_uarte_event_check(pReg, NRF_UARTE_EVENT_RXTO)) {
        // Clear time-out event
        nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_RXTO);
    }

    if (nrf_uarte_event_check(pReg, NRF_UARTE_EVENT_ENDTX)) {
        nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_ENDTX);
        // Transmitter has to be stopped by triggering STOPTX task to achieve
        // the lowest possible level of the UARTE power consumption.
        nrf_uarte_task_trigger(pReg, NRF_UARTE_TASK_STOPTX);
    }

    if (nrf_uarte_event_check(pReg, NRF_UARTE_EVENT_TXSTOPPED)) {
        nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_TXSTOPPED);
    }

    if (wasRxEvent) {
        // Start the next receive
        length = CELLULAR_PORT_UART_RX_BUFFER_SIZE - pRxBuffer->bytesUsed;
        if (length > DMA_MAX_LEN) {
            length = DMA_MAX_LEN;
        }
        nrf_uarte_rx_buffer_set(pReg,
                                pRxBuffer->pBufferStart + pRxBuffer->bytesUsed,
                                length);
        nrf_uarte_task_trigger(pReg, NRF_UARTE_TASK_STARTRX);
    }
}

And, to complete the picture, here's the relevant part of the initialisation function also:

                            // Set baud rate
                            nrf_uarte_baudrate_set(pReg, baudRateNrf);

                            // Set Tx/Rx pins
                            nrf_gpio_pin_set(pinTx);
                            nrf_gpio_cfg_output(pinTx);
                            nrf_gpio_cfg_input(pinRx, NRF_GPIO_PIN_NOPULL);
                            nrf_uarte_txrx_pins_set(pReg, pinTx, pinRx);

                            // Set flow control
                            if (pinCts >= 0) {
                                pinCtsNrf = pinCts;
                                nrf_gpio_cfg_input(pinCtsNrf,
                                                   NRF_GPIO_PIN_NOPULL);
                                hwfc = NRF_UARTE_HWFC_ENABLED;
                            }
                            if (pinRts >= 0) {
                                pinRtsNrf = pinRts;
                                nrf_gpio_pin_set(pinRtsNrf);
                                nrf_gpio_cfg_output(pinRtsNrf);
                                hwfc = NRF_UARTE_HWFC_ENABLED;
                            }

                            if (hwfc == NRF_UARTE_HWFC_ENABLED) {
                                nrf_uarte_hwfc_pins_set(pReg, pinRtsNrf, pinCtsNrf);
                            }
                            nrf_uarte_configure(pReg, NRF_UARTE_PARITY_EXCLUDED, hwfc);

                            // Set interrupts and buffer and let it go
                            nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_ENDRX);
                            nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_ENDTX);
                            nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_ERROR);
                            nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_RXTO);
                            nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_TXSTOPPED);
                            nrf_uarte_rx_buffer_set(pReg, gUartData[uart].rxBuffer.pBufferStart,
                                                    DMA_MAX_LEN);
                            nrf_uarte_task_trigger(pReg, NRF_UARTE_TASK_STARTRX);
                            nrf_uarte_int_enable(pReg, NRF_UARTE_INT_ENDRX_MASK |
                                                       NRF_UARTE_INT_ENDTX_MASK |
                                                       NRF_UARTE_INT_ERROR_MASK |
                                                       NRF_UARTE_INT_RXTO_MASK  |
                                                       NRF_UARTE_INT_TXSTOPPED_MASK);
                            NRFX_IRQ_PRIORITY_SET(getIrqNumber((void *) pReg),
                                                  NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY);
                            NRFX_IRQ_ENABLE(getIrqNumber((void *) (pReg)));

EDIT: I must be doing something really basic wrong so, in order to make this really simple for you to look at, find below a ZIP file containing a stand-alone build, just one source file, showing what I'm doing.  In this build I initialise the UARTE, attempt to transmit a few bytes of data through it and then deinitialise it.  My code gets stuck during my attempt to transmit the bytes, while waiting for the event NRF_UARTE_EVENT_ENDTX.

4263.uarte_driver_test.zip

Rob

Parents
  • Some changes were recently made to the NRFX drivers which I think will fix your problem: https://github.com/NordicSemiconductor/nrfx/blob/master/CHANGELOG.md

    "Improved the UARTE driver to consume less current after the driver uninitialization. Now all clocks are disabled properly after uninitialization."
    Check it out here

    Best regards,
    Simon
  • Interesting changes but I'm not sure they have helped in my case.  As you will see from the code above, I have a buffer within my driver code which is set up to receive constantly (gUartData[uart].rxBuffer.pBufferStart) from the point that  my init() function is called, so there is always reception on-going.  Hence the code at your link:

        if (p_cb->handler)
        {
            interrupts_disable(p_instance);
        }
        // Make sure all transfers are finished before UARTE is disabled
        // to achieve the lowest power consumption.
        nrf_uarte_shorts_disable(p_reg, NRF_UARTE_SHORT_ENDRX_STARTRX);
    
        // Check if there is any ongoing reception.
        if (p_cb->rx_buffer_length)
        {
            nrf_uarte_event_clear(p_reg, NRF_UARTE_EVENT_RXTO);
            nrf_uarte_task_trigger(p_reg, NRF_UARTE_TASK_STOPRX);
        }
    
        nrf_uarte_event_clear(p_reg, NRF_UARTE_EVENT_TXSTOPPED);
        nrf_uarte_task_trigger(p_reg, NRF_UARTE_TASK_STOPTX);
    
        // Wait for TXSTOPPED event and for RXTO event, provided that there was ongoing reception.
        while (!nrf_uarte_event_check(p_reg, NRF_UARTE_EVENT_TXSTOPPED) ||
               (p_cb->rx_buffer_length && !nrf_uarte_event_check(p_reg, NRF_UARTE_EVENT_RXTO)))
        {}
    
        nrf_uarte_disable(p_reg);
    

    ...reduces to:

        nrf_uarte_int_disable(pReg, NRF_UARTE_INT_ENDRX_MASK |
                                    NRF_UARTE_INT_ENDTX_MASK |
                                    NRF_UARTE_INT_ERROR_MASK |
                                    NRF_UARTE_INT_RXTO_MASK  |
                                    NRF_UARTE_INT_TXSTOPPED_MASK);
        NRFX_IRQ_DISABLE(nrfx_get_irq_number((void *) (pReg)));
        
        // Make sure all transfers are finished before UARTE is
        // disabled to achieve the lowest power consumption
        nrf_uarte_shorts_disable(pReg, NRF_UARTE_SHORT_ENDRX_STARTRX);
        nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_RXTO);
        nrf_uarte_task_trigger(pReg, NRF_UARTE_TASK_STOPRX);
        nrf_uarte_event_clear(pReg, NRF_UARTE_EVENT_TXSTOPPED);
        nrf_uarte_task_trigger(pReg, NRF_UARTE_TASK_STOPTX);
        while (!nrf_uarte_event_check(pReg, NRF_UARTE_EVENT_TXSTOPPED) ||
               !nrf_uarte_event_check(pReg, NRF_UARTE_EVENT_RXTO)) {}
        
        // Disable the UARTE
        nrf_uarte_disable(pReg);
    

    ...which still gets stuck for me at that while() statement.

    So that you can see the full picture, my entire driver (without these latest changes above) is visible here:

    https://github.com/u-blox/cellular/blob/master/port/platform/nordic/nrf52840/src/cellular_port_uart.c

    Can you see anything else that I'm doing wrong, or have I somehow misunderstood the intention of the changes?

  • Thanks: I'm sure it is something stupid that I've [not] done, appreciate your help.

    Rob

  • I am currently looking into it. I attached a logic analyzer to look at the TX pin, and I noticed that nothing was being sent at all.

    I also saw that the event EVENTS_TXSTARTED did not happen either, thus the call to nrf_uarte_task_trigger(.. NRF_UARTE_STARTTX) has no effect.

    I will investigate more tomorrow.

    However, here is a nice bare-metal example that demonstrates, on a register level, how to configure UARTE and transmit a message. It might be nice to use it as a reference. Just copy the content of main.c into your projects main.c

    Best regards,

    Simon

  • Well, replacing what I have in main_test.c with what's in that example's main.c certainly works in that it doesn't get stuck on NRF_UARTE0->EVENTS_TXSTOPPED, it flows through and ends up in the while() loop at the end. So what am I doing wrong?

  • Right, I have it working now.  There were two problems:

    1. It helps if you enable the UARTE :-).  I had nrf_uarte_int_enable() but didn't also have nrf_uarte_enable().  However, that alone didn't fix it...

    2. My Tx is always blocking, so I handle TX_STOP etc. in my send routine.  But I'd also OR'ed the Tx events into the interrupt enable.  I had to take the Tx events out of the bitmask given to nrf_uarte_int_enable() then everything behaves.

    Phew, at last.  Thanks for trying to help me with this.

Reply
  • Right, I have it working now.  There were two problems:

    1. It helps if you enable the UARTE :-).  I had nrf_uarte_int_enable() but didn't also have nrf_uarte_enable().  However, that alone didn't fix it...

    2. My Tx is always blocking, so I handle TX_STOP etc. in my send routine.  But I'd also OR'ed the Tx events into the interrupt enable.  I had to take the Tx events out of the bitmask given to nrf_uarte_int_enable() then everything behaves.

    Phew, at last.  Thanks for trying to help me with this.

Children
No Data
Related