How to use the internal RTC on nRF52833 SoC to keep system time across reboots

Hello,

I need your help on how to use the internal RTC on nRF52833 SoC to keep the system time under zypher.

I need the RTC to keep its time after,

1- Reboots

2- Sleep using k_sleep( Duration ) 

3- System Off mode, although this use case may not be necessary but still interested if it is possible.

Finally, looking at the nRF52833 RTC registers I could not find a way of setting the time. Is there one?

Kind regards

Mohamed

Parents
  • Thank you.

    I would appreciate it a great deal if you could point me to an example showing how to the persistent registers.

    Also, what does the first line in red mean?

    This information applies to the following SoftDevices: S130, S132, S332

    Note, I am using,

    - nRF52833 SoC

    - SEGGER Embedded Studio for ARM
      Release 5.60  Build 2021081102.47262
      Nordic Edition
      Windows x64

    - Zephyr OS build v2.6.99-ncs1

    - NCS v1.7.0

    Kind regards

    Mohamed

  • Learner said:
    Please answer my question on persistent registers above. Also, If you do a search for '?

    This is what I found when I found searched for "?"

    Finally, looking at the nRF52833 RTC registers I could not find a way of setting the time. Is there one?

    None of the RTC's peripheral on embedded devices I know tells you time but tell you number of ticks. The frequency of tick increment depends on your configuration on PRESCALER: For actual time you either need to rely on the higher level libraries or utilities from the RTOS.

    Learner said:
    So, UICR is out of the running.

    If you want to update it more than once, then yes, UICR is not a solution for you.

    Learner said:
    I will have a look at the 'Settings' route but before I do I would like to understand why I can't use the RTC on its own to keep system time?

    Using RTC on its own to keep system time is not an issue here. That is very simple and can be used directly by accessing RTC register and converting the ticks read into any time unit you want. But I assumed that your main issue is to keep track of time between reboots. That is the trickiest part and that is where you need to access persistent storage. 

    I think we are mixing up two of your requriements.

    1. Keeping track of time can be done very easily using Zephyr utitities or other kernel timing API. You do not need to access RTC registers directly for this.
    2. Retaining time between reboots. This needs special handling from your application to save the previous time before reboot persistently and accessing again after new reboot to restore older value. You cannot restore old value into RTC register but you need to have your own application memory (static variable) or similar to remember the old offsets of time before reboot.

     

  • did you try code what I have wrote?

    You can use RTC and save this time into NRF_UICR_Type anf after reset you will get actual time

  • Did you try code what I have wrote? I dont have behavior you wrote. After reset the value = +1

  • Hi Susheel,

    Thank you.

    Finally, looking at the nRF52833 RTC registers I could not find a way of setting the time. Is there one?

    None of the RTC's peripheral on embedded devices I know tells you time but tell you number of ticks. The frequency of tick increment depends on your configuration on PRESCALER: For actual time you either need to rely on the higher level libraries or utilities from the RTOS.

    My question was about setting the RTC time (ticks) not reading it. I could not find a register that would allow such operation. 

    But I assumed that your main issue is to keep track of time between reboots. That is the trickiest part and that is where you need to access persistent storage. 

    Yes, this ticket, as the title suggests, is about keeping time between reboots. 

    You cannot restore old value into RTC register but you need to have your own application memory (static variable) or similar to remember the old offsets of time before reboot.

    I will probably have to store the old value in flash and read it back after reboot...

    Also, what does the first line in red mean?

    This information applies to the following SoftDevices: S130, S132, S332

    I do not know which SoftDevice I am using. How do I find out?

    Kind regards

    Mohamed

  • Not quite what you are looking for, but there is a way which seems to work fairly reliably; however note the only reliable solution is to use an external battery-backed RTC unless you can find a way to use the 2 x 8-bit GPREGRET registers, the latter by perhaps storing number of seconds since last flash write of full time field (note some bootloaders use GPREGRET registers for bootup modes so they may not be available).

    The trick which I use is to note that internal RAM is never reset, though it may be "corrupted". Provided one or more RAM areas are not selected for power down during Off, one or more instances of the tick can be held and used to continue operation after a reset. Memory corruption is detected by loading a unique value into the same RAM area. Time of startup after a reset is lost but that time may be measured (manual calibration step) and used as compensation after the reset provided the RTC is always restarted before any other time-consuming action. The example here ticks at 8 times per second, but typically once per second might be more appropriate. Note this method will not of course work across an extended power removal such as when a battery is removed.

    // Place following data in section .noinit so it doesn't get wiped on a reset
    #pragma default_variable_attributes = @ ".noinit"
      static volatile uint32_t mIamInitializedRTC; // Valid data indication for this data
      // Packet Sample Time timer interrupt, typically 8 times per second
      static volatile uint32_t mPacketSampleTimer;
      // 32-bit unsigned RTC time: number of 125 millisecond ticks from Jan 1st 2024
      static volatile uint32_t mPacketSampleTimer;
    #pragma default_variable_attributes =
    // End - Place following data in section .noinit so it doesn't get wiped on a reset
    
    bool CheckPacketSampleTimeValid(void)
    {
        // See if RTC data is valid - Use unique device ID as the valid signature
        return (mIamInitializedRTC == NRF_FICR->DEVICEID[0]);
    }
    
    void InitializeRTC(void)
    {
        // See if RTC data is valid - Use unique device ID as the valid signature
        if (!CheckPacketSampleTimeValid())
        {
           // RTC backup failed, clear the stored time
           mPacketSampleTimer = 0;
           // Indicated data is now valid - Use unique device ID as the valid signature
           mIamInitializedRTC = NRF_FICR->DEVICEID[0];
        }
        // Configure RTC and add in known reset startup time
        start lfclk and rtc
    }
    .

