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

nRF52840 fails to go to low-power mode when using UART RX double buffering

Hello,

We are experiencing an issue when putting the nRF52840 back to a low-power state after having had UART configured for double buffering. For context we are using nRF5 SDK v15.3.0.

We have reverted to a very simple example application which does the following:

1. Initialize UART with the following configuration:

nrfx_uarte_config_t uarte_config {
        .pseltxd = NRF_GPIO_PIN_MAP(0, 30),
        .pselrxd = NRF_GPIO_PIN_MAP(0, 29),
        .pselcts = NRF_UARTE_PSEL_DISCONNECTED,
        .pselrts = NRF_UARTE_PSEL_DISCONNECTED,
        .p_context = nullptr,
        .hwfc = NRF_UARTE_HWFC_DISABLED,
        .parity = NRF_UARTE_PARITY_EXCLUDED,
        .baudrate = NRF_UARTE_BAUDRATE_115200,
        .interrupt_priority = NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY
    };

nrfx_uarte_init(&diag_uarte_instance, &uarte_config, uarte_event_handler);

2. Setup a RX transfer for either a) single-buffering or b) double-buffering -> based on the following usage description "The secondary buffer can be set immediately after starting the transfer and will be filled when the primary buffer is full":

a)

nrfx_uarte_rx(&diag_uarte_instance, &rx0, 1);

b)

nrfx_uarte_rx(&diag_uarte_instance, &rx0, 1);
nrfx_uarte_rx(&diag_uarte_instance, &rx1, 1);

3. Transfer something over UART to trigger event handler

4. Un-initialize the UART on the event handler being triggered (we don't care about the data in this test case)

static void uarte_event_handler(const nrfx_uarte_event_t* p_event, void* p_context) {
    nrfx_uarte_uninit(&diag_uarte_instance);
}

5. Finally, call nrf_pwr_mgmt_run() to wait until next event

We find that in the case of 2.a) the processor will return to a low-power state (~3.6uA) after the transfer, however in 2.b) it will not. We also experimented with use of nrfx_uarte_rx_abort but this seems to be unrelated.

We can likely work around the issue in the short term (simply by single buffering), however would like to understand where the issue lies so it can be improved in the future.

Any insight you can offer would be much appreciated,

