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

Retain RTC during soft reset

I'm working on a battery powered system which needs to keep track of an absolute time stamp. So I need the RTC module and some kind of energy saving mode. As the RTC is not running in System Off I use the System On sleep mode when Idle.

To decrease the current consumption in sleep mode, I also disable RAM beforehand. I noticed that I need to keep RAM Block3 enabled to be able to wake up again (I guess because it contains the Stack, right?) However, the content of RAM Blocks 0,1,2 is not useable after wake up, so I perform a soft reset. Problem is that this resets the RTC COUNTER register. Is there a possibility to retain this register?

I was also thinking about making the compiler use RAM Block 3 for a part of my code, which then will still be usable after wake up and where I would save the counter value before resetting. I think I need to change the linker script but I couldn't find how exactly. Any ideas? (I'm using GCC 5.3.0)

I'm also using soft device S110, which I disable before entering sleep mode. Here's the code snippet where sleep mode is initiated:

sd_softdevice_disable();

// Disable RAM-Blocks 0,1,2 (Block 3 must stay enabled as it contains the Stack)
NRF_POWER->RAMON	= POWER_RAMON_OFFRAM0_RAM0Off << POWER_RAMON_OFFRAM0_Pos
                    | POWER_RAMON_OFFRAM1_RAM1Off << POWER_RAMON_OFFRAM1_Pos
                    | POWER_RAMON_ONRAM0_RAM0Off << POWER_RAMON_ONRAM0_Pos
                    | POWER_RAMON_ONRAM1_RAM1Off << POWER_RAMON_ONRAM1_Pos;

NRF_POWER->RAMONB	= POWER_RAMONB_OFFRAM2_RAM2Off << POWER_RAMONB_OFFRAM2_Pos
                    | POWER_RAMONB_OFFRAM3_RAM3Off << POWER_RAMONB_OFFRAM3_Pos
                    | POWER_RAMONB_ONRAM2_RAM2Off << POWER_RAMONB_ONRAM2_Pos
                    | POWER_RAMONB_ONRAM3_RAM3On << POWER_RAMONB_ONRAM3_Pos;


__set_PRIMASK(1);	// prevent execution of isr when waking up
__WFI();			// enter sleep mode

/* RAMON and RAMONB Registers are retained. Therefore RAM needs to be enabled
   prior to soft reset in order to make it accessible for the next startup. */
NRF_POWER->RAMON	= POWER_RAMON_OFFRAM0_RAM0On << POWER_RAMON_OFFRAM0_Pos
                    | POWER_RAMON_OFFRAM1_RAM1On << POWER_RAMON_OFFRAM1_Pos
                    | POWER_RAMON_ONRAM0_RAM0On << POWER_RAMON_ONRAM0_Pos
                    | POWER_RAMON_ONRAM1_RAM1On << POWER_RAMON_ONRAM1_Pos;

NRF_POWER->RAMONB	= POWER_RAMONB_OFFRAM2_RAM2On << POWER_RAMONB_OFFRAM2_Pos
                    | POWER_RAMONB_OFFRAM3_RAM3On << POWER_RAMONB_OFFRAM3_Pos
                    | POWER_RAMONB_ONRAM2_RAM2On << POWER_RAMONB_ONRAM2_Pos
                    | POWER_RAMONB_ONRAM3_RAM3On << POWER_RAMONB_ONRAM3_Pos;

NVIC_SystemReset(); // Perform soft reset because content of RAM was lost during sleep mode
  • You're right, I was a bit too fast with my answer :-) My counter never reached the 8-Bit limit, so I didn't notice.

    Concerning restoring the COUNTER value: I was assuming that GPREGRET was 32-Bit, so my idea was to add the current RTC COUNTER value to GPREGRET before resetting. RTC would then start from zero, but to get the time I would just take the current COUNTER value plus the summed up GPREGRET value.

    Now that I realized that this approach doesn't work, I want to use a variable in the powered-on RAM. But how to make sure a variable is placed in a certain location in RAM?

  • uint32_t* backupCounter = (uint32_t*)0x20003000UL; //or whatever ram address you want to use *backupCounter = NRF_RTC0->COUNTER;

  • Good point on the GPREGRET size. I missed that as well. Another way of allocating a variable to a specific location is below. Blatantly stolen from Nordics bootloader example:

        #if defined ( __CC_ARM )
    static dfu_ble_peer_data_t m_peer_data __attribute__((section("NoInit"), zero_init));            /**< This variable should be placed in a non initialized RAM section in order to be valid upon soft reset from application into bootloader. */
    static uint16_t            m_peer_data_crc __attribute__((section("NoInit"), zero_init));        /**< CRC variable to ensure the integrity of the peer data provided. */
    #elif defined ( __GNUC__ )
    __attribute__((section(".noinit"))) static dfu_ble_peer_data_t m_peer_data;                      /**< This variable should be placed in a non initialized RAM section in order to be valid upon soft reset from application into bootloader. */
    __attribute__((section(".noinit"))) static uint16_t            m_peer_data_crc;                  /**< CRC variable to ensure the integrity of the peer data provided. */
    #elif defined ( __ICCARM__ )
    __no_init static dfu_ble_peer_data_t m_peer_data     @ 0x20003F80;                               /**< This variable should be placed in a non initialized RAM section in order to be valid upon soft reset from application into bootloader. */
    __no_init static uint16_t            m_peer_data_crc @ 0x20003F80 + sizeof(dfu_ble_peer_data_t); /**< CRC variable to ensure the integrity of the peer data provided. */
    #endif
    
  • Note that the "noinit" sections for the non IAR examples require modification to the linker control files.

  • Do you need all the RAM while cpu is active? One alternative is to leave blocks 0,1,2 disabled (power off) and modify your linker script to say there is only 8k of RAM (block3). Then the stack would be at the top of block 3 and your variables at the bottom. All would be valid after coming out of sleep and you wouldn't need to simulate a reset. I could be wrong?

Related