/**************************************************************************//** * @brief BURTC_IRQHandler * Interrupt Service Routine for RTC which is used as system tick counter in EM3 *****************************************************************************/ void BURTC_IRQHandler(void) { /* If using preemption, also force a context switch. */ #if (configUSE_PREEMPTION == 1) port_NVIC_INT_CTRL_REG = port_NVIC_PENDSVSET_BIT; #endif /* (configUSE_PREEMPTION == 1) */ /* Set BURTC interrupt to one system tick period*/ BURTC_Enable(false); BURTC_CompareSet(0, ulTimerReloadValueForOneTick); /* Restart the counter */ BURTC_CounterReset(); #if (configUSE_TICKLESS_IDLE == 1) /* Set flag that interrupt was made*/ intTickFlag = true; #endif /* (configUSE_TICKLESS_IDLE == 1) */ /* Critical section which protect incrementing the tick*/ ( void ) portSET_INTERRUPT_MASK_FROM_ISR(); { xTaskIncrementTick(); } portCLEAR_INTERRUPT_MASK_FROM_ISR(0); /* Clear interrupt */ BURTC_IntClear(_RTC_IFC_MASK); BURTC_CounterReset(); }
/***************************************************************************//** * @brief Initialize BURTC * * @details * Configures the BURTC peripheral. * * @note * Before initialization, BURTC module must first be enabled by clearing the * reset bit in the RMU, i.e. * @verbatim * RMU_ResetControl(rmuResetBU, rmuResetModeClear); * @endverbatim * Compare channel 0 must be configured outside this function, before * initialization if enable is set to true. The counter will always be reset. * * @param[in] burtcInit * Pointer to BURTC initialization structure ******************************************************************************/ void BURTC_Init(const BURTC_Init_TypeDef *burtcInit) { uint32_t ctrl; uint32_t presc; /* Check initializer structure integrity */ EFM_ASSERT(burtcInit != (BURTC_Init_TypeDef *) 0); /* Clock divider must be between 1 and 128, really on the form 2^n */ EFM_ASSERT((burtcInit->clkDiv >= 1) && (burtcInit->clkDiv <= 128)); /* Ignored compare bits during low power operation must be less than 7 */ /* Note! Giant Gecko revision C errata, do NOT use LPCOMP=7 */ EFM_ASSERT(burtcInit->lowPowerComp <= 6); /* You cannot enable the BURTC if mode is set to disabled */ EFM_ASSERT((burtcInit->enable == false) || ((burtcInit->enable == true) && (burtcInit->mode != burtcModeDisable))); /* Low power mode is only available with LFRCO or LFXO as clock source */ EFM_ASSERT((burtcInit->clkSel != burtcClkSelULFRCO) || ((burtcInit->clkSel == burtcClkSelULFRCO) && (burtcInit->lowPowerMode == burtcLPDisable))); /* Calculate prescaler value from clock divider input */ /* Note! If clock select (clkSel) is ULFRCO, a clock divisor (clkDiv) of value 1 will select a 2kHz ULFRCO clock, while any other value will select a 1kHz ULFRCO clock source. */ presc = divToLog2(burtcInit->clkDiv); /* Make sure all registers are updated simultaneously */ if (burtcInit->enable) { BURTC_FreezeEnable(true); } /* Modification of LPMODE register requires sync with potential ongoing * register updates in LF domain. */ regSync(BURTC_SYNCBUSY_LPMODE); /* Configure low power mode */ BURTC->LPMODE = (uint32_t) (burtcInit->lowPowerMode); /* New configuration */ ctrl = (BURTC_CTRL_RSTEN | (burtcInit->mode) | (burtcInit->debugRun << _BURTC_CTRL_DEBUGRUN_SHIFT) | (burtcInit->compare0Top << _BURTC_CTRL_COMP0TOP_SHIFT) | (burtcInit->lowPowerComp << _BURTC_CTRL_LPCOMP_SHIFT) | (presc << _BURTC_CTRL_PRESC_SHIFT) | (burtcInit->clkSel) | (burtcInit->timeStamp << _BURTC_CTRL_BUMODETSEN_SHIFT)); /* Clear interrupts */ BURTC_IntClear(0xFFFFFFFF); /* Set new configuration */ BURTC->CTRL = ctrl; /* Enable BURTC and counter */ if (burtcInit->enable) { /* To enable BURTC counter, we need to disable reset */ BURTC_Enable(true); /* Clear freeze */ BURTC_FreezeEnable(false); } }
/**************************************************************************//** * @brief vPortSetupTimerInterrupt * Override the default definition of vPortSuppressTicksAndSleep() that is weakly * defined in the FreeRTOS Cortex-M3 port layer layer *****************************************************************************/ void vPortSuppressTicksAndSleep(portTickType xExpectedIdleTime) { unsigned long ulReloadValue, ulCompleteTickPeriods; portTickType xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ if (xExpectedIdleTime > xMaximumPossibleSuppressedTicks) { xExpectedIdleTime = xMaximumPossibleSuppressedTicks; } /* Calculate the reload value required to wait xExpectedIdleTime * tick periods. -1 is used because this code will execute part way * through one of the tick periods, and the fraction of a tick period is * accounted for later. */ ulReloadValue = (ulTimerReloadValueForOneTick * (xExpectedIdleTime )); if (ulReloadValue > ulStoppedTimerCompensation) { ulReloadValue -= ulStoppedTimerCompensation; } /* Stop the SysTick momentarily. The time the SysTick is stopped for * is accounted for as best it can be, but using the tickless mode will * inevitably result in some tiny drift of the time maintained by the * kernel with respect to calendar time. */ /* Stop the RTC clock*/ BURTC_Enable(false); /* Enter a critical section but don't use the taskENTER_CRITICAL() * method as that will mask interrupts that should exit sleep mode. */ INT_Disable(); /* The tick flag is set to false before sleeping. If it is true when sleep * mode is exited then sleep mode was probably exited because the tick was * suppressed for the entire xExpectedIdleTime period. */ intTickFlag = false; /* If a context switch is pending or a task is waiting for the scheduler * to be unsuspended then abandon the low power entry. */ if (eTaskConfirmSleepModeStatus() == eAbortSleep) { BURTC_Enable(true); /* Re-enable interrupts */ INT_Enable(); } else { /* Set the new reload value. */ ulReloadValue -= BURTC_CounterGet(); BURTC_CompareSet(0, ulReloadValue); /* Restart the counter*/ BURTC_CounterReset(); /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can * set its parameter to 0 to indicate that its implementation contains * its own wait for interrupt or wait for event instruction, and so wfi * should not be executed again. However, the original expected idle * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING(xModifiableIdleTime); if (xModifiableIdleTime > 0) { SLEEP_Sleep(); __DSB(); __ISB(); } configPOST_SLEEP_PROCESSING(xExpectedIdleTime); /* Stop SysTick. Again, the time the SysTick is stopped for is * accounted for as best it can be, but using the tickless mode will * inevitably result in some tiny drift of the time maintained by the * kernel with respect to calendar time. */ BURTC_Enable(false); /* Re-enable interrupts - see comments above __disable_interrupt() * call above. */ INT_Enable(); if (intTickFlag != false) { /* The tick interrupt has already executed, * Reset the alarm value with whatever remains of this tick period. */ BURTC_CompareSet(0, TIMER_CAPACITY & (ulTimerReloadValueForOneTick - BURTC_CounterGet())); /* The tick interrupt handler will already have pended the tick * processing in the kernel. As the pending tick will be * processed as soon as this function exits, the tick value * maintained by the tick is stepped forward by one less than the * time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { /* Some other interrupt than system tick ended the sleep. * Calculate how many tick periods passed while the processor * was waiting */ ulCompleteTickPeriods = BURTC_CounterGet() / ulTimerReloadValueForOneTick; /* The reload value is set to whatever fraction of a single tick * period remains. */ if (ulCompleteTickPeriods == 0) { ulReloadValue = ulTimerReloadValueForOneTick - BURTC_CounterGet(); } else { ulReloadValue = BURTC_CounterGet() - (ulCompleteTickPeriods * ulTimerReloadValueForOneTick); } BURTC_CompareSet(0, ulReloadValue); } /* Restart the RTCounter*/ BURTC_CounterReset(); /* The tick forward by the number of tick periods that * remained in a low power state. */ vTaskStepTick(ulCompleteTickPeriods); } }
void main () { uint32_t lfa_Hz; uint16_t reset_cause; int sleep_mode = 0; uint32_t burtc_ctrl; CHIP_Init(); reset_cause = RMU->RSTCAUSE; RMU_ResetCauseClear(); #if ! defined(_EFM32_ZERO_FAMILY) BSPACM_CORE_BITBAND_PERIPH(RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT) = 0; #endif SystemCoreClockUpdate(); vBSPACMledConfigure(); setvbuf(stdout, NULL, _IONBF, 0); BSPACM_CORE_ENABLE_INTERRUPT(); printf("\n" __DATE__ " " __TIME__ "\n"); printf("System clock %lu Hz\n", SystemCoreClock); { int i = sizeof(xResetCause)/sizeof(*xResetCause); printf("Reset cause [%04x]:", reset_cause); while (0 <= --i) { const sResetCause * const rcp = xResetCause + i; if (rcp->value == (reset_cause & rcp->mask)) { printf(" %s", rcp->name); } } printf("\nRMU CTRL %lx\n", RMU->CTRL); } /* Enable low-energy support. */ CMU_ClockEnable(cmuClock_CORELE, true); BURTC_Enable(true); if (MAGIC != retained_state->magic) { memset(retained_state, 0, sizeof(*retained_state)); retained_state->magic = MAGIC; printf("Resetting retained state\n"); } ++retained_state->boots; printf("Boot count %lu\n", retained_state->boots); printf("BURTC clock source: "); #if (WITH_ULFRCO - 0) /* Use ULFRCO, which enables EM4 wakeup but is pretty inaccurate. */ printf("ULFRCO\n"); /* NB: DIV2 means 1 kHz instead of 2 kHz */ burtc_ctrl = BURTC_CTRL_CLKSEL_ULFRCO | BURTC_CTRL_PRESC_DIV2; CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_ULFRCO); lfa_Hz = CMU_ClockFreqGet(cmuClock_LFA); { EMU_EM4Init_TypeDef cfg = { .lockConfig = 1, .osc = EMU_EM4CONF_OSC_ULFRCO, .buRtcWakeup = 1, .vreg = 1, }; EMU_EM4Init(&cfg); } #elif (WITH_LFRCO - 0) printf("LFRCO/32\n"); CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO); burtc_ctrl = BURTC_CTRL_CLKSEL_LFRCO | BURTC_CTRL_PRESC_DIV32; lfa_Hz = CMU_ClockFreqGet(cmuClock_LFA) / 32; #else printf("LFXO/32\n"); CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO); burtc_ctrl = BURTC_CTRL_CLKSEL_LFXO | BURTC_CTRL_PRESC_DIV32; lfa_Hz = CMU_ClockFreqGet(cmuClock_LFA) / 32; #endif /* LFA source */ printf("LFA clock at %lu Hz ; wake every %u seconds\n", lfa_Hz, WAKE_INTERVAL_S); /* Initialize BURTC. */ if (! (RMU_RSTCAUSE_EM4WURST & reset_cause)) { printf("Initializing BURTC\n"); BURTC->FREEZE = BURTC_FREEZE_REGFREEZE; BURTC->LPMODE = BURTC_LPMODE_LPMODE_DISABLE; BURTC->CTRL = burtc_ctrl /* CLKSEL + PRESC */ | BURTC_CTRL_RSTEN | BURTC_CTRL_MODE_EM4EN ; BURTC->COMP0 = WAKE_INTERVAL_S * lfa_Hz; BURTC->IEN = BURTC_IF_COMP0; BURTC->CTRL &= ~BURTC_CTRL_RSTEN; BURTC->FREEZE = 0; } else { while (BURTC_SYNCBUSY_COMP0 & BURTC->SYNCBUSY) { } BURTC->COMP0 += WAKE_INTERVAL_S * lfa_Hz; } BURTC->IFC = BURTC_IFC_COMP0; NVIC_EnableIRQ(BURTC_IRQn); printf("BURTC CTRL %lx IEN %lx\n", BURTC->CTRL, BURTC->IEN); (void)iBSPACMperiphUARTflush(hBSPACMdefaultUART, eBSPACMperiphUARTfifoState_TX); while (1) { static const sBSPACMperiphUARTconfiguration cfg = { .speed_baud = 0 }; printf("Sleeping in mode %u, %lu to %lu rtc_if %x or %lx\n", sleep_mode, BURTC->CNT, BURTC->COMP0, burtc_if, BURTC->IF); BSPACM_CORE_DISABLE_INTERRUPT(); do { (void)iBSPACMperiphUARTflush(hBSPACMdefaultUART, eBSPACMperiphUARTfifoState_TX); hBSPACMperiphUARTconfigure(hBSPACMdefaultUART, 0); switch (sleep_mode) { case 0: while (! (BURTC_IF_COMP0 & BURTC->IF)) { } ++sleep_mode; break; case 1: EMU_EnterEM1(); ++sleep_mode; break; case 2: EMU_EnterEM2(true); SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; if (cmuSelect_ULFRCO == CMU_ClockSelectGet(cmuClock_LFA)) { ++sleep_mode; } else { sleep_mode = 0; } break; case 3: EMU_EnterEM3(true); SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; if (cmuSelect_ULFRCO == CMU_ClockSelectGet(cmuClock_LFA)) { ++sleep_mode; } else { sleep_mode = 0; } break; case 4: EMU_EnterEM4(); sleep_mode = 0; break; } } while (0); BSPACM_CORE_ENABLE_INTERRUPT(); hBSPACMperiphUARTconfigure(hBSPACMdefaultUART, &cfg); while (BURTC_SYNCBUSY_COMP0 & BURTC->SYNCBUSY) { } BURTC->COMP0 += WAKE_INTERVAL_S * lfa_Hz; /* Giant Gecko * Source EM0 EM1 EM2 EM3 EM4 * ULFRCO 2.5m 1.1m 622n 622n 450n */ } }