Skip to main content
CHell
Associate
February 26, 2019
Question

Using subseconds register on STM32F7 RTC

  • February 26, 2019
  • 8 replies
  • 2051 views

We have a driver for the RTC which I am trying to enhance with sub-second resolution.

I am testing with a tight loop which is repeatedly reading the time. First I am reading the SSR register (which should 'lock' TR and DR?) then the TR, then DR (to 'unlock'). I am following the calculation for the SSR value which is described in the Ref Manual, but a strange roll-over case occurs: TR / secs stays the same, but sub-seconds has changed from near-max to zero. i.e. time has 'gone backwards'.

In detail - from one 'read' to the next - Hrs/Mins/Secs is staying the same, but sub-seconds decreases. SSR 0->255, but TR doesn't increase.

Does anyone know what the problem is? I assumed that TR would increment when SSR rolls over

    This topic has been closed for replies.

    8 replies

    waclawek.jan
    Super User
    February 26, 2019

    You probably experience consequences of the problem described in RTC calendar registers are not locked properly erratum (which in effect says that the lock is broken).

    JW

    CHell
    CHellAuthor
    Associate
    March 1, 2019

    Well I certainly needed to know about that erratum - thanks. But even correcting for this, I still see the problem. I think this piece of code demonstrates it:

     uint32_t temp_tr, temp_sr, temp_dr, roll = 0;
     do
     {
     RTCRegisters_m->RTC_ISR.Set(RTCRegisters_m->RTC_ISR.Get() & ~RTC_ISR_RSF_MASK);
     while(!IsRSFFlag()) ;
     
     temp_sr = RTCRegisters_m->RTC_SSR.Get();
     temp_tr = RTCRegisters_m->RTC_TR.Get();
     temp_dr = RTCRegisters_m->RTC_DR.Get();
     if (temp_sr == 255) { roll = 1; printf("%08X\n", temp_tr);}
     } 
     while ((temp_sr != RTCRegisters_m->RTC_SSR.Get()) || (temp_sr == 255));
     if (roll) puts(".");

    Which produces example below. See that while SSR =255, the TR seconds is incrementing just after.

    <snip>

    00121310

    .

    00121310

    00121311

    00121311

    00121311

    00121311

    00121311

    <snip>

    00121311

    00121311

    00121311

    00121311

    .

    waclawek.jan
    Super User
    March 1, 2019

    I don't quite understand that code. Shouldn't the printf be outside the loop? Also, the second read to SSR would lock TR, so when you enter the loop the next time, the "old" value of TR is read and subsequently printf'd...So you want to read DR once again after second reading of SSR.

    JW

    CHell
    CHellAuthor
    Associate
    March 1, 2019

    The printf inside the loop is just to show the value of TR whilst SSR=255.

    Re "the second read to SSR would lock TR" - was thinking that the erratum is that it might not lock TR??

    But I take your point about reading DR again to ensure an unlock before the re-read - I will explore some more. Thanks for the feedback tho.

    CHell
    CHellAuthor
    Associate
    March 1, 2019

    Ok, I re-worked this a bit, but something is still going wrong somewhere.

    PreDiv_S = 255, by the way.

    As below is how the function would ideally be [of it worked lol] - but uncommenting the commented sections demonstrates that the value of TR is incrementing after SSR has changed to 255.

    e.g. TR=1, SSR=0 -> TR=1, SSR=255 -> TR=2, SSR=255

    Therefore the caller can see time (momentarily) go backwards - subsec rolls over to zero without secs changing: 1.996, 1.0, 2.0

    I'm calculating the subsec value as per the Ref Manual: (PreDiv_S - SSR) / (PreDiv_S + 1).

    Can't see what else I'm getting wrong; wondering if it's another bug in the silicon?

    If the latter, there doesn't seem to be any workaround except to wait while SSR=PreDiv_S, since TR can't be trusted while SSR is at that value.

     uint32_t temp_tr, temp_sr, temp_dr, temp_sr2, roll=0;
     do
     {
     temp_dr = RTCRegisters_m->RTC_DR.Get();
     
     RTCRegisters_m->RTC_ISR.Set(RTCRegisters_m->RTC_ISR.Get() & ~(RTC_ISR_RSF_MASK | RTC_ISR_INIT_MSK)); // STM32Cube driver clears INIT too?
     while(!IsRSFFlag()) ;
     
     temp_sr = RTCRegisters_m->RTC_SSR.Get();
     temp_tr = RTCRegisters_m->RTC_TR.Get();
     temp_dr = RTCRegisters_m->RTC_DR.Get();
     temp_sr2 = RTCRegisters_m->RTC_SSR.Get();
     //if (temp_sr == 255) { roll = 1; printf("%08X\n", temp_tr);}
     } 
     while (/*(temp_sr == 255) ||*/ (temp_sr != temp_sr2));
     if (roll) puts(".");

    waclawek.jan
    Super User
    September 4, 2019

    Do you have *unlocked writes to RTC registers* while running this routine?

    Otherwise, RTC_ISR.RSF is locked, thus the check for shadows reload in lines 6 and 7 is ineffective; and then what you read in line 9 and 10 and 11 may still be the old "not-yet-reloaded" value, locked by SSR read at line 12 at the *previous* iteration of this loop.

    Just been bitten by this...

    JW

    After Forever
    Senior III
    September 4, 2019

    Colin, please don't forget that if any optimization is in use the compiler can remove those lines below from your loop because you don't use the result:

    ...

    temp_tr = RTCRegisters_m->RTC_TR.Get();

    temp_dr = RTCRegisters_m->RTC_DR.Get();

    ...

    If you don't want that to happen declare the variables as volatile.

    waclawek.jan
    Super User
    September 4, 2019

    temp_tr *is* used in case the commented portion is uncommented (see accompanying text to that code).

    If RTCRegisters_m->RTC_TR.Get(); contains enough "volatility" so that the actual RTC_TR and RTC_DR registers get read, in this context it does not matter if the result is thrown away.

    JW