Minimizing the power draw using System OFF and RAM retention

FormerMember
FormerMember

Hi everyone,

I'm having an issue with the power draw of my application (SDK 15.0.0, SD 132 v6, Sparkfun nRF52832 Breakout). The application does roughly the following: it sleeps until it receives an GPIO interrupt (there are two different GPIOs which can wake up the application), does some (non connectable) advertising and resumes sleeping.

According to the data sheet of the nRF52832 0.7 µA are possible with full RAM retention and in system state System OFF. The power draw of my application exceeds this number (around ten times higher), however I'm not utilising the System OFF mode yet (currently using only the nrf_pwr_mgmt_run() function).

I did some research and discovered the sd_power_off() function. When I add the sd_power_off() function to my code, the system "freezes" / 'hangs" and draws about 10mA.

I did take a look at the example "ble_app_pwr_profiling" but it is rather complex - has anyone created / found a simpler solution (do some advertising, go to System OFF with RAM retention, wake up to an GPIO interrupt, repeat) yet?

How can I differentiate the two different GPIO interrupt sources when using them to wake up? (I want to count the times my application has woken up to sources 1 and 2)


Best,

lhochstetter

Parents
  • Hi,

     

    If you are using the nrf_pwr_mgmt library, you should call the appropriate API for entering system off mode:

    nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
    Are you debugging while trying to enter system off mode? Emulated systemoff (entering system off in debug mode) will not work as intended, as it will return immediately due to the debugger being attached.
    Kind regards,
    Håkon
  • FormerMember
    0 FormerMember in reply to Håkon Alseth

    Hi,

    sorry for the belated reply. I tried your suggestion (the board did go to some sort of power saving mode, drawing ~1mA but was unresponsive afterwards).

    I spent some time studying the ble_app_pwr_profiling example. In the example two functions are used to go to the System OFF mode:

    nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF)

    and

    sd_power_system_off()

    Since sd_power_system_off() is used in the ble_evt_handler() I assume that this function is used when the Softdevice is / was active. nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF) is used otherwise.

    Right now I'm trying to gut the ble_app_pwr_profiling example to get a simple non-connectable beacon which reacts to button presses, advertising the button pressed and the count of button presses, and returning to System OFF afterwards.

    EDIT

    I'm not debugging / There is no debugger attached

    /EDIT

    Best,

    lhochstetter

  • FormerMember
    0 FormerMember in reply to Håkon Alseth

    Hi,

    thanks for the link, I tried to merge your example (the gcc one) with mine, but sadly it didn't work out i.e. the value was not retained. Again I'm trying to only save the second slave of the 7th RAM tile.

    Code snippets:

    uint32_t j __attribute__((section(".no_init"))) __attribute__((used));
    
    int main(void)
    {
    	// Initialize.
    	gpio_init();
    	ble_stack_init();
    
    	sd_power_ram_power_set(7, (POWER_RAM_POWER_S1POWER_On << POWER_RAM_POWER_S1POWER_Pos)
    		                | (POWER_RAM_POWER_S1RETENTION_On << POWER_RAM_POWER_S1RETENTION_Pos));
    
    	if (0 == nrf_gpio_pin_read(BSP_BUTTON_0)) {
    		nrf_drv_gpiote_out_clear(BSP_LED_0);
    	}
    
    	if (3 == j) {
    		nrf_drv_gpiote_out_clear(BSP_LED_2);
    		j = 0;
    	}
    
    	j++;
    	nrf_delay_ms(1000);
    	nrf_drv_gpiote_out_set(BSP_LED_0);
    	nrf_drv_gpiote_out_set(BSP_LED_2);
    	sd_power_system_off();
    
    	for(;;) {
    		idle_state_handle();
    	}
    
    }

    My linker file (based on the original ble_app_beacon) which I tied to merge with the linker file provided in your example.

    /* Linker script to configure memory regions. */
    
    SEARCH_DIR(.)
    GROUP(-lgcc -lc -lnosys)
    
    MEMORY
    {
      FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0x5a000
      RAM (rwx) :  ORIGIN = 0x200018a8, LENGTH = 0xd758
      NO_INIT (rwx) :  ORIGIN = 0x2000F000, LENGTH = 0x1000
    }
    
    SECTIONS
    {
      .no_init (NOLOAD):
      {
        PROVIDE(__start_no_init_data = .);
    	KEEP(*(SORT(.no_init*)))
    	PROVIDE(__stop_no_init_data = .);
      } > NO_INIT
    }
    
    SECTIONS
    {
      . = ALIGN(4);
      .mem_section_dummy_ram :
      {
      }
      .log_dynamic_data :
      {
        PROVIDE(__start_log_dynamic_data = .);
        KEEP(*(SORT(.log_dynamic_data*)))
        PROVIDE(__stop_log_dynamic_data = .);
      } > RAM
      .cli_sorted_cmd_ptrs :
      {
        PROVIDE(__start_cli_sorted_cmd_ptrs = .);
        KEEP(*(.cli_sorted_cmd_ptrs))
        PROVIDE(__stop_cli_sorted_cmd_ptrs = .);
      } > RAM
      .fs_data :
      {
        PROVIDE(__start_fs_data = .);
        KEEP(*(.fs_data))
        PROVIDE(__stop_fs_data = .);
      } > RAM
    
    } INSERT AFTER .data;
    
    SECTIONS
    {
      .mem_section_dummy_rom :
      {
      }
      .sdh_soc_observers :
      {
        PROVIDE(__start_sdh_soc_observers = .);
        KEEP(*(SORT(.sdh_soc_observers*)))
        PROVIDE(__stop_sdh_soc_observers = .);
      } > FLASH
      .sdh_ble_observers :
      {
        PROVIDE(__start_sdh_ble_observers = .);
        KEEP(*(SORT(.sdh_ble_observers*)))
        PROVIDE(__stop_sdh_ble_observers = .);
      } > FLASH
      .pwr_mgmt_data :
      {
        PROVIDE(__start_pwr_mgmt_data = .);
        KEEP(*(SORT(.pwr_mgmt_data*)))
        PROVIDE(__stop_pwr_mgmt_data = .);
      } > FLASH
      .log_const_data :
      {
        PROVIDE(__start_log_const_data = .);
        KEEP(*(SORT(.log_const_data*)))
        PROVIDE(__stop_log_const_data = .);
      } > FLASH
        .nrf_balloc :
      {
        PROVIDE(__start_nrf_balloc = .);
        KEEP(*(.nrf_balloc))
        PROVIDE(__stop_nrf_balloc = .);
      } > FLASH
      .sdh_state_observers :
      {
        PROVIDE(__start_sdh_state_observers = .);
        KEEP(*(SORT(.sdh_state_observers*)))
        PROVIDE(__stop_sdh_state_observers = .);
      } > FLASH
      .sdh_stack_observers :
      {
        PROVIDE(__start_sdh_stack_observers = .);
        KEEP(*(SORT(.sdh_stack_observers*)))
        PROVIDE(__stop_sdh_stack_observers = .);
      } > FLASH
      .sdh_req_observers :
      {
        PROVIDE(__start_sdh_req_observers = .);
        KEEP(*(SORT(.sdh_req_observers*)))
        PROVIDE(__stop_sdh_req_observers = .);
      } > FLASH
        .nrf_queue :
      {
        PROVIDE(__start_nrf_queue = .);
        KEEP(*(.nrf_queue))
        PROVIDE(__stop_nrf_queue = .);
      } > FLASH
        .cli_command :
      {
        PROVIDE(__start_cli_command = .);
        KEEP(*(.cli_command))
        PROVIDE(__stop_cli_command = .);
      } > FLASH
      .crypto_data :
      {
        PROVIDE(__start_crypto_data = .);
        KEEP(*(SORT(.crypto_data*)))
        PROVIDE(__stop_crypto_data = .);
      } > FLASH
    
    } INSERT AFTER .text
    
    INCLUDE "nrf_common.ld"


    Sorry for making you basically hold my hand but I'm fairly ignorant when it comes to the SDK and how things work / are compiled ... is there by chance an introduction / tutorial which explains the SDK structure etc. ? (I'm also at a loss when it comes to tracking down function definitions etc.)

    I tried to compile your example but it kept complaining, that some files where missing - the ../../config directory does not seem to be in the .zip. I'm also guessing that your example was written with an older SDK version as system_nrf52.c seems to have changed places.

    Kind regards,

    lhochstetter

  • Hi,

     

    I integrated the previous example into ble_app_beacon (quick and dirty, my apologies), here's the main file and linker file (SDK v15.2):

    ble_app_beacon_gcc_nrf52.ld

    main.c

    I didn't have any problems running this code, it loops through the LEDs like it should. Could you try this and see if that works?

     

    Kind regards,

    Håkon

  • FormerMember
    0 FormerMember in reply to Håkon Alseth

    Hi Håkon,

    it just works (tm)! Thank you!

    I took a closer look at your .ld and .c files and noticed some differences:

    In the .ld file you "included" the no_init declaration in the section which was to be "inserted after .text" (if I understand the .ld file correctly). I used the "spare" section at the very beginning without the "INSERT AFTER ..." command.

    I therefore suspect that my main issue was the no_init declaration at the wrong section.

    In the .c file / in the main function you "only enabled RAM retention" and did not touch the "POWER_RAM_POWER_S1POWER_On register" at all.

    One more question: could you provide me with a couple terms to research, so I can properly understand the .ld files syntax / notation?

    Thanks again!

    Kind regards,

    lhochstetter

  • Hi lhochstetter,

     

    Glad to hear that it worked.

    You do not need to set the "POWER_RAM_POWER_S1POWER_On" field, as it is already set (reset value is '1').

     

    Regarding the .ld scripts and syntax, this is given by the toolchain: https://sourceware.org/binutils/docs/ld/Scripts.html

    You may find better ways of describing the linker scripts, I'd recommend searching around and checking on stack overflow etc. to see if you can find any resources there.

     

    Kind regards,

    Håkon

  • FormerMember
    0 FormerMember in reply to Håkon Alseth

    Hi Håkon,

    thanks for the link and hints!

    I'll mark your 'quick and dirty' solution as answer as it was quite helpful.

    Kind regards,

    lhochstetter

Reply Children
No Data
Related