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

M33 core in nRF9160 low power mode - Constant latency mode

Hello, I am working on low power modes of MCU M33 core in nRF9160 SOC.

From the document of nRF9160 product specification v1.0, understood that there are two low power modes for M33 core 

1. System ON Mode

     a. Low power mode - Default mode after reset

     b. Constant Latency mode.

2.System OFF Mode - The device is put into System OFF mode using the REGULATORS register interface.

As you suggested in my previous case Case ID: 246126, In serial Link monitor example code, I found enter_sleep() function which is actually System OFF Mode.

I have changed the enter_Sleep() function to enter into system ON mode instead of System OFF mode as in the example.

Below are my code which I am trying to wake up using GPIO button from system ON mode - low power mode but not successful.

When I checked the TASKS_LOWPWR register in embedded segger studio mode, the value in this register is still 0  though I am writing 1 to this register using nrf_power_task_trigger(NRF_POWER_NS,NRF_POWER_TASK_LOWPWR) API

I have also checked operation of the System ON mode - Constant Latency Mode, TASKS_CONSTLAT register is still 0.

void enter_sleep(void)
{

nrf_gpio_cfg_input(CONFIG_SLM_INTERFACE_PIN,
NRF_GPIO_PIN_PULLUP);
nrf_gpio_cfg_sense_set(CONFIG_SLM_INTERFACE_PIN,
NRF_GPIO_PIN_SENSE_LOW);


lte_lc_power_off();
bsd_shutdown();
nrf_power_task_trigger(NRF_POWER_NS,NRF_POWER_TASK_LOWPWR);

}

Can you guide me how to enable these modes- low power and contant latency mode.

