Suspending the UART using PM_DEVICE ACTION_SUSPEND does not reduce power consumption that much

Hello

As the title says, I am suspending the UART using PM_DEVICE ACTION_SUSPEND, but the power consumption is not decreasing significantly.

With CONFIG_SERIAL=n and no UART used, power consumption is now around 20uA.

When using pm_device_action_run, the power consumption was about 1.3mA.

Please tell me the correct way to stop UART.

SDK : nrf Connect SDK 2.6.1

Board : Raytac MDBT42Q-DB

#include <zephyr/kernel.h>
#include <zephyr/pm/device.h>
#include <zephyr/logging/log.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/uart.h>

LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);

static uint8_t Buffer[16];

static const struct device *Uart = DEVICE_DT_GET(DT_NODELABEL(uart0));

static void Callback(const struct device *dev, struct uart_event *evt, void *user_data);

int main(void)
{
#if defined(CONFIG_SERIAL)
    if (!device_is_ready(Uart))
    {
        LOG_ERR("device is not ready");
        return 0;
    }

    const struct uart_config cfg = {
        .baudrate = 1000000,
        .parity = UART_CFG_PARITY_NONE,
        .stop_bits = UART_CFG_STOP_BITS_1,
        .data_bits = UART_CFG_DATA_BITS_8,
        .flow_ctrl = UART_CFG_FLOW_CTRL_RTS_CTS,
    };

    int err = uart_configure(Uart, &cfg);
    if (err)
    {
        LOG_ERR("uart_configure (%d)", err);
        return 0;
    }

    err = uart_callback_set(Uart, Callback, NULL);
    if (err)
    {
        LOG_ERR("uart_callback_set (%d)", err);
        return 0;
    }

    err = uart_rx_enable(Uart, Buffer, sizeof(Buffer), 1000);
    if (err)
    {
        LOG_ERR("uart_rx_enable (%d)", err);
        return err;
    }

    int count = 0;
    while (count < 10)
    {
        k_msleep(1000);
        LOG_INF(".");
        count++;
    }
    err = pm_device_action_run(Uart, PM_DEVICE_ACTION_SUSPEND);
    if (err)
    {
        LOG_ERR("pm_device_action_run (%d)", err);
        return 0;
    }
    else
    {
        LOG_INF("SUSPEND");
    }
#endif
    while(true)
    {
        k_msleep(1000);
        LOG_INF(".");
    }

    return 0;
}

static void Callback(const struct device *dev, struct uart_event *evt, void *user_data)
{
    switch (evt->type)
    {
    case UART_TX_DONE:

        break;
    case UART_TX_ABORTED:

        break;
    case UART_RX_RDY:

        break;
    case UART_RX_BUF_REQUEST:

        break;
    case UART_RX_BUF_RELEASED:

        break;
    case UART_RX_DISABLED:

        break;
    case UART_RX_STOPPED:

        break;
    default:
        break;
    }
}

  • Hello,

    Can you please upload the application that you are using to test this?

    After you suspend the UART, are the gpios still configured as outputs? And is the HFCLK still running?

    Best regards,

    Edvin

  • Hello Edvin

    This is my test project.

    power_test.zip

    After Uart suspend, all PIN_CNF of GPIO are disconnect (0x00000002).

    HFCLK is still running (HFCLKSTAT is 0x00010000)

    Thank you.

  • Using the uart_rx_disable() function or receiving data to disable the UART RX has significantly reduced power consumption.

    Using the uart_rx_disable() function alone does not yet disable GPIO, so PM_DEVICE_ACTION_SUSPEND was also used to reduce power consumption to the ideal value.

    The power consumption results for each pattern are shown below.

    pattern 1 : use PM_DEVICE_ACTION_SUSPEND

    pattern 2 : use uart_rx_disable()

    pattern 3 : use uart_rx_disable() & PM_DEVICE_ACTION_SUSPEND

    The source code is shown below.

    int main(void)
    {
        if (!device_is_ready(Uart))
        {
            LOG_ERR("device is not ready");
            return 0;
        }
    
        const struct uart_config cfg = {
            .baudrate = 1000000,
            .parity = UART_CFG_PARITY_NONE,
            .stop_bits = UART_CFG_STOP_BITS_1,
            .data_bits = UART_CFG_DATA_BITS_8,
            .flow_ctrl = UART_CFG_FLOW_CTRL_RTS_CTS,
        };
    
        int err = uart_configure(Uart, &cfg);
        if (err)
        {
            LOG_ERR("uart_configure (%d)", err);
            return 0;
        }
    
        err = uart_callback_set(Uart, Callback, NULL);
        if (err)
        {
            LOG_ERR("uart_callback_set (%d)", err);
            return 0;
        }
    
        err = uart_rx_enable(Uart, Buffer, sizeof(Buffer), 1000);
        LOG_INF("uart_rx_enable (%d)", err);
    
        k_msleep(1000);
    
        err = pm_device_action_run(Uart, PM_DEVICE_ACTION_SUSPEND);
    
        err = uart_rx_disable(Uart);
    
        k_msleep(1000);
    
        err = pm_device_action_run(Uart, PM_DEVICE_ACTION_RESUME);
    
        err = uart_rx_disable(Uart);
        LOG_INF("uart_rx_disable (%d)", err);
    
        k_msleep(10);
    
        err = uart_rx_enable(Uart, Buffer, sizeof(Buffer), 1000);
        LOG_INF("uart_rx_enable (%d)", err);
    
        return 0;
    }

    The above code works, is this method correct?

  • Hello,

    Thank you. I ran your application, and noticed that you are setting up the UART in the application, and not in the devicetree. Also, you have CONFIG_UART_USE_RUNTIME_CONFIGURE=y in your prj.conf. 

    This is of course fine, but that also means that the UART is not properly disabled when you run the command:

    err = pm_device_action_run(Uart, PM_DEVICE_ACTION_SUSPEND);

    I also noticed that the logging was output on your UART as well as on RTT, although it looks like you tried to disable it. So I did some changes to your prj.conf as well as disabling the UART runtime using uart_rx_disable(). 

    After doing those things, it looks like it behaves as you'd expect.

    Give it a go, and let me know if it doesn't work.

    power_test_patch.zip

    Best regards,

    Edvin

  • Hello.

    Thank you. Your application worked fine.

    This is of course fine, but that also means that the UART is not properly disabled when you run the command:

    Is this mean, If set CONFIG_UART_USE_RUNTIME_CONFIGURE=n, power consumption drops without using uart_rx_disable() ?

Related