app_uart reinit after low power mode

I have been working on trying to get the app_uart to go into low power mode to reduce current draw. I have looked on the forum and have not found a clear answer. 

I'm running on the PCA10056 and a slightly modified version for the ble_app_uart example. 

TLDR Changes to ble_app_uart:

  • APP_ADV_DURATION is set to 1000 (So that BLE goes idle after 10Sec)
  • BLE_ADV_EVT_IDLE doesn't call sleep_mode_enter(); (so we can see current draw in system_on with no BLE active)

With only the above changes in place we see the following in release mode:

~2mA with BLE advertising 

~1.6mA with BLE IDLE (i.e. after BLE_ADV_EVT_IDLE occurs)

1.6mA with no BLE active is very high and my goal was to reduce this into the uA range. What would be the proper way to disable the uart before using nrf_pwr_mgmt_run(); and then reinit after nrf_pwr_mgmt_run();has returned? 

Thanks

  • Hi 

    Do you know when you are going to use the UART interface or not? 

    Are you using the UART both in TX and RX mode?

    As long as the UART is enabled it will increase the sleep current by a significant amount. When RX is enabled in particular you will get more than 1mA current draw continuously, which seems in line with what you are seeing. With only TX enabled you get a similar current when you are actively transmitting, down to around 20uA when you are not transmitting anything. 

    Really the only way to get low sleep currents when using the UART is to disable it when it's not needed, by calling app_uart_close()

    Best regards
    Torbjørn

  • Torbjørn,

    Thanks for the replay. My use case for the UART is very simple, I plan to read data from a sensors that streams it to the RX as soon as I turn on the sensor. So I will have a set interval in which I want the UART to be enabled. 

    I have come across app_uart_close() and it does seem to disable the UART and lower current draw. However I can't seem to get the UART to reinit after existing sleep.

    I have tried using the same init block provided in the example:

    static void idle_state_handle(void)
    {
        if (NRF_LOG_PROCESS() == false && tx_complete_flag)
        {
            app_uart_close();
            nrf_pwr_mgmt_run();
            uart_init();
        }
    }
    
    static void uart_init(void)
    {
        uint32_t                     err_code;
        app_uart_comm_params_t const comm_params =
        {
            .rx_pin_no    = RX_PIN_NUMBER,
            .tx_pin_no    = TX_PIN_NUMBER,
            .rts_pin_no   = RTS_PIN_NUMBER,
            .cts_pin_no   = CTS_PIN_NUMBER,
            .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
            .use_parity   = false,
    #if defined (UART_PRESENT)
            .baud_rate    = NRF_UART_BAUDRATE_115200
    #else
            .baud_rate    = NRF_UARTE_BAUDRATE_115200
    #endif
        };
    
        APP_UART_FIFO_INIT(&comm_params,
                           UART_RX_BUF_SIZE,
                           UART_TX_BUF_SIZE,
                           uart_event_handle,
                           APP_IRQ_PRIORITY_LOWEST,
                           err_code);
        APP_ERROR_CHECK(err_code);
    }

    However the issue seem that  APP_UART_FIFO_INIT may not be suitable for calling more than once. What would be the proper way to reinit the UART after calling app_uart_close()?

    Thanks,

    Eric 

  • Hi Eric

    It's correct that using the macro doesn't really work if you need to initialize the library multiple times. In this case I think you should copy the code from the macro definition, and run the variable initialization and app_uart_init(..) call as separate steps. 

    The code is documented here, or you can find it in the source files. 

    Best regards
    Torbjørn

  • Torbjørn,

    I found a solution that is working. The APP_UART_FIFO_INIT does work for multiple calls (at least with my implementation with no flow control enabled). The macro will unwrap the static buffer allocation inside of uart_init() and future calls shouldn't reallocate buffer space, but rather use the existing buffers allocated. 

    I tracked my issue down to a slightly different problem.

    My problem was that when the ble service attempted to send data via the uart this caused app_uart_put to be called in an interrupt context. Prior to nrf_pwr_mgmt_run() returning and uart_init() being called again.

    As a result the ble_nus_handler was trying to put data into un-initd' uart driver. 

    static void nus_data_handler(ble_nus_evt_t * p_evt)
    {
    
        if (p_evt->type == BLE_NUS_EVT_RX_DATA)
        {
            uint32_t err_code;
    
            NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
            NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
           
            for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++)
            {
                do
                {
    
                    err_code = app_uart_put(p_evt->params.rx_data.p_data[i]); //casuing issue
                    if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY))
                    {
                        NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
                        APP_ERROR_CHECK(err_code);
                    }
                } while (err_code == NRF_ERROR_BUSY);
            }
            if (p_evt->params.rx_data.p_data[p_evt->params.rx_data.length - 1] == '\r')
            {
                while (app_uart_put('\n') == NRF_ERROR_BUSY);
            }
          
        }
    
    }

    The solution that I decided on was to check the init status of the uart prior to trying to use it. app_uart doesn't seem to have an inherent way to check init status and checking init status via the lower level UART or UARTE driver seem clunky depending on if you are using DMA as well as getting the right UART instant due to app_uart abstracting this all away. 

    I will likely wrap app_uart in my own APIs to init and reint so that tracking of init status is done automatically. 

    As a side note, it would be very helpful if app_uart library included an API to check the init status. Maybe something to consider for a future SDK releases. 

    All the best and thanks for the help. 

    Eric

  • Hi Eric

    Good to hear you found the issue Slight smile

    Unfortunately the nRF5 SDK is in maintenance mode at this stage, after we transitioned to the new nRF Connect SDK. 

    We might issue minor updates if bugs are found, but new features is not really a priority. 

    Best regards
    Torbjørn

Related