Parents
  • Hi Heidi,

    Yesterday I followed your suggestion and turned OFF logging feature and disable UART peripheral. Current consumption i was able to land is

    System ON Mode-Idle Mode : 2.93mA

    System OFF Mode - 5.42uA.

    Still not matching to datasheet values.

    Let me know if you need further details.

  • Hi, could I take a look at your code?

    Based on the values you're getting, it looks like the modem is still running when in System ON Mode. Is this intended? If yes, you will need to implement PSM or eDRX intervals to reduce power consumption. 

    Best regards,

    Heidi

  • Hi Heidi,

    Here I have attached by source file, in function sleepModeEnter(shutdown_modes_t modes) i have implemented Idle mode and SYSTEM OFF mode functions.

    I have disabled the Modem using AT+CFUN=0 in lte_lc_power_off function and also bsd shutdown.


    
    
    
    #include <drivers/gpio.h>
    #include <modem/lte_lc.h>
    #include <bsd.h>
    #include <hal/nrf_power.h>
    #include <hal/nrf_regulators.h>
    #include "Uarts.h"
    #include "TaskDefs.h"
    #include "Common.h"
    
    
    #define BUTTON_PORT  DT_GPIO_KEYS_BUTTON_2_GPIOS_CONTROLLER
    #define BUTTON_PIN   DT_GPIO_KEYS_SW0_GPIOS_PIN
    
    static struct gpio_callback wakeup_cb;
    static struct device *gpio_dev;
    taskAttributes_t taskAttributeTable[TOTAL_NUMBER_OF_TASKS];
    bool isReadyToSleep = false;
    extern uartDevice_t uartDevs[];
    extern const k_tid_t tid_EvcComm; 
    
    K_SEM_DEFINE(task_state, 1, 1); //Statically define and intialize a semaphore
    
    LOG_MODULE_REGISTER(CommonTask, CONFIG_CP_LOG_LEVEL);
    
    void t_CommonTask(void *p1, void *p2, void *p3)
    {
        ARG_UNUSED(p1);
        ARG_UNUSED(p2);
        ARG_UNUSED(p3);
        LOG_INF("Periodic Task started");
        for (;;)
        {
            MonitorTasksState();
    
            if (isReadyToSleep)
            {
                sleepModeEnter(SHUTDOWN_MODE_IDLE);
            }
        }
    }
    
    void sleepModeEnter(shutdown_modes_t modes)
    {
       wakeupSource_init();
       switch(modes)
       {
          case SHUTDOWN_MODE_IDLE:
        
          uartDeinitial(&uartDevs[EVC_UART_IDX],DEVICE_PM_OFF_STATE);
          lte_lc_power_off();
          bsd_shutdown();
    
          /* As suggested in ticket 
             https://devzone.nordicsemi.com/f/nordic-q-a/59812/m33-core-in-nrf9160-low-power-mode---constant-latency-mode
             to use Zephry Idle mode API.
          */
          k_cpu_idle();
    
          break;
    
          case SHUTDOWN_MODE_SLEEP:
    
              lte_lc_power_off();
              bsd_shutdown();
    //          sys_set_power_state(SYS_POWER_STATE_DEEP_SLEEP_1);
              nrf_regulators_system_off(NRF_REGULATORS_NS);
    
              break;
    
          default:
          break;
       }
    }
    
    void SleepModeExit(struct device *gpiob,u32_t pins)
    {
        gpio_pin_disable_callback(gpiob, pins);
        gpio_remove_callback(gpiob, &wakeup_cb);
        gpio_pin_configure(gpiob, pins, GPIO_DIR_IN | GPIO_PUD_NORMAL);
    }
    
    
    void wakeupSource_init(void)
    {
    
        int err;
    
        gpio_dev = device_get_binding(BUTTON_PORT);
        if (gpio_dev == NULL)
        {
            LOG_ERR("Error in GPIO binding: %d", err);
            return;
        }
        err = gpio_pin_configure(gpio_dev, BUTTON_PIN,
                GPIO_DIR_IN | GPIO_INT | GPIO_INT_LEVEL |
                GPIO_INT_ACTIVE_LOW | GPIO_PUD_PULL_UP |
                GPIO_INT_DEBOUNCE);
        if (err)
        {
            LOG_ERR("GPIO_0 config error: %d", err);
            return;
        }
        gpio_init_callback(&wakeup_cb,buttonPresscallback,BIT(BUTTON_PIN));
    
        err = gpio_add_callback(gpio_dev, &wakeup_cb);
        if (err)
        {
            LOG_ERR("GPIO_0 add callback error: %d", err);
            return;
        }
        err = gpio_pin_enable_callback(gpio_dev, BUTTON_PIN);
        if (err)
        {
            LOG_ERR("GPIO_0 enable callback error: %d", err);
        }
    }
    
    void buttonPresscallback(struct device *gpiob, struct gpio_callback *cb,u32_t pins)
    {
    
      SleepModeExit(gpiob,BUTTON_PIN); 
    }
    
    
    void updateTaskState(u8_t TaskNum , taskStates_t state)
    {
        if (taskAttributeTable[TaskNum].taskCurrentState != state)
        {
    
            k_sem_take(&task_state, K_FOREVER);
    
            taskAttributeTable[TaskNum].taskPreviousState = taskAttributeTable[TaskNum].taskCurrentState;
    
            taskAttributeTable[TaskNum].taskCurrentState = state;
    
            k_sem_give(&task_state);
        }
    }
    
    
    
    /*! \fn       void MonitorTasksState(void)
     *  \brief
     *  \details  This function monitors all tasks' state. If all the tasks are in sleep state
     *  \details  Put the system to sleep
     *  \param    void
     *  \return   void
     */
    void MonitorTasksState( void )
    {
        u8_t taskCounter = 0;
        bool allTasksInSleep = true;
    
    
        for(taskCounter = 1; taskCounter < TOTAL_NUMBER_OF_TASKS; taskCounter++)
        {
            if(taskAttributeTable[taskCounter].taskCurrentState != TASK_STATE_ASLEEP)
            {
                allTasksInSleep = false;
                break;
            }
        }
    
        if(allTasksInSleep == true)
        {
           isReadyToSleep = true;
        }
      
    }
    
    K_THREAD_DEFINE(tid_Common, COMMON_TASK_STACKSIZE, t_CommonTask, NULL, NULL, NULL, COMMON_TASK_PRIORITY, 0, K_NO_WAIT);

