RTC Module doesn't work well

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

RTC Module doesn't work well

710 Views
cab
Contributor III

CW 10.2 (1.0.0) Win7x64 MC9S08LG32

 

I want to create a real-time clock with ms resolution.  Tried using the RTC module, but the register functions are less than ideal.  I would not like to have an interrupt every ms--how about every 150 ms?  That seems reasonable but not without flaws for my MCU.

 

This looks like a reasonable try:

void startRTC() {  SCGC1_RTC= 1; RTCSC_RTCLKS= 0; //binary 00 RTCSC_RTCPS=  8; //binary 1000, ms resolution RTCMOD= 250;            //250ms rollover  RTCSC_RTIE= 1;  //enable RTC rrupts} //--------------------------------------------startRTC()
ulong rtcMs=0;

interrupt VectorNumber_Vrtc void Vrtc_isr(void) {//every 250 ms. RTCSC_RTIF= 1; //Clear interrupt rtcMs+= 250;        //Update} //--------------------------------------------Vrtc_isr()ulong getMs(void) { //ms since power-up (1yr rollover) ulong ms; RTCSC_RTIE= 0; ms= rtcMs + RTCCNT; RTCSC_RTIE= 1; return ms;} //-------------------------------------------getMs()

 

The problem is, what happens when RTCMOD is reached?  The interrupt occurs sometime later, but BAM! as soon as RTCMOD=250, RTCCNT is zeroed--but rtcMs has not been increased.  So until the interrupt occurs, getMs() has lost 250ms.  No way to avoid occasional mistakes--the user could be -reading- rtcMs when RTCMOD rolls over.

 

A solution would be an option to not zero RTCCNT when RTCMOD is reached--let the isr clear RTCCNT, that would work.  I don't see that option.  The example in the manual shows a 1-sec interrupt, but the resolution is only one second.  Am I missing a feasible way to use the RTC module and create a ms-timer without getting interrupted every ms?

- Well perhaps getMs() could look at the RTIF flag and call the isr.  But this looks like a race condition as well.  The manual is silent.

Labels (1)
0 Kudos
2 Replies

403 Views
bigmac
Specialist III

Hello, 


cab wrote:

CW 10.2 (1.0.0) Win7x64 MC9S08LG32

 

The problem is, what happens when RTCMOD is reached?  The interrupt occurs sometime later, but BAM! as soon as RTCMOD=250, RTCCNT is zeroed--but rtcMs has not been increased.  So until the interrupt occurs, getMs() has lost 250ms.  No way to avoid occasional mistakes--the user could be -reading- rtcMs when RTCMOD rolls over.

  


If we are to believe the example shown in Fig. 15.6 of the reference manual, RTCCNT should remain at the RTCMOD value for a full prescaled clock period, with the count being zeroed very closely to the flag being set (probably within a bus cycle, or so).  This would imply that any problems you have experienced are more likely to be caused by other non-related interrupts delaying the execution of the RTC ISR. 

 

This also means that, for a required timeout period of 250ms, the RTCMOD register should have a value of 249.  A value of 250 would actually give a period of 251ms.  However, with the use of the 1kHz oscillator, with its poor accuracy, this is rather academic.

 

My solution to the delayed ISR processing would be to simply monitor the flag status, and correct the returned value if the flag is set (signifying that the ISR has not yet been processed).  Then let the ISR processing subsequently occur in the normal fashion.  When temporarily disabling interrupts within the getMS() function, it is better to globally disable all interrupts rather than just the RTC interrupt.

 

A final, but important issue is that, because the global variable rtcMs is updated within the ISR, it needs to be defined as volatile, for the benefit of the getMs() function, or any other function that reads the variable.

 

volatile ulong rtcMs = 0;ulong getMs(void){   ulong ms;   __asm sei;           // Disable interrupts   ms = rtcMs + RTCCNT;   if (RTCSC_RTIF)      ms += 250;        // Corrected value   __asm cli;           // Re-enable interrupts   return ms;}

 

Regards,

Mac

 

0 Kudos

403 Views
rocco
Senior Contributor II

Hi Cab,

 

Yes, it is a pain in the neck. The problem is that you are trying to use the RTC beyond the capability that the designers envisioned. I think their original plan was just to have something that could keep time while the processor was sleeping, and wake it periodically.

 

Here is how I would fix it:

You already disable the interrupt in your "getMS" routine. You may want to use SEI and CLI to disable all interrupts, so that no ISR can delay processing and possibly screw-up the following approach.

 

After disabling interrupts, check the RTC interrupt flag to see if the interrupt is pending.

   if it is, then you know that "rtcMs" is out of date and you return ( rtcMs+RTCCNT+250 ).
   If not, than read RTCCNT (save it temporarily) and test for zero.

       If it is zero, then test the interrupt flag again and return ( rtcMs+RTCCNT+250 ) if it's now set.

       If it's not zero, then "rtcMs" is valid, so you can add it with temporarily saved RTCCNT and return ( rtcMs+RTCCNT ).

 

The idea is to make sure you accommodate a rollover, whenever it happens, as it could happen anywhere within "getMs". We accommodate it simply by adding the missing 250 to the returned value whenever we detect that RTCCNT has rolled over but "rtcMs" has not been updated yet. When "getMS" re-enables interrupts, than the RTC ISR can update "rtcMs" as it normally does for the next time.

 

Let me know if you think that will work.

0 Kudos