RTC Timer re-initialization

Hi,

I have nRF52840 DK and am using nRF5_SDK_17.0.2_d674dde.

I am not using SoftDevice, and Zephyr RTOS.

I have a simple program and using RTC0 timer. My code snippet:

const uint32_t m_dwPreScales[10]={
4095,1365,819,585,455,  315,273,195,151,105};
const uint32_t m_dwRTCsCC[10]={
8, 24, 40, 56, 72,  104,120,168,216,312};

void rtc_initialize(void) {
    uint32_t err_code;
    k++;
    if (k>=9) k=0;
    //k=5;
    //=======================================================
    NRF_RTC0->TASKS_STOP= 1;
    NVIC_ClearPendingIRQ(RTC0_IRQn);
    NVIC_DisableIRQ(RTC0_IRQn);
    NRF_RTC0->EVTENCLR = RTC_INT_MASK;
    NRF_RTC0->INTENCLR = RTC_INT_MASK;
    //=======================================================
    volatile uint32_t prescaler= m_dwPreScales[k];
    volatile uint32_t duration_ticks= m_dwRTCsCC[k];
    NRF_RTC0->TASKS_CLEAR= 1;
    //NRF_RTC0->TASKS_TRIGOVRFLW= 1;
    NRF_RTC0->PRESCALER = prescaler;
    NRF_RTC0->CC[0]     = 3;
    NRF_RTC0->CC[1]     = duration_ticks;

    NRF_RTC0->INTENSET = RTC_CHANNEL_INT_MASK(0);
    NRF_RTC0->EVTENSET = RTC_CHANNEL_INT_MASK(0);
    NRF_RTC0->INTENSET = RTC_CHANNEL_INT_MASK(1);
    NRF_RTC0->EVTENSET = RTC_CHANNEL_INT_MASK(1);
    NRF_RTC0->EVTENSET = NRF_RTC_INT_TICK_MASK;
    NVIC_SetPriority(RTC0_IRQn, 2);  
    NVIC_EnableIRQ(RTC0_IRQn);
    NRF_RTC0->TASKS_START=1U;
    //======================================================
}
 
void RTC0_IRQHandler(void)
{
   if(NRF_RTC0->EVENTS_TICK){
       NRF_RTC0->EVENTS_TICK = 0;
   }
    
   if ((NRF_RTC0->EVENTS_COMPARE[0] != 0) )  
   {
      NRF_RTC0->EVENTS_COMPARE[0] = 0;  
      nrf_gpio_pin_toggle(LED1_PIN);
   }

   if ((NRF_RTC0->EVENTS_COMPARE[1] != 0))
   {
      NRF_RTC0->EVENTS_COMPARE[1] = 0;  
      nrf_gpio_pin_toggle(LED1_PIN);
      rtc_initialize();
      //NRF_RTC0->TASKS_START =1U;
   }
   if(NRF_RTC0->EVENTS_OVRFLW){
       NRF_RTC0->EVENTS_OVRFLW = 0;
   }  
   return ;
}

The code runs OK, the period is 1 second for any fixed k, which basically prescaler and CC[1]. Only CC[0] and CC[1] are enabled. For any fixed k with a pair of prescaler and CC1, with event period is 1 second.

But If I change k++, everytime re-initialization, the period becomes  1s, 2s, 3s,4s, 5s, ... (may not exactly).

Could you please help me take a code review for apparent mistakes?

Thank you,

David

Parents
  • Hi David

    I can't see any obvious mistake in the code, other than the fact that it is a bit unorthodox to reconfigure the entire peripheral and it's interrupt inside its own interrupt handler. 

    What happens if you rather try just to clear the RTC timer when the EVENTS_COMPARE[1] interrupt happens, and only do a full re-initialize every 10 seconds or so (either by using a separate timer to schedule this, or by checking a counter inside the interrupt and wait for the counter to reach a certain value)?

    Also, you could try just to run the rtc_initialize() function from main rather than interrupt context, by setting a flag in the interrupt handler that you can check from the main loop. 

    Best regards
    Torbjørn

  • Hi Torbjorn

    Thank you so much for your code review, explanation, and solutions for me.

    I now realized that RTC in on LFCLK, it needs time to settle or for prescaler, etc. to take effective. I will resolve it my self based on my application.

    Best Regards,

    David

Reply Children
  • Hi David

    Sounds good. Unless you have more questions I will consider the case resolved then Slight smile

    Best regards
    Torbjørn

  • Hi Torbjorn,

    I am good and you can close the case for me. Thank you for your help.

    But just as feedback, I have tested that CC[] can be dynamically reinitialized, except prescaler. Although the re-initialization  is in the ISR, but the interrupt and tasks are stopped before initialization. So it shouldn't matter. Also RTC is on LFCLK, but the registers for RTC is on another CLK as CPU? I validated prescaler by read-out into a variable. I am not sure why CC can be reinitialized while Prescaler not. As I said, I am good and I can fix the prescaler all the time, and just change CC values.

    Best Regards,

    David Zhou

  • Hi David

    The peripheral registers are running off of the 16MHz peripheral clock, so you should be able to access these much faster than the LFCLK, but the RTC module itself might not read the value until the next tick happens. 

    I notice from the PRESCALER section in the product specification that you can only write the PRESCALER value while the RTC is stopped, and quite possibly you need to wait for one LFCLK period after stopping the RTC in order to ensure it is fully stopped. 

    The CC registers does not have this limitation, and can be changed at any time. 

    While it is a bit more work to change the PRESCALER value, this is not something that is usually done dynamically. Most applications use a static PRESCALER value, and simply change the CC values instead when changing the wakeup interval. 

    Best regards
    Torbjørn

Related