cancel
Showing results for 
Search instead for 
Did you mean: 

STM32WB55 RTC SRR register sometimes not monotonical

APort
Associate III

Hello,

We are running STM32WB RTC block from LSE.

Usually it works as expected (hours at a time).

One in a while, the RTC->SSR resister jumps back (up as it is a countdown reg) couple of ticks (~100 ticks) and continues from the same point as normal.

It happens with or without the RTC_CR_BYPSHAD flag (its just slightly harder to catch while using shadow registers as they require sync).

Reading the SSR value twice does not change this behavior.

It looks for example, like this if I read it in a loop:

7406

7405

7404

7494

7493

7492

...

RTC clock div is 16, prescaler is 7FFF.

It is probably not an issue with the crystal as the time itself is accurate.

Thanks in advance.

13 REPLIES 13

Consequence of shift (through RTC_SHIFTR)?

JW

Probably not; Always set to zero.

AFAIK there's no other mechanism of SSR to change abruptly by a relatively large value.

Try with a minimal application, which does nothing just reads the RTC.

Read out and check/post RTC registers content.

JW

Well, with minimal configuration, just a tight loop after minimal init, it happens even faster.

Below is the block registers when it happens (all look pretty normal to me).

I would understand issue with the clock or faulty read value, but this thing is very consistent, and I can't imagine oscillator issue causing it (we did check just to be sure, it looks alright).

I have a feeling it has something to do with reading it in a tight loop, but with no shadow registers enabled, it should not be a problem.

0693W00000QNnrYQAT.png

Adding the minimal test code

void SystemInit(void)
{
    // basic
    SCB->VTOR = 0;
    RCC->CR |= RCC_CR_MSION;
    RCC->CFGR = 0x00070000U;
    RCC->CR &= (uint32_t) 0xFAF6FEFBU;
    RCC->CSR &= (uint32_t) 0x0000FFFAU;
    RCC->CRRCR &= (uint32_t) 0xFFFFFFFEU;
    RCC->PLLCFGR = 0x22041000U;
    RCC->PLLSAI1CFGR = 0x22041000U;
    RCC->CR &= 0xFFFBFFFFU;
    RCC->CIER = 0x00000000;
 
    // init clocks
    if (HAL_IS_BIT_CLR(PWR->CR1, PWR_CR1_DBP))
    {
        HAL_PWR_EnableBkUpAccess();
        while (HAL_IS_BIT_CLR(PWR->CR1, PWR_CR1_DBP));
    }
 
    LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
 
    // setup clocks
    __HAL_RCC_HSE_CONFIG(RCC_HSE_ON);
    while (!LL_RCC_HSE_IsReady());
 
    LL_RCC_LSE_SetDriveCapability(RCC_LSEDRIVE_LOW);
    LL_RCC_LSE_Enable();
    while (!LL_RCC_LSE_IsReady());
 
    LL_RCC_SetSysClkSource(RCC_SYSCLKSOURCE_HSE);
    while (LL_RCC_GetSysClkSource() != (RCC_SYSCLKSOURCE_HSE << RCC_CFGR_SWS_Pos));
 
    __HAL_RCC_RFWAKEUP_CONFIG(RCC_RFWKPCLKSOURCE_NONE);
 
    // make sure the source for RTC set properly
    LL_RCC_SetRTCClockSource(RCC_RTCCLKSOURCE_LSE);
    if (LL_RCC_GetRTCClockSource() != RCC_RTCCLKSOURCE_LSE)
    {
        __asm volatile("bkpt #0");
    }
 
    // init RTC
    __HAL_RCC_RTC_ENABLE();
    __HAL_RCC_RTCAPB_CLK_ENABLE();
 
    // enter init
    RTC->WPR = 0xCAU;
    RTC->WPR = 0x53U;
    if((RTC->ISR & RTC_ISR_INITF) == 0U)
    {
        RTC->ISR = (uint32_t)RTC_INIT_MASK;
        while((RTC->ISR & RTC_ISR_INITF) == 0U);
    }
 
    RTC->CR = RTC_CR_BYPSHAD;
    RTC->PRER = 0x7FFF;
    RTC->PRER |= (0xF << 16);
    RTC->TR = 0;
    RTC->DR = 0;
    RTC->OR = 0;
 
    // exit init
    RTC->ISR &= ((uint32_t)~RTC_ISR_INIT);
    RTC->WPR = 0xFFU;
 
    // loop until fails
    volatile uint32_t counter = 0;
    volatile uint32_t prev = 0x7fff;
    while(1)
    {
        const uint32_t ssr = RTC->SSR;
        // check for jump (it is a countdown from 0x7fff), which is not a rollover
        if(prev < ssr && ssr - prev < (0x7fff >> 1))
        {
            volatile RTC_TypeDef* rtc = (RTC_TypeDef*)0x40002800UL;
            __asm volatile("bkpt #0");
        }
 
        counter++;
        prev = ssr;
    }
}