Rob

  • Hi,

    Please try to update to latest nrfx driver, and see it that solves the issue. There was a bug-fixe when it comes to gracefully uinit of the UARTE peripheral in nrfx v1.7.2. See e.g. this post on this topic.

  • Hi Sigurd,

    Thanks for your help and suggestion.

    We have updated to the latest SDK v16.0.0, which has nrfx v1.8.0, however the problem is still present.

    I believe the UART changes in v1.7.2 were actually something we had experienced previously and had been working around by nrfx_uarte_tx_abort  after sending over UART. However, the changes do not seem to help us on the receive side, as described above.

    If you have any further pointers or could clarify the usage of double buffering on RX transfers that would be great.

    Best regards,

    Rob

  • Hi Sigurd,

    Further to the findings above, it seems that we are unable to abort from a RX transfer and successfully return to a low-power state (refer to the sequence below). It effectively means that only after the number of bytes configured by nrfx_uarte_rx() have been received can we successfully uninitialize the peripheral, which is simple but significant problem.

    Simple RX abort sequence (fails to return to low power mode, current is ~907uA)

    nrfx_uarte_init(&diag_uarte_instance, &uarte_config, uarte_event_handler);
    nrfx_uarte_rx(&diag_uarte_instance, &rx0, 1);
    nrfx_uarte_rx_abort(&diag_uarte_instance);
    nrfx_uarte_uninit(&diag_uarte_instance);

    Note that we have tested this with SDK v16.0.0. Could you please let us know if there is something we're doing wrong here?

    Best regards,

    Rob

  • Hi,

    Simple RX abort sequence (fails to return to low power mode, current is ~907uA)

    I see the same behavior, and was able to reproduce it with this code:

    /**
     * Copyright (c) 2014 - 2019, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    /** @file
     * @defgroup Based on uart_example_main main.c
     * @{
     * @ingroup uart_example
     * @brief UART Example Application main file.
     *
     * This file contains the source code for a sample application using UART.
     *
     */
    
    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>
    
    #include "app_error.h"
    #include "nrf_delay.h"
    #include "nrf.h"
    #include "bsp.h"
    #if defined (UART_PRESENT)
    #include "nrf_uart.h"
    #endif
    #if defined (UARTE_PRESENT)
    #include "nrf_uarte.h"
    #endif
    
    #include "nrf_drv_uart.h"
    
    #define UARTE_INSTANCE  0 
    static const nrfx_uarte_t uarte = NRFX_UARTE_INSTANCE(UARTE_INSTANCE);  /**< UARTE instance. **/
    
    void uarte_event_handler(nrfx_uarte_event_t const * p_event, void* p_context)
    {
        uint32_t err_code;
    
        switch (p_event->type)
        {
            case NRFX_UARTE_EVT_TX_DONE:
    
                break;
    
            case NRFX_UARTE_EVT_RX_DONE:
    
                break;
    
            case NRFX_UARTE_EVT_ERROR:
    
                break;
    
    
    
            default:
                break;
        }
    }
    
    static uint8_t rx_buffer[1];
    
    #define UARTE_WORKAROUND_ENABLED 1
    
    int main(void)
    {
        uint32_t err_code;
    
        bsp_board_init(BSP_INIT_LEDS);
    
    
        nrfx_uarte_config_t uarte_config = NRFX_UARTE_DEFAULT_CONFIG;
        uarte_config.pseltxd = TX_PIN_NUMBER;
        uarte_config.pselrxd = RX_PIN_NUMBER; 
        uarte_config.pselcts = NRF_UARTE_PSEL_DISCONNECTED;
        uarte_config.pselrts = NRF_UARTE_PSEL_DISCONNECTED;
        uarte_config.p_context = NULL;
        uarte_config.hwfc = NRF_UARTE_HWFC_DISABLED;
        uarte_config.parity = NRF_UARTE_PARITY_EXCLUDED;
        uarte_config.baudrate = NRF_UARTE_BAUDRATE_115200;
        uarte_config.interrupt_priority = NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY;
    
        APP_ERROR_CHECK(nrfx_uarte_init(&uarte, &uarte_config, uarte_event_handler));
    
        nrfx_uarte_rx(&uarte, rx_buffer, 1);
        nrfx_uarte_rx_abort(&uarte);
        nrfx_uarte_uninit(&uarte);
    
    
        #if UARTE_WORKAROUND_ENABLED
        *(volatile uint32_t *)0x40002FFC = 0;
        *(volatile uint32_t *)0x40002FFC;
        *(volatile uint32_t *)0x40002FFC = 1;
        #endif // UARTE_WORKAROUND_ENABLED
    
    
        while (true)
        {
             // Wait for an event.
            __WFE();
            // Clear the internal event register.
            __SEV();
            __WFE();
    
        }
    }
    
    
    /** @} */

    It might seem like the mention fix in nrfx only covers TX operation, and not RX. So the workaround mentioned by hmolesworth in the other post is still needed.

    If you are using UARTE0:

        *(volatile uint32_t *)0x40002FFC = 0;
        *(volatile uint32_t *)0x40002FFC;
        *(volatile uint32_t *)0x40002FFC = 1;

    For UARTE1:

    *(volatile uint32_t *)0x40028FFC = 0;
    *(volatile uint32_t *)0x40028FFC;
    *(volatile uint32_t *)0x40028FFC = 1;

    Edit:

    I did some testing, and it seems like waiting for the RXTO event after RX_STOPPED is triggered is required. I will report this to the nrfx team, and the fix will hopefully make it into the upcoming nrfx v2.1 relase.

    void nrfx_uarte_uninit(nrfx_uarte_t const * p_instance)
    {
        uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
    
        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_instance->p_reg, NRF_UARTE_SHORT_ENDRX_STARTRX);
        nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_RXTO);
        nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPRX);
        while (!nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_RXTO))
        {}
        nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED);
        nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPTX);
        while (!nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED))
        {}
    
        nrf_uarte_disable(p_instance->p_reg);
        pins_to_default(p_instance);
    
    #if NRFX_CHECK(NRFX_PRS_ENABLED)
        nrfx_prs_release(p_instance->p_reg);
    #endif
    
        p_cb->state   = NRFX_DRV_STATE_UNINITIALIZED;
        p_cb->handler = NULL;
        NRFX_LOG_INFO("Instance uninitialized: %d.", p_instance->drv_inst_idx);
    }

    Edit2: Added a test for rx_buffer_length (Check if there is any ongoing reception)

    void nrfx_uarte_uninit(nrfx_uarte_t const * p_instance)
    {
        uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
    
        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_instance->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_instance->p_reg, NRF_UARTE_EVENT_RXTO);
        nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPRX);
        while (!nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_RXTO))
        {}
        }
        
        nrf_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED);
        nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPTX);
        while (!nrf_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_TXSTOPPED))
        {}
    
        nrf_uarte_disable(p_instance->p_reg);
        pins_to_default(p_instance);
    
    #if NRFX_CHECK(NRFX_PRS_ENABLED)
        nrfx_prs_release(p_instance->p_reg);
    #endif
    
        p_cb->state   = NRFX_DRV_STATE_UNINITIALIZED;
        p_cb->handler = NULL;
        NRFX_LOG_INFO("Instance uninitialized: %d.", p_instance->drv_inst_idx);
    }

Related