cancel
Showing results for 
Search instead for 
Did you mean: 

32F417 RTC errata sheet if reading subseconds - is this code ok?

PHolt.1
Senior III

I am referring to the bug whereby the shadowing of the date/time registers is very occassionally broken if the sync prescaler was read beforehand.

That reading order is exactly what the ST HAL functions do. See below.

HAL_RTC_GetTime reads the SSR and the time.

HAL_RTC_GetDate reads the date.

I have been doing tests and reading the RTC at 5Hz it fails once every few hours; then my code below reads the whole lot again.

int getrtc (struct tm * mytime)

{

RTC_TimeTypeDef sTime = {0};

RTC_DateTypeDef sDate = {0};

int rtcret1=0;

int rtcret2=0;

RTC_Lock();

// Implement RTC errata sheet (SSR needs reading before and after date/time and compared, until same)

uint32_t last_subsec;

volatile uint32_t errorcnt=0; // for doing a breakpoint

do

{

last_subsec = (uint32_t)(hrtc.Instance->SSR);

// Always read both, even if 1st returns an error

rtcret1 = HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);

rtcret2 = HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);

errorcnt++;

}

while (last_subsec != sTime.SubSeconds);

RTC_Unlock();

if ( (rtcret1!=0) || (rtcret2!=0))

return 2;

// Convert the 32F4 RTC API to the K&R standard C clock structure "tm"

// Comments in setrtc above

// No error checking - would be meaningless

mytime->tm_year = sDate.Year+100;

mytime->tm_mon = sDate.Month-1;

mytime->tm_mday = sDate.Date;

mytime->tm_wday = sDate.WeekDay; // assume the RTC maintained the day of week correctly

if (sDate.WeekDay==7) mytime->tm_wday = 0;

mytime->tm_hour = sTime.Hours;

mytime->tm_min = sTime.Minutes;

mytime->tm_sec = sTime.Seconds;

// Load subseconds into a global variable, as microseconds

// There were problems with this when using the SHIFTR method to advance the RTC...

g_SubSeconds = ((RTC_SYNC_PREDIV-sTime.SubSeconds)*1000000L)/(RTC_SYNC_PREDIV+1);

// Calculate day of year from the date, because that doesn't come from the RTC.

mytime->tm_yday = day_of_year(mytime->tm_mday, mytime->tm_mon, mytime->tm_year);

// Return -1 for daylight saving, as per K&R

mytime->tm_isdst = -1;

return 0;

}

29 REPLIES 29
TDK
Guru

@PHolt.1​, I put the proper code in the first response to this post. Did you miss that? Or do you not like it or something? Just want to make sure you saw it, don't mind if you use it or not.

"Eliminating HAL" by copy/pasting the HAL function code into your own misses the point.

If you feel a post has answered your question, please click "Accept as Solution".
PHolt.1
Senior III

Apologies TDK. I came to the same conclusion - that reading SSR twice at the beginning was the cause of the problem, but removing that didn't fix the problem.

This form of the loop works fine, but only with the extra DR read at the very end and dumping the result

	uint32_t last_subsec;
	uint32_t loopcnt=0;
	uint32_t tmpreg = 0U;
	uint32_t datetmpreg = 0U;
 
	// Implement RTC errata sheet (SSR needs reading before and after date/time and compared, until same)
	// There is a max loop count, which is simpler than a timeout.
	// It was found experimentally that you get a dud SSR read every ~10k operations here.
 
	do
	{
 
		// Get subseconds, and save for later comparison
		last_subsec = (uint32_t)(hrtc.Instance->SSR);
 
		// Always read both time and date, in that order
 
		//HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
 
		sTime.SubSeconds = last_subsec; 	// (uint32_t)(hrtc.Instance->SSR);
 
		// Get SecondFraction structure field from the corresponding register field - not used
		//sTime.SecondFraction = (uint32_t)(hrtc.Instance->PRER & RTC_PRER_PREDIV_S);
 
		/* Get the TR register */
		tmpreg = (uint32_t)(hrtc.Instance->TR & RTC_TR_RESERVED_MASK);
 
		//HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
 
		/* Get the DR register */
		datetmpreg = (uint32_t)(hrtc.Instance->DR & RTC_DR_RESERVED_MASK);
 
		loopcnt++;
 
	}
	while ( (last_subsec != (uint32_t)(hrtc.Instance->SSR)) && (loopcnt < MAX_SSR_READS) );
 
	if (loopcnt >= MAX_SSR_READS) return 2;
 
	// A dummy DR read to unlock the regs because last read above is the SSR, but the
	// last read should be the DR
	hrtc.Instance->DR;

PHolt.1
Senior III

Can anyone suggest the order of register accesses which deals with the RTC SSR errata issue i.e. reading SSR before and after, while at the same time reading DR as last?

Piranha
Chief II

EDIT: This example is updated according to the comments in further discussion!

I have an idea...

void RTC_DateTime(void)
{
	uint32_t rDR1 = RTC->DR;
	uint32_t rTR = RTC->TR;
	uint32_t rDR2 = RTC->DR;
 
	uint32_t rDR = (rTR >= 0x130000) ? rDR1 : rDR2;
 
	// Use rDR, rTR variables.
}
 
#define PREDIV_S 0xFF
 
