cancel
Showing results for 
Search instead for 
Did you mean: 

Using subseconds register on STM32F7 RTC

CHell
Associate II

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

8 REPLIES 8

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
Associate II

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

.

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
Associate II

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
Associate II

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(".");

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

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.

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