Reply
  • Hi Heidi,

    Here I have attached by source file, in function sleepModeEnter(shutdown_modes_t modes) i have implemented Idle mode and SYSTEM OFF mode functions.

    I have disabled the Modem using AT+CFUN=0 in lte_lc_power_off function and also bsd shutdown.


    
    
    
    #include <drivers/gpio.h>
    #include <modem/lte_lc.h>
    #include <bsd.h>
    #include <hal/nrf_power.h>
    #include <hal/nrf_regulators.h>
    #include "Uarts.h"
    #include "TaskDefs.h"
    #include "Common.h"
    
    
    #define BUTTON_PORT  DT_GPIO_KEYS_BUTTON_2_GPIOS_CONTROLLER
    #define BUTTON_PIN   DT_GPIO_KEYS_SW0_GPIOS_PIN
    
    static struct gpio_callback wakeup_cb;
    static struct device *gpio_dev;
    taskAttributes_t taskAttributeTable[TOTAL_NUMBER_OF_TASKS];
    bool isReadyToSleep = false;
    extern uartDevice_t uartDevs[];
    extern const k_tid_t tid_EvcComm; 
    
    K_SEM_DEFINE(task_state, 1, 1); //Statically define and intialize a semaphore
    
    LOG_MODULE_REGISTER(CommonTask, CONFIG_CP_LOG_LEVEL);
    
    void t_CommonTask(void *p1, void *p2, void *p3)
    {
        ARG_UNUSED(p1);
        ARG_UNUSED(p2);
        ARG_UNUSED(p3);
        LOG_INF("Periodic Task started");
        for (;;)
        {
            MonitorTasksState();
    
            if (isReadyToSleep)
            {
                sleepModeEnter(SHUTDOWN_MODE_IDLE);
            }
        }
    }
    
    void sleepModeEnter(shutdown_modes_t modes)
    {
       wakeupSource_init();
       switch(modes)
       {
          case SHUTDOWN_MODE_IDLE:
        
          uartDeinitial(&uartDevs[EVC_UART_IDX],DEVICE_PM_OFF_STATE);
          lte_lc_power_off();
          bsd_shutdown();
    
          /* As suggested in ticket 
             https://devzone.nordicsemi.com/f/nordic-q-a/59812/m33-core-in-nrf9160-low-power-mode---constant-latency-mode
             to use Zephry Idle mode API.
          */
          k_cpu_idle();
    
          break;
    
          case SHUTDOWN_MODE_SLEEP:
    
              lte_lc_power_off();
              bsd_shutdown();
    //          sys_set_power_state(SYS_POWER_STATE_DEEP_SLEEP_1);
              nrf_regulators_system_off(NRF_REGULATORS_NS);
    
              break;
    
          default:
          break;
       }
    }
    
    void SleepModeExit(struct device *gpiob,u32_t pins)
    {
        gpio_pin_disable_callback(gpiob, pins);
        gpio_remove_callback(gpiob, &wakeup_cb);
        gpio_pin_configure(gpiob, pins, GPIO_DIR_IN | GPIO_PUD_NORMAL);
    }
    
    
    void wakeupSource_init(void)
    {
    
        int err;
    
        gpio_dev = device_get_binding(BUTTON_PORT);
        if (gpio_dev == NULL)
        {
            LOG_ERR("Error in GPIO binding: %d", err);
            return;
        }
        err = gpio_pin_configure(gpio_dev, BUTTON_PIN,
                GPIO_DIR_IN | GPIO_INT | GPIO_INT_LEVEL |
                GPIO_INT_ACTIVE_LOW | GPIO_PUD_PULL_UP |
                GPIO_INT_DEBOUNCE);
        if (err)
        {
            LOG_ERR("GPIO_0 config error: %d", err);
            return;
        }
        gpio_init_callback(&wakeup_cb,buttonPresscallback,BIT(BUTTON_PIN));
    
        err = gpio_add_callback(gpio_dev, &wakeup_cb);
        if (err)
        {
            LOG_ERR("GPIO_0 add callback error: %d", err);
            return;
        }
        err = gpio_pin_enable_callback(gpio_dev, BUTTON_PIN);
        if (err)
        {
            LOG_ERR("GPIO_0 enable callback error: %d", err);
        }
    }
    
    void buttonPresscallback(struct device *gpiob, struct gpio_callback *cb,u32_t pins)
    {
    
      SleepModeExit(gpiob,BUTTON_PIN); 
    }
    
    
    void updateTaskState(u8_t TaskNum , taskStates_t state)
    {
        if (taskAttributeTable[TaskNum].taskCurrentState != state)
        {
    
            k_sem_take(&task_state, K_FOREVER);
    
            taskAttributeTable[TaskNum].taskPreviousState = taskAttributeTable[TaskNum].taskCurrentState;
    
            taskAttributeTable[TaskNum].taskCurrentState = state;
    
            k_sem_give(&task_state);
        }
    }
    
    
    
    /*! \fn       void MonitorTasksState(void)
     *  \brief
     *  \details  This function monitors all tasks' state. If all the tasks are in sleep state
     *  \details  Put the system to sleep
     *  \param    void
     *  \return   void
     */
    void MonitorTasksState( void )
    {
        u8_t taskCounter = 0;
        bool allTasksInSleep = true;
    
    
        for(taskCounter = 1; taskCounter < TOTAL_NUMBER_OF_TASKS; taskCounter++)
        {
            if(taskAttributeTable[taskCounter].taskCurrentState != TASK_STATE_ASLEEP)
            {
                allTasksInSleep = false;
                break;
            }
        }
    
        if(allTasksInSleep == true)
        {
           isReadyToSleep = true;
        }
      
    }
    
    K_THREAD_DEFINE(tid_Common, COMMON_TASK_STACKSIZE, t_CommonTask, NULL, NULL, NULL, COMMON_TASK_PRIORITY, 0, K_NO_WAIT);