void RTC_DateTimeSubsecond(void)
{
	uint32_t rTR1 = RTC->TR;
	uint32_t rDR1 = RTC->DR;
	uint32_t rSSR = RTC->SSR;
	uint32_t rTR2 = RTC->TR;
	uint32_t rDR2 = RTC->DR;
 
	uint32_t rTR, rDR;
 
	if (rSSR > (PREDIV_S / 2)) {
		rTR = rTR2; rDR = rDR2;
	} else {
		rTR = rTR1; rDR = rDR1;
	}
 
	// Use rDR, rTR, rSSR variables.
}

Some comments on the code:

  • The code works regardless off BYPSHAD selected mode, but, because shadow registers are updated once every two RTCCLK cycles, BYPSHAD=1 should be slightly more accurate. The code works correctly only with BYPSHAD=0.
  • Instead of (rTR >= 0x130000) the mathematically optimal comparison should be (rTR >= 0x120000) for 24-hour format and (rTR & RTC_TR_PM) for 12-hour format, but the proposed version will work for both formats.
  • The code assumes that the time between the two reads of a higher unit register does not exceed half of the smaller unit's overflow period. That means a maximum of 12 hours between the DR reads, which are reduced to 11 hours because of the universal 24/12-hour comparison code, but that is not an issue. For the version with subseconds, a maximum of 500 ms is allowed between the TR reads, which still should not be a problem in any decent design.

Honestly, I just figured this out in my head and haven't even tested yet. If I am not missing something, this code is simple, small, fast, deterministic and solves everything related to reading RTC date and time registers. Actually the general principle can also be used for reading the Ethernet PTP clock registers, which doesn't even have a lock mechanism, and can be used on reading CNT based timestamps from chained timers.

@Community member​ , @Community member​ , @TDK​ , everyone - can you review this and comment?

Nice.

The above method is OK, and as you've said, used for ages with non-atomically-changing counters (e.g. timer + overflow counter in ISR). Devil is in the details, as usually. This is hard to get right.

The time-only example is IMO OK with both settings of BYPSHD.

The real problem is with BYPSHD=0. When rTR1 = RTC->TR; DR is locked, so uint32_t rDR2 = RTC->DR; in fact reads the value locked *before* SSR is read, which is not what you want.

I believe the following modification should work also for BYPSHD=0:

void RTC_DateTimeSubsecond(void)
{
	uint32_t rTR1 = RTC->TR;    
	uint32_t rDR1 = RTC->DR;   
	uint32_t rSSR = RTC->SSR;  
	uint32_t rTR2 = RTC->TR;
	uint32_t rDR2 = RTC->DR;
 
	uint32_t rTR, rDR;
 
	if (rSSR > (PREDIV_S / 2)) {   // SSR is downcounter
		rTR = rTR2; rDR = rDR2;
	} else {
		rTR = rTR1; rDR = rDR1;
	}
 
	// Use rDR, rTR, rSSR variables.
}

I may be wrong of course.

JW

PHolt.1
Senior III

Interesting. I can sort of see how that might work.

However, what is actually wrong with my code?

The RTC is made up like this

[ async (ripple) counter ] -->> [ sync counter SSR ] -->> [ time counter ] -->> [ date counter ]

That is how all RTCs are designed. I did one in an ASIC. It is done to minimise how much logic gets clocked downstream. You prescale as much as you can with a ripple counter (down to 1Hz ideally, but if you do that, you don't get subseconds).

I read SSR first, store it

Read the time and date

Read SSR again and compare

If the SSR is the same, then the time and date could not have possibly been clocked, could it, because it is clocked from the output of the SSR synchronous counter, and if that produced a clock pulse then its value must have changed.

It is like reading any other chained counter arrangement. If you read the counter and "somehow" ensure that the input to the counter chain has not seen a clock since you started reading it, then the counter chain which you have just read must be self-consistent.

I've done tons of hardware design, including Xilinx FPGA stuff. Of course I may not be properly understanding how the 32F4 RTC works. STM do some funny stuff, with using PCLK1 or 2 to sync some things...

I need to add a timeout into that do/while loop otherwise the whole thing will hang if the 32k oscillator stops running (e.g. the xtal is duff, not soldered, etc). That's easy using CYCCNT.

> However, what is actually wrong with my code?

> I read SSR first, store it

> Read the time and date

> Read SSR again and compare

That's OK. if "read the time and date" means reading RTC_TR and RTC_DR in this order, but you need to read DR if BYPSHD=0. I may have mentioned it already.

And, as I've said, make sure there's no interrupt between first and second SSR read lasting exactly 1 second in the SSR granularity (or integer multiples). Using RTOS and Cube does not sound much reassuring in this regard, but it's your code.

> I need to add a timeout into that do/while loop otherwise the whole thing will hang if the 32k oscillator stops running

No, it won't. It will loop only if SSR changes. If XTAL stops XTALling, SSR wil stay the same.

JW

PHolt.1
Senior III

Oh yes I didn't think about it =) Thanks!

I do read DR - see code above.

I am very sure there won't be a 1 sec delay. The RTOS is pre-emptive and runs on a 1ms tick.

I am absolutely uninterested in RTOS, but last time I checked "pre-emptive" meant "it can interrupt and switch task whenever it wants" (i.e. also in between the two SSR reads). I am not sure what "runs on 1ms tick" means, but surely there are ways to tell the RTOS "this task is important, if you've taken control from it, switch back no later than after 𝗑𝗑�?�� ms", or at least have a promise it will do that.

Remember reading an article titled "Windows NT as a real-time operating system" or something like that, back then.

JW

Tried to write up this and related findings. Comments welcome.

JW