I stumbled upon an issue where the LFCLKSTAT register in the NRF_CLOCK peripheral can not be properly evaluated after a softreset if the application that was running before the soft reset was executed uses either the COMPARE1 or COMPARE2 interrupt of an RTC peripheral.
I tried to condense the code leading to this issue to a concise excample.
The LFCLK startup code is shown below. I evaluate the LFCLKSTAT register first, because in a real-life application, the bootloader may have already started the LFCLK.
if (((NRF_CLOCK->LFCLKSTAT & CLOCK_LFCLKSTAT_STATE_Msk) >> CLOCK_LFCLKSTAT_STATE_Pos) == 0) { // This block is never reached if RTC compare event 1 or 2 were used. // select LFCLK switch (m_lfclk_config.source) { case NRF_CLOCK_LF_SRC_RC: NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_RC; break; // NRF_CLOCK_LF_SRC_RC case NRF_CLOCK_LF_SRC_XTAL: NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_Xtal; break; // NRF_CLOCK_LF_SRC_XTAL case NRF_CLOCK_LF_SRC_SYNTH: NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_Synth; break; // NRF_CLOCK_LF_SRC_SYNTH } NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; // Enable interrupt, so we can call __WFE later. NRF_CLOCK->INTENSET = CLOCK_INTENSET_LFCLKSTARTED_Msk; #if defined(NRF52832_XXAA) || defined(NRF52832_XXAB) nrfx_clock_anomaly_132(); #endif // defined(NRF52832_XXAA) || defined(NRF52832_XXAB) NRF_CLOCK->TASKS_LFCLKSTART = 1; while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0) { __WFE(); } NRF_CLOCK->INTENCLR = CLOCK_INTENSET_LFCLKSTARTED_Msk; NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; }
The code inside the above if-block does not get executed after a soft reset if RTC compare event 1 or 2 were used.
The main-function, which calls the above clock initialisation and initializes the RTC is shown below:
int main(void) { nrf_gpio_cfg_output(28); nrf_gpio_cfg_output(4); // allow wakeup from sleep on pending interrupt SCB->SCR |= SCB_SCR_SEVONPEND_Msk; nrf_gpio_pin_set(4); init_lfclk_f(); nrf_gpio_pin_clear(4); // setup and start RTC0 NRF_RTC0->PRESCALER = (RTC_PRESCALER - 1); NRF_RTC0->CC[1] = (NRF_RTC0->COUNTER + RTC_INTERVAL) & RTC_CNT_MAX; NRF_RTC0->INTENSET = RTC_INTENSET_COMPARE1_Msk; NRF_RTC0->EVTENSET = RTC_EVTEN_COMPARE1_Msk; NVIC_SetPriority(RTC0_IRQn, APP_IRQ_PRIORITY_LOW); NVIC_EnableIRQ(RTC0_IRQn); NRF_RTC0->TASKS_START = 1; // enable interrupts __ASM("CPSIE i"); nrf_gpio_pin_set(28); while(1) { // enter application wait __WFE(); } return (0); }
And the RTC0_IRQHandler...
void RTC0_IRQHandler(void) { uint_fast32_t next_tick; static uint8_t num_invoked = 0; // COMPARE1 event ? if (NRF_RTC0->EVENTS_COMPARE[1] != 0) { // clear event NRF_RTC0->EVENTS_COMPARE[1] = 0; // force write operation to be done by reading the value back in Cortex M4F devices // URL: http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.nrf52.v1.0.0/migration/preface.html (void)NRF_RTC0->EVENTS_COMPARE[1]; // calculate next tick next_tick = (NRF_RTC0->CC[1] + RTC_INTERVAL) & RTC_CNT_MAX; // set next tick NRF_RTC0->CC[1] = next_tick; if (++num_invoked == 3) { nrf_gpio_pin_clear(28); NVIC_SystemReset(); } } }
If using the COMPARE0 event instead of COMPARE1 or COMPARE2, the LFCLKSTAT register can be evaluated just fine after a reset. Also, when setting a debugger breakpoint *before* the clock initialisation, the register seems to be evaluated just fine. But an artificial delay in software does not seem to do the trick.
I've also attached the complete example in a zip file:
I think, this is actually related to this unresolved issue I had a few months back: https://devzone.nordicsemi.com/f/nordic-q-a/27195/lfclk-related-issues-in-sdk14-dfu