With BYPSHAD=1 you *must* read twice and accept only if results match.

http://www.efton.sk/STM32/gotcha/g95.html

I can imagine that with extreme PREDIV_A (here == 0) the readout may get trickier. [EDIT] What's the related APB frequency? [/EDIT]

JW

Well, I did mention it happens with or without shadow registers used (BYPSHAD), the minimal sample code just sets the BYPSHAD=1 while the original actually waits for a sync.

Now, If I understand this correctly, you must read the value twice to make sure the TR and SSR in sync (as they can change in between) and since we are reading only SSR it makes no difference. Besides, when I do it in a loop, I do read it "twice".

The values are the same, its not like I get one bad read then its ok, it shifts and is consistent afterwards.

I setup the entire system and all the buses to run on HSE @ 32MHz with no PLL in between.

Changing PREDIV_A to say 0xF (RTC->PRER = 0x7FFF | (0xF << 16U)), made no difference either.

I was looking at errata, but besides some pins interfering with the LSE clock, I did not found anything relevant (I don't use those pins either).

One thing I noticed, is that changing the loop to the following (adding small busy-wait delay) "solves" it...

I wonder if its something intentional or undocumented limitation.

In case of the later, its pretty bad news as in system with multiple elements trying to read system time I have no controll over this access timing.

    while(1)
    {
        const uint32_t ssr = RTC->SSR;
        // check for jump (it is a countdown from 0x7fff), which is not a rollover
        if(prev < ssr && ssr - prev < (0x7fff >> 1))
        {
            volatile RTC_TypeDef* rtc = (RTC_TypeDef*)0x40002800UL;
            __asm volatile("bkpt #0");
        }
 
        counter++;
        prev = ssr;
 
        int cc = 0;
        while(cc++ < 5000);
        volatile int x = cc;
    }

> I did mention it happens with or without shadow registers used (

Yes I read that; I've commented on the code you've posted.

> Now, If I understand this correctly, you must read the value twice to make sure the TR and SSR in sync

No. With BYPSHAD=1, read of asynchronous counters of the RTC may read them in inconsistent state, i.e. part of the counter has been updated and part not. Found experimentally, but the RM confirms, see the BYPSHAD=1 part of the writeup I've given above link to.

> RTC->PRER = 0x7FFF | (0xF << 16U)

PRER register has to be written in two steps, at least that's what RM says. Interestingly, RM0434 does say so in description of RTC_PRER register, but the linked description in Calendar initialization and configuration subchapter fails to give details... In RM0090 for 'F4 this section reads in the relevant point:

To generate a 1 Hz clock for the calendar counter, program first the synchronous

prescaler factor in RTC_PRER register, and then program the asynchronous prescaler

factor. Even if only one of the two fields needs to be changed, 2 separate write

accesses must be performed to the RTC_PRER register.

What hardware is this? Can you try on some "known good" board such as Nucleo or Disco or similar?

JW