0

I am currently working with S32k142 microcontroller. I would like to configure a 1ms delay using the RTC clock. I have an external RTC clock(32.768KHz) which has been configured to drive the RTC module of the microcontroller. I have programmed the following:

 void wait_1ms_RTC(void)
{
    
    UINT32 tpr = (UINT32)RTC->TPR; // RTC Time Prescaler Register: increments at a freq of 32.768KHz
    UINT32 tsr = (UINT32)RTC->TSR; // RTC Time Seconds Register: increments every second

    tpr = tpr + (UINT32)32; // 32->1ms(32*30.51us=976.32us)
    if (tpr <= 32767)
    {
        while (((UINT32)RTC->TPR <= tpr)&& ((UINT32)RTC->TSR <= tsr));
    }
    else
    {
        while (((UINT32)RTC->TPR <= (tpr - 32768))&& ((UINT32)RTC->TSR <= tsr+1));
    }
}

This code simply reads the TPR and TSR register, and waits till the TPR register and TSR register reached certain value that corresponds to 1ms. This works most of the time except in few occasions where it does not create a delay of 1ms ,I am assuming it is happening during the roll over, but still cannot figure out where exactly is the issue in the code.

Any ideas on how this can be solved?

Mike
  • 4,041
  • 6
  • 20
  • 37
Liz
  • 1
  • 1
  • Can't you use interrupts? – Basile Starynkevitch Mar 29 '21 at 18:02
  • 2
    @BasileStarynkevitch. The problem with a 1ms interrupt is that when you wait for the next interrupt it will come along in 0 to 1ms. If you want exactly 1ms that is no help. Looking at the reference manual however, the RTC interrupt rate maximum is 128Hz (~7.8ms). There are other timer modules on the part of course. – Clifford Mar 29 '21 at 18:07

1 Answers1

1

You have over complicated it; you only need the TPR. By shifting the TPR value left by 1 bit, and assigning it to uint16_t, the natural modulo 216 arithmetic deals with the roll-over for you:

#define RTC_TPR_MILLISEC ((32768<<1)/1000)
void wait_1ms_RTC(void)
{
    // RTC Time Prescaler Register: 0 to 2^15, 
    // shift-left 1 but to make it 2^16 so uint16_t modulo arithmetic works
    uint16_t start = RTC->TPR << 1 ; 

    while( (RTC->TPR << 1) - start < RTC_TPR_MILLISEC )
    {
        // spin 
    }
}

It fact it is probably even simpler than that. The user manual shows that TPR is a 16 bit register and that TSR is incremented when TPR:14 transitions from 1 to 0. It does not imply that TPR is a 15 bit value and that the counter is reset to zero after 32767. I am not familiar with the part, but if in fact it is a full 16 bit counter as the documentation suggests then the shifting is unnecessary:

#define RTC_TPR_MILLISEC (32768/1000)
void wait_1ms_RTC(void)
{
    // RTC Time Prescaler Register: 0 to 2^16
    uint16_t start = RTC->TPR ; 

    while( RTC->TPR - start < RTC_TPR_MILLISEC )
    {
        // spin 
    }
}

If you are not sure why that works, see How to deal with a wrapping counter in embedded C (the example there is a 32 bit counter, but the same applies for 16 bit).

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • Yes! This way is much more efficient and it works. Thanks! – Liz Mar 30 '21 at 11:18
  • @Liz - feel free to wait for other answers, but at some point you should mark an answer as accepted. Even if a better answer comes along you can change your "accepted answer" after the fact. – Clifford Mar 30 '21 at 11:22