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

nrf52832 sleep mode else if (DMP_acq == 0) { app_timer_start(m_timer, APP_TIMER_TICKS(1000), NULL); // timer one shoot - events will wake up the sensor // Put the CPU to sleep until an interrupt is detected.

Hi,

I have develop an application using the nrf52832 combined wit the ICM20948 sensor.  

The application use ESB communication between peripheral and central.  I have solder 10 ohm resistor between the nrf52 board  and the nominal 3V , and another one between the ICM20948 board and  the 3 V supply.

When the ICM is not in used,  I want to put the nrf52 in sleep mode for one second.  The nrf52 then wake up and send the battery level to the central to keep communication going and also pickup new instruction from the returning acknowledge.  This is the portion of the code use for the standby :

    else if (DMP_acq == 0)    
           {  app_timer_start(m_timer, APP_TIMER_TICKS(1000), NULL);  //  timer one shoot  - events will wake up the sensor 
          
            // Put the CPU to sleep until an interrupt is detected.   (data or timer)                    
            // Make sure any pending events are cleared
            __SEV();
            __WFE();
            // Enter System ON sleep mode
            __WFE();
               
       
            } 

When in standby, I measure about 47 mV across the nrf52 10 ohm resistor. That would give about 4.7 mA. The ICM chip is almost at 0 mA.     Number goes up approx to 7.5mA for the nrf52 board and 5 mA for the ICM board (12.5 mA) when sampling values at 56 Hz.  

I suspect that I am doing something wrong. I would expect low uA value when the nrf52 is in standby, no sampling going on, using the above code .

I have seen a post with the following calculation:

System ON, no RAM retention + RTC + LFRC = 1.2uA + 0.1uA + 0.6uA = 1.9uA

I have RTC and LFRC, working for timestamp and +,  nrf_clock for ESB


 What am I doing wrong ?

(Note : Not sure what is no ram retention, should I clear the memory before going to sleep ?)

Thanks you in advance for your help.  It's truly appreciated.

Parents
  • Sorry for the title.   I have search how to edit the title and the text but did not find how...

  • Hello,

     

    SL06 said:
    Sorry for the title.   I have search how to edit the title and the text but did not find how...

     No worries. I don't think it is possible.

    Where do you set DMP_acq = 1?

    First of all, What do you use to measure the voltage over the serial resistor? If you use a multimeter that may be quite inaccurate, because the current consumption is not constant. BLE will typically cause very short current spikes that are quite tall. Multimeters hande these spikes very differently. 

    As you can see in the attached screenshot from the Power Profiler Kit, using nRF Connect for Desktop -> Power Profiler:

    You can see that the spikes are at ~10mA, but the average current consumption for the entire window is 1.073mA. 

    I suspect that you are trying to implement your own sleep logic using timers and stuff. I don't know how you did that implementation, but be aware of that both starting and stopping the app_timer will generate events shortly after this call,

    BR,

    Edvin

  • Sorry, I realized that there an error in the code above .  

    It should be 

    while (timer_event == false);  // ie wait for the app_timer event

    However, that did not work, I did not get the event. I had used that timer_event routine before successfully.

    I have tried to simple put a 50 ms second delay after the app_timer start codeto pass over the timer_event but I dont see any improvement.








  • This is the code I pick up from the low power esb exemple :

    while (true)
                   {
                     // Use directly __WFE and __SEV macros since the SoftDevice is not available.
    
                     // Wait for event.
                      __WFE();
    
                      // Clear Event Register.
                      __SEV();
                      __WFE();
                   }


    If implement in my program, the voltage across the 10 ohm resistor is still 50 mV (5 mA) but the app_timer event does not work anymore.

    IF I disable < while(true) >, then app_timer work, voltage is about the same.



  • I think part of the solution is in this quote from an other ticket:

    "

    Also remember to disable TWI peripheral before sleep. And mind the PAN89: "TWI: Static 400 µA current while using GPIOTE"

    "

    I am using twi communication with the ICM-20948.

    I have play with the ESB _low power exemple and fine that to get low power consumption in the sleep mode, I had to disable gpio.

    Note that my external board does not have any LED.

    I will work on that tomorrow.

  • OK , I have done more testing.

    I have strip done my program o the minimum until I get low power value and add back some component.

    First of all, ==>   I could not get low power  with  the JLinkRTTviewer connected.   

    This is the minimize code used for testing, I have not put the void routine but they are quite standard.

    int main(void)
    {
       
        ret_code_t err_code;
        uint16_t battery_value;
        
        // Initialize.
       APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
       NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        
        APP_ERROR_CHECK(esb_init());
        clocks_start();    /// for ESB
    
    
       NRF_LOG_RAW_INFO("\n**********  ICM20948 start  **********\n\r");
    
        lfclk_init();  //for timestamp + app_timer
    
       //  rtc_config(); // sl   ---- problem
       // gpiote_init();   // init interrupt  // problem
    
        timers_init();    //  app_timer  problem
    
      
      
       Adc12bitPolledInitialise();
       twi_init();
       
    
      
      while(1)
        {   // main loop to fill later
             if (battery_timer)
               {  battery_value = GetBatteryVoltage();
                  sprintf(ESBstring, "Bat: %d",battery_value); 
                  NRF_LOG_DEBUG("%s ",ESBstring)
                  ESBsend();
                  battery_timer = false;
                 // next_battery_read_ms = mytimestamp + BATTERY_READ_MS;
               }
    
    
    
    
               app_timer_start(m_timer, APP_TIMER_TICKS(1000), NULL);  //  timer one shoot  - events will wake up the sensor 
              // nrf_delay_ms(10);
    
           //    while (true)
            {
              //  if (NRF_LOG_PROCESS() == false)
                {// Put the CPU to sleep until an interrupt is detected.   (data or timer)                    
                // Make sure any pending events are cleared
                __SEV();
                __WFE();
                // Enter System ON sleep mode
                __WFE();
            
                 }  }
        
    
            
        } // while(1)
    } // main
    

    With the code above, I measure about 2.1 mV across the 10 ohm resistor, so about 210  µA.  Value was the same without twi_init.  Unfortunately, this include the current going to the ICM-20948.  I will have to separate both the circuit alimentation for individual measurement.

    RTC_config (tick routine generation) and gpiote interfere with sleep mode.  I will have to disable those before entering sleep.

    Now I have this question:

    The routine below for fine  as part of the main loop but  if I enable the line < while (true) > like in SDK16  exemple then the timer _event  won't wakeup  the board.  I don't understand why.

     

        app_timer_start(m_timer, APP_TIMER_TICKS(1000), NULL);  //  timer one shoot  - events will wake up the sensor 
    // while (true)
    {// Put the CPU to sleep until an interrupt is detected. (data or timer) // Make sure any pending events are cleared __SEV(); __WFE(); // Enter System ON sleep mode __WFE(); }

  • I don't understand your main() function. You have two nested while loops? And you realize that you will start the m_timer app_timer many (!) times? You have other events that will wake you up, other than the timeout itself. Perhaps you can try to set the timer to a repeated timer, and start it once, instead of what it looks like you are trying to do here.

    I think what you imagine will happen here is:

    start timer -> go to sleep -> timer times out -> do something -> start timer again.

    what will actually happen: 

    start timer -> go to sleep -> start timer event fires (not timeout) -> start timer again -> go to sleep -> start timer event fires -> start timer again.

    So if you think you are sleeping for 1 second here, that is not the case.

    Try this:

    Start repeated timer-> sleep. Then use timer events to set flags that you can handle in main, like it looks like you are doing, but the timeout event is not the only thing that will wake up the CPU.

Reply
  • I don't understand your main() function. You have two nested while loops? And you realize that you will start the m_timer app_timer many (!) times? You have other events that will wake you up, other than the timeout itself. Perhaps you can try to set the timer to a repeated timer, and start it once, instead of what it looks like you are trying to do here.

    I think what you imagine will happen here is:

    start timer -> go to sleep -> timer times out -> do something -> start timer again.

    what will actually happen: 

    start timer -> go to sleep -> start timer event fires (not timeout) -> start timer again -> go to sleep -> start timer event fires -> start timer again.

    So if you think you are sleeping for 1 second here, that is not the case.

    Try this:

    Start repeated timer-> sleep. Then use timer events to set flags that you can handle in main, like it looks like you are doing, but the timeout event is not the only thing that will wake up the CPU.

Children
  • Thnaks Edwin,

    I will work on that.

    What I am trying to do is

    main loop:

    while (1):

    do something,

    if no data acquisition  - >  start one shot 1sec timer -> wait for timer start event -> go to sleep   -> timer times out -> go back to do something 

    while(true)

    if (timer_event ) {send_battery_level}

    if (DMP_acq >= 0)

       {  .....           }

    else if (DMP_acq == 0)
    { app_timer_start(m_timer, APP_TIMER_TICKS(1000), NULL); // timer one shoot - events will wake up the sensor

    (wait to timer start event :)


    // Put the CPU to sleep until an interrupt is detected. (data or timer)
    // Make sure any pending events are cleared
    __SEV();
    __WFE();
    // Enter System ON sleep mode
    __WFE();


    }

    }

    I have tried to catch the timer start_event before going to sleep but 

       while (timer_event == false); // did not work  , it keep looping

       tried  nrf_delay_ms(X); // was not needed

    Finally, sleep mode was working for 1 sec without any of those line.  I can see a very small burst, maybe 1 ms burst of power (not measured) when the processor wake up to send battery level, and then it goes back to sleep.

     

    Thanks for your help.  I am almost there.  I will work on disabling RTC and GPIOTE before sleep ans see if it work.

    I will report later.

  • Done more testing today :

    basically this is the section of code that I use inside the main section to goto sleep :

    if (DMP_acq == 0)    
               {  
                   nrfx_gpiote_in_event_disable(MPU_INT_PIN);
                 //  twi_disable();
                   app_timer_start(m_timer, APP_TIMER_TICKS(1000), NULL);  //  timer one shoot  - events will wake up the sensor            
                 //  NRF_LOG_INFO("sleep start \n");    
                   {   
                     // Use directly __WFE and __SEV macros since the SoftDevice is not available.
    
                     // Wait for event.
                      __WFE();
    
                      // Clear Event Register.
                      __SEV();
                      __WFE();
                   }
    
               //NRF_LOG_INFO("sleep stop ");        
                NRF_LOG_FLUSH();
             }

    No event was observed after app_timer_start. Sleep mode was engage for 1 sec.


    With that code, gpiote off, Got 2.0 mV across the 10 ohm resistor => so 200 µA. If I disable twi be calling nrfx_twi_disable(&m_twi_instance);
    I don't gain anything more.

    I remove reference to RTC since it was not needed.

    The ICM-20946 board have not separate alimentation, so the curent measuremen for the nrf52 only. It use 7.5 mA when sampling data at 40 Hz.

    200 µA is acceptable and much better than before but can I got further ?

    Anything else I can try?


  • Can you please try to toggle a pin before __WFE to make sure it is only called once every second? Or you can print some message. I am still worried about the sleep logic in your application.

    Have you tested your application without SPI? Even if the application doesn't do much, it is worth testing and comparing without the peripherals, to see if the current consumption is as expected when you don't use the peripherals. If you want to save as much power as possible, you should uninit them when you are not using them. So if you are the SPI master, you may uninitialize the SPI, and disconnect the GPIOs. Some drivers have an uninit function, but it is not always they reset the pins properly. To reset a pin you can use nrf_drv_gpiote_out_uninit(), which will put the pin in disconnected - input mode, which doesn't draw any current.

    BR,

    Edvin

  • HI Edvin,

    I went back to this power consumption problem.  

    I have disable almost everything and found out that the HFCLK used for ESB is the main culprit.

    nrf_drv_clock_hfclk_release();  // don't do anything 

    but NRF_CLOCK->TASKS_HFCLKSTOP = 1; // drop the power to few uA

    the problem now is to restart the clock.  

    Calling the init funtion dont work 

    void clocks_start( void )
    {
        NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
        NRF_CLOCK->TASKS_HFCLKSTART = 1;
        while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
    }

    What is the proper procedure  to stop and restart the HFCLK ?

  • Edvin said:
    Can you please try to toggle a pin before __WFE to make sure it is only called once every second? Or you can print some message. I am still worried about the sleep logic in your application.

    Are you still doing this?

        else if (DMP_acq == 0)    
               {  app_timer_start(m_timer, APP_TIMER_TICKS(1000), NULL);  //  timer one shoot  - events will wake up the sensor 
              
                // Put the CPU to sleep until an interrupt is detected.   (data or timer)                    
                // Make sure any pending events are cleared
                __SEV();
                __WFE();
                // Enter System ON sleep mode
                __WFE();
                   
           
                } 

    That will not work. 

    Your main() functions while loop should look like this:

            while (true)
            {
                custom_functions(); // If you need any
                __SEV();
                __WFE();
                // Enter System ON sleep mode
                __WFE();
            }

Related