RTC seconds not updated properly

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

RTC seconds not updated properly

1,822 Views
cwati
Contributor III

I'm monitoring the behavior of the second and TPR (Prescaler) of this K22F board.

I notice that sometimes the Second register (HW_RTC_TSR_RD) hasn't been updated when the Prescaler (HW_RTC_TPR_RD) has.

Code snippet is as follows:

static uint32_t last_sec = 0, last_tpr = 0;

dummy_tpr = HW_RTC_TPR_RD(RTC_BASE);

current_tpr = HW_RTC_TPR_RD(RTC_BASE);

while (dummy_tpr != current_tpr) {

          dummy_tpr = HW_RTC_TPR_RD(RTC_BASE);

          current_tpr = HW_RTC_TPR_RD(RTC_BASE);

}

dummy_rtc = HW_RTC_TSR_RD(RTC_BASE);

current_sec = HW_RTC_TSR_RD(RTC_BASE);

while (dummy_tpr != current_tpr) {

     dummy_rtc = HW_RTC_TSR_RD(RTC_BASE);

     current_sec = HW_RTC_TSR_RD(RTC_BASE);

}

if ((last_sec != 0) && (last_tpr != 0)) {

     if ((last_sec == current_sec) && (last_tpr > current_tpr)) {

          //This is disaster...

          //STOP HERE in DEBUGGER

     }

}

last_sec = current_sec;

last_tpr = current_tpr;

Note: I re-read registers per this entry:

RTC counters behavior (seconds and prescaler registers)


I notice that these cases happen sometimes

first read:
second: 0x2, tpr: 0x7FFF

then next read:

second: 0x2, tpr: 0x0  ----> or sometimes even 0x1

Which tells me that most likely TPR has incremented while second hasn't?

Is this a known HW problem? Or, did I miss some settings?

Any help is appreciated.  Thanks.

0 Kudos
8 Replies

975 Views
cwati
Contributor III

Hmpf seems like some sort of race condition

When I read time is, say 0/7FFF

I read TPR first to get: 7FFF
By the time I got second, it has incremented (time is now 1/000), so I get: 1


I end up with 1/7FFF, which is wrong.  It should be 0/7FFF or 1/0000.

Thanks,
Cecylia

0 Kudos

975 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Cecylia,

I'd like to confirm with you about this phenomenon below whether always happens or does sometimes happen.

I'm looking forward to your reply.

first read:

second: 0x0, tpr: 0x7FFF

second read:

second: 0x1, tpr: 1x7FFF


Have a great day,
Ping

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

975 Views
cwati
Contributor III

Jeremy,

Another experiment shows:

* On our first read, say time was

* 0/7FFF

*

* It is possible that the above read yields:

* 7FFF, then 0 (which is correct)

* or

* 7FFF, then 1  --> when second has incremented.

Thanks,

Cecylia

0 Kudos

975 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Cecylia,

Thanks for your reply,

I'll appreciate if you can share the whole demo, then I'm going to run the demo on FRDM-K22F for testing.

I'm looking forward to your reply.

And If you get the result by using the Mark's method, please let me know.
Have a great day,
Ping

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

975 Views
cwati
Contributor III

Jeremy, sorry I can't share the demo as it contains proprietary functions.
The functions I pasted up there should be enough.  You can create your own demo, and if it works then you can share here.  But, as far as I'm concerned there seems to be an obvious problem with the hardware being inconsistent in updating its registers' values. 

Mark's method doesn't work.  I'm creating my own software function to make sure at least the timer doesn't revert back in time.

Thanks,
Cecylia

0 Kudos

975 Views
cwati
Contributor III

Jeremy, please look at my previous reply to my own question.  It looks like a race condition, so it'd happen every time TPR moves from 0x7FFF to 0.
If I put a break point, I'd say it get hit every few seconds.
I'm going to try Mark's solution.

0 Kudos

975 Views
mjbcswitzerland
Specialist V

Hi

Try temporarily disabling the RTC when reading the prescalter:

RTC_SR = 0; // disable

read values

RTC_SR = RTC_SR_TCE; // re-enable

Regards

Mark

Kinetis: µTasker Kinetis support

K22: µTasker Kinetis FRDM-K22F support  / µTasker Kinetis TWR-K22F120M support

RTC/Time Management: http://www.utasker.com/docs/uTasker/uTasker_Time.pdf

For the complete "out-of-the-box" Kinetis experience and faster time to market

0 Kudos

975 Views
cwati
Contributor III

Mark,

That actually sounds like a very legit solution, but I still hit the breakpoints very often.
Almost everytime the TPR transitions from 0x7FFF to 0.

Code snippet below.

Thanks,
Cecylia

/*******************************************************************************

* Get K22F's RTC in milliseconds

******************************************************************************/