Reply
  • Not quite what you are looking for, but there is a way which seems to work fairly reliably; however note the only reliable solution is to use an external battery-backed RTC unless you can find a way to use the 2 x 8-bit GPREGRET registers, the latter by perhaps storing number of seconds since last flash write of full time field (note some bootloaders use GPREGRET registers for bootup modes so they may not be available).

    The trick which I use is to note that internal RAM is never reset, though it may be "corrupted". Provided one or more RAM areas are not selected for power down during Off, one or more instances of the tick can be held and used to continue operation after a reset. Memory corruption is detected by loading a unique value into the same RAM area. Time of startup after a reset is lost but that time may be measured (manual calibration step) and used as compensation after the reset provided the RTC is always restarted before any other time-consuming action. The example here ticks at 8 times per second, but typically once per second might be more appropriate. Note this method will not of course work across an extended power removal such as when a battery is removed.

    // Place following data in section .noinit so it doesn't get wiped on a reset
    #pragma default_variable_attributes = @ ".noinit"
      static volatile uint32_t mIamInitializedRTC; // Valid data indication for this data
      // Packet Sample Time timer interrupt, typically 8 times per second
      static volatile uint32_t mPacketSampleTimer;
      // 32-bit unsigned RTC time: number of 125 millisecond ticks from Jan 1st 2024
      static volatile uint32_t mPacketSampleTimer;
    #pragma default_variable_attributes =
    // End - Place following data in section .noinit so it doesn't get wiped on a reset
    
    bool CheckPacketSampleTimeValid(void)
    {
        // See if RTC data is valid - Use unique device ID as the valid signature
        return (mIamInitializedRTC == NRF_FICR->DEVICEID[0]);
    }
    
    void InitializeRTC(void)
    {
        // See if RTC data is valid - Use unique device ID as the valid signature
        if (!CheckPacketSampleTimeValid())
        {
           // RTC backup failed, clear the stored time
           mPacketSampleTimer = 0;
           // Indicated data is now valid - Use unique device ID as the valid signature
           mIamInitializedRTC = NRF_FICR->DEVICEID[0];
        }
        // Configure RTC and add in known reset startup time
        start lfclk and rtc
    }
    .

Children
  • Hello hmolesworth,

    I tried a similar approach but it does not seem to work.

    First, I could not use exactly your syntax #pragma default_variable_attributes = @ ".noinit".
    My compiler did not recognise the pragma default_variable_attributes, probably because I am using an old version of the tools NCS v1.7.0. So, I used this instead,

    #define NOINIT_SECTION ".noinit.pid_timer"

    volatile uint32_t g_timer_dev_life __attribute__((section(NOINIT_SECTION)));

    g_timer_dev_life is the variable I am using to store the data I would like to keep across reboots.

    However, my variable is getting re-initialised to 0 after every reboot.

    Any reason why it is not working?

    Kind regards

    Mohamed

  • Maybe try this, gcc usually has a pre-defined ".non_init" section - check the map file to verify:

    volatile uint32_t g_timer_dev_life __attribute__((section(".non_init")));

    Note if using a Bootloader must ensure that doesn't scribble over everything as well ..

  • Hi,

    gcc usually has a pre-defined ".non_init" section - check the map file to verify

    I can see in my map file .noinit sections but no .non_init.

    I will try this

    volatile uint32_t g_timer_dev_life __attribute__((section(".noinit")));

    Note if using a Bootloader must ensure that doesn't scribble over everything as well ..

    Not sure how to do this.

    Thank you.

Related