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
  • Ok, got it. I call the MBR Reset Handler with

    typedef void resetHandler_t(void);
    resetHandler_t* mbrResetHandler = (resetHandler_t*)*(uint32_t*)0x04;
    mbrResetHandler();
    

    It seems to work as it runs thru the application Reset Handler after a while. But again, the first call of sd_nvic_critical_region_enter(0) fails, this time with a hard fault error.

    I'm still confused with the stack pointer. Is it properly initialized without an actual reset?

  • Why not stash the RTC in the POWER->GPREGRET register which is designed to survive reset prior to resetting? As long as you don't need to be hyper accurate the time to reset and return to running state shouldn't affect your absolute time that much.

  • stack pointer initialised, yes and no. A 'real' reset takes the stack pointer from 0x00000000 and sets it before it does anything else so in that way, no it's not reset by what you're doing. However just about every startup assembler file I've seen ever also quite early on sets the SP to something valid explicitly. If you're getting all the way into main() then it's very unlikely the SP hasn't been set 2 or 3 times and is valid. So you probably have a different error.

    Not sure that faking reset is the best way to go, it's hard to cover all the bases and get the chip in the same state it was in before. As John DeWitt suggests below, how about using a retained register and properly resetting. Also, if you just write it to a location in powered-on RAM, does that work, RAM isn't cleared by reset AFAIK.

  • Thanks for your help guys. I went for the approach with GPREGRET

  • but how? the GPREGRET is a 1 byte register and you can't backup the COUNTER register with it. and you can't restore the COUNTER value. I don't get it.

Related