uint32_t k22f_get_rtc(void) {

  static uint32_t last_sec = 0, last_tpr = 0;

  uint32_t dummy_tpr, dummy_tpr1, current_tpr;

  uint32_t rtc_milliseconds;

  uint32_t dummy_rtc, current_sec;

  uint32_t cnt;

  const uint32_t cnt_max = 0xFF;

  uint32_t temp_SR;

  //debug

  static uint32_t last_total_ms = 0;

  uint32_t total_ms_diff, current_total_ms;

  /* WARNING: There will be consequences if you change the order of the reading!

  * Make sure you know what you're doing.

  *

  * For example:

  * At time 0 second, 0x7FFF

  * Read TPR: get 0x7FFF

  * Read second: get 1 (already incremented)

  * So you need to fix it.

  *

  * If you read second first

  * second: get 0,

  * TPR: get 0 (already incremented).

  * Must handle these cases.

  */

  //cw testing todo

  // Disable time counters TSR and TPR before writing

  temp_SR = BR_RTC_SR_TCE(RTC_BASE);

  BW_RTC_SR_TCE(RTC_BASE, 0);

  /* We have to read twice or more until we get same values */

  dummy_tpr = HW_RTC_TPR_RD(RTC_BASE);

  current_tpr = HW_RTC_TPR_RD(RTC_BASE);

  cnt = 0;

  while (dummy_tpr != current_tpr) {

  if (++cnt == cnt_max) {

  /* Put a count to avoid infinite loop.  Shouldn't arrive here unless HW

  * goes kaput. */

  return UINT32_MAX;

  }

  dummy_tpr = HW_RTC_TPR_RD(RTC_BASE);

  current_tpr = HW_RTC_TPR_RD(RTC_BASE);

  }

  /* We have to read twice or more until we get same values */

  dummy_rtc = HW_RTC_TSR_RD(RTC_BASE);

  current_sec = HW_RTC_TSR_RD(RTC_BASE);

  cnt = 0;

  while (dummy_rtc != current_sec) {

  if (++cnt == cnt_max) {

  /* Put a count to avoid infinite loop.  Shouldn't arrive here unless HW

  * goes kaput. */

  return UINT32_MAX;

  }

  dummy_rtc = HW_RTC_TSR_RD(RTC_BASE);

  current_sec = HW_RTC_TSR_RD(RTC_BASE);

  }

// /* Check again.  If TPR has gone back to 0, then we should reduce the sec.

// * Meaning, on our first read, say time was

// * 0/7FFF

// * during read we got: 7FFF, then 1. ---> 1/7FFF which is 1 second off

// * now time is actually 1/000

// * We reduce the second by 1 to get: 0/7FFF.

// */

// dummy_tpr = HW_RTC_TPR_RD(RTC_BASE);

// dummy_tpr1 = HW_RTC_TPR_RD(RTC_BASE);

// while (dummy_tpr != dummy_tpr1) {

// if (++cnt == cnt_max) {

// /* Put a count to avoid infinite loop.  Shouldn't arrive here unless HW

// * goes kaput. */

// return UINT32_MAX;

// }

// dummy_tpr = HW_RTC_TPR_RD(RTC_BASE);

// dummy_tpr1 = HW_RTC_TPR_RD(RTC_BASE);

// }

// if (dummy_tpr < current_tpr) {

// current_sec = current_sec - 1;

// }

  // TPR = 1 equals to ~30.5us = 1000000us / 32768Hz = (30.5/1000)ms = (1000/32768)ms

  rtc_milliseconds = current_tpr * 1000 / 32768;

  current_total_ms = (current_sec * 1000) + rtc_milliseconds;

  if (current_total_ms < last_total_ms) {

  //dummy.  this is not good!  

  dummy_tpr = 0;                 ----> [cw] Put a breakpoint here

  }

  last_sec = current_sec;

  last_tpr = current_tpr;

  last_total_ms = current_total_ms;

  //cw todo re enable Timer

  BW_RTC_SR_TCE(RTC_BASE, temp_SR);

  return current_total_ms;

}

/*******************************************************************************

* Enable K22F's RTC registers (TSR - seconds counter, TPR - prescaler counter

* TSR is incremented every second

* TPR is incremented every 32.768 kHz

******************************************************************************/

void k22f_enable_rtc() {

  // Enable 32.768 kHz oscillator

  BW_RTC_CR_OSCE(RTC_BASE, 1);

  // Wait 100ms for oscillator output to settle down

  OSA_TimeDelay(100);

  // Disable time counters TSR and TPR before writing

  BW_RTC_SR_TCE(RTC_BASE, 0);

  // Clear the RTC_TPR counter

  HW_RTC_TPR_CLR(RTC_BASE, 0xFFFFFFFFU);

  // Write the RTC_TSR with 1 second value

  HW_RTC_TSR_WR(RTC_BASE, 1);

  // Enable time counters TSR and TPR after writing

  BW_RTC_SR_TCE(RTC_BASE, 1);

}

// Main loop

{

       //some init

      k22f_enable_rtc();

  while (true)

  {

       k22f_get_rtc();

       for (uint16_t tmp_cnt = 0; tmp_cnt < 0x255; tmp_cnt++) {

            ;

       }

      //doing other bunch of stuff

  }

}

0 Kudos