Children
  • Do I also need to set application data profile in low power mode

    %XDATAPRFL=<power_level>

    0 – Ultra-low power

    1 – Low power

    2 – Normal

    3 – Performance

    4 – High performance

  • Hi!

    No, setting %XDATAPRFL won't have a large impact as you can read from the application note:

    As Martin mentioned in the email thread, it looks like your CPU is still running. 

    However, I think the optimal situation for your use-case would be to keep the modem turned on, in power-saving mode, for lower overall power consumption. Turning the modem off means every time you want to transmit or receive, you need to turn the modem back on and re-attach to the network which takes a lot of power compared to having the modem in PSM, which has a floor-current of 4 uA. 

    You can use the lte_lc_psm_req(bool enable) function to go to or disable PSM. 

    Best regards,

    Heidi

  • Hi Heidi,

    Yeah, I just replied to that mail chain.

    Again here I am copying the same :-)

    Still we are not clear on the end application use case( Modem should be ON with PSM or completely OFF), as of now we want to evaluate power consumption of M33 core. May be after getting hands on low power states of M33 core in SoC, we will evaluate Modem ON Idle mode with PSM, Modem ON idle mode without PSM then conclude the best possible approach.

    We are working in gas meter application. We have two modes of operation

    1. TCP client mode we call it as CALL-IN mode.

    2.TCP server mode we call it as CALL-OUT mode/continuous server mode.

    In CALL-IN mode we are not always connected to network, we will turn of the radio. If we get request from gas meter to connect to network and transfer the data to headend system(cloud), we will wakeup M33 core and turn on nRF9160 radio in SoC, register to network,  transfer data to cloud then switch off the radio and move the M33 core to sleep.For this use case we are trying to evulate sleep modes in M33 core.

    As martin suggested approach is good and best suited for our CALL-IN mode. We will consider it but before we want to have a hands on sleep mode in M33 core in SoC.

    Coming to CALL-OUT mode or continuous server mode, nRF9160 radio will always connected to network and with eDRX enabled. If there is no data transaction on the network, nRF9160 will move to eDRX  mode and M33 core should also move to sleep mode. If a remote client connection request comes to radio nRF9160 in paging time window, radio internally should wake up M33 core for processing data received.

  • Hi!

    Okay, I understand. I received help from a colleague to fix a few things in the code you sent over: 

    #include <drivers/gpio.h>
    #include <modem/lte_lc.h>
    #include <bsd.h>
    #include <hal/nrf_power.h>
    #include <hal/nrf_regulators.h>
    #include "Uarts.h"
    #include "TaskDefs.h"
    #include "Common.h"
     
    #define BUTTON_PORT  DT_GPIO_KEYS_BUTTON_2_GPIOS_CONTROLLER
    #define BUTTON_PIN   DT_GPIO_KEYS_SW0_GPIOS_PIN
     
    static struct gpio_callback wakeup_cb;
    static struct device *gpio_dev;
    taskAttributes_t taskAttributeTable[TOTAL_NUMBER_OF_TASKS];
    bool isReadyToSleep = false;
    extern uartDevice_t uartDevs[];
    extern const k_tid_t tid_EvcComm; 
     
    K_SEM_DEFINE(task_state, 1, 1); //Statically define and intialize a semaphore
     
    LOG_MODULE_REGISTER(CommonTask, CONFIG_CP_LOG_LEVEL);
     
    void t_CommonTask(void *p1, void *p2, void *p3)
    {
        ARG_UNUSED(p1);
        ARG_UNUSED(p2);
        ARG_UNUSED(p3);
        LOG_INF("Periodic Task started");
        for (;;)
        {
            MonitorTasksState();
     
            if (isReadyToSleep)
            {
                sleepModeEnter(SHUTDOWN_MODE_IDLE);
            }
            k_sleep(K_MSEC(2000));
        }
    }
     
    void sleepModeEnter(shutdown_modes_t modes)
    {
       
       switch(modes)
       {
          case SHUTDOWN_MODE_IDLE:
        
          uartDeinitial(&uartDevs[EVC_UART_IDX],DEVICE_PM_OFF_STATE);
          lte_lc_power_off();
          bsd_shutdown();
          isReadyToSleep = false;
     
          break;
     
          case SHUTDOWN_MODE_SLEEP:
              wakeupSource_init();
     
              lte_lc_power_off();
              bsd_shutdown();
    //          sys_set_power_state(SYS_POWER_STATE_DEEP_SLEEP_1);
              nrf_regulators_system_off(NRF_REGULATORS_NS);
     
              break;
     
          default:
          break;
       }
    }
     
    void SleepModeExit(struct device *gpiob,u32_t pins)
    {
        gpio_pin_disable_callback(gpiob, pins);
        gpio_remove_callback(gpiob, &wakeup_cb);
        gpio_pin_configure(gpiob, pins, GPIO_DIR_IN | GPIO_PUD_NORMAL);
    }
     
    void wakeupSource_init(void)
    {
     
        int err;
     
        gpio_dev = device_get_binding(BUTTON_PORT);
        if (gpio_dev == NULL)
        {
            LOG_ERR("Error in GPIO binding: %d", err);
            return;
        }
        err = gpio_pin_configure(gpio_dev, BUTTON_PIN,
                GPIO_DIR_IN | GPIO_INT | GPIO_INT_LEVEL |
                GPIO_INT_ACTIVE_LOW | GPIO_PUD_PULL_UP |
                GPIO_INT_DEBOUNCE);
        if (err)
        {
            LOG_ERR("GPIO_0 config error: %d", err);
            return;
        }
        gpio_init_callback(&wakeup_cb,buttonPresscallback,BIT(BUTTON_PIN));
     
        err = gpio_add_callback(gpio_dev, &wakeup_cb);
        if (err)
        {
            LOG_ERR("GPIO_0 add callback error: %d", err);
            return;
        }
        err = gpio_pin_enable_callback(gpio_dev, BUTTON_PIN);
        if (err)
        {
            LOG_ERR("GPIO_0 enable callback error: %d", err);
        }
    }
     
    void buttonPresscallback(struct device *gpiob, struct gpio_callback *cb,u32_t pins)
    {
     
      SleepModeExit(gpiob,BUTTON_PIN); 
    }
     
    void updateTaskState(u8_t TaskNum , taskStates_t state)
    {
        if (taskAttributeTable[TaskNum].taskCurrentState != state)
        {
     
            k_sem_take(&task_state, K_FOREVER);
     
            taskAttributeTable[TaskNum].taskPreviousState = taskAttributeTable[TaskNum].taskCurrentState;
     
            taskAttributeTable[TaskNum].taskCurrentState = state;
     
            k_sem_give(&task_state);
        }
    }
    
    
     
    /*! \fn       void MonitorTasksState(void)
     *  \brief
     *  \details  This function monitors all tasks' state. If all the tasks are in sleep state
     *  \details  Put the system to sleep
     *  \param    void
     *  \return   void
     */
    void MonitorTasksState( void )
    {
        u8_t taskCounter = 0;
        bool allTasksInSleep = true;
     
        for(taskCounter = 1; taskCounter < TOTAL_NUMBER_OF_TASKS; taskCounter++)
        {
            if(taskAttributeTable[taskCounter].taskCurrentState != TASK_STATE_ASLEEP)
            {
                allTasksInSleep = false;
                break;
            }
        }
     
        if(allTasksInSleep == true)
        {
           isReadyToSleep = true;
        }
      
    }
     
    K_THREAD_DEFINE(tid_Common, COMMON_TASK_STACKSIZE, t_CommonTask, NULL, NULL, NULL, COMMON_TASK_PRIORITY, 0, K_NO_WAIT);

    Could you try this instead and see if it helps with the power consumption?

    I missed a note at the bottom of the CPU Idling documentationDo not use these APIs unless absolutely necessary. In a normal system, the idle thread takes care of power management, including CPU idling.

    According to the Zephyr Threads documentation if all threads are unready, the Zephyr idle thread will set the CPU in System ON Idle. So it will either happen automatically, or you can set k_sleep() for a certain amount of time. More information on the idle thread can be found here.

    Which is why this function has been removed from the code. 

    Best regards,

    Heidi

  • Hi Heidi,

    I have tried the code given COmmon_fixed.c and 1641.prj.txt and measured current.

    It is around 2.92mA

Related