/**************************************************************************//** * @brief RTC_IRQHandler * Interrupt Service Routine for RTC which is used as system tick counter in EM2 *****************************************************************************/ void RTC_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 RTC interrupt to one system tick period*/ RTC_Enable(false); RTC_CompareSet(0, ulTimerReloadValueForOneTick); /* Restart the counter */ #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 */ RTC_IntClear(_RTC_IFC_MASK); RTC_CounterReset(); }
/*---------------------------------------------------------------------------*/ void RTC_IRQHandler(void) { // Find reason of IRQ if(RTC_IntGet() & RTC_IF_COMP0) { // Update second counters //_u32_seconds_since_epoch += DAY_VALUE_IN_SEC; //_u32_uptime += DAY_VALUE_IN_SEC; // Update alarm (alarm or second values may be updated) // Get about 100us to take into account Interrupt scheduling time //SI32_RTC_0->ALARM0.U32 = (_u32_alarm0_secvalue * _rtc_second) - 2; // Reset Counter RTC_CounterReset(); } else if(RTC_IntGet() & RTC_IF_COMP1) { if(alarm_callback != NULL) alarm_callback(); // Disable handler alarm_callback = NULL; } else { printf("%s: unknown reason for RTC interrupt\r\n",__func__); } // Clear interrupts RTC_IntClear(_RTC_IF_MASK); }
/**************************************************************************//** * @brief Enables LFACLK and selects LFXO as clock source for RTC * Sets up the RTC to generate an interrupt every second. *****************************************************************************/ static void rtcSetup(unsigned int frequency) { RTC_Init_TypeDef rtcInit = RTC_INIT_DEFAULT; palClockSetup(cmuClock_LFA); /* Set the prescaler. */ CMU_ClockDivSet( cmuClock_RTC, cmuClkDiv_2 ); /* Enable RTC clock */ CMU_ClockEnable(cmuClock_RTC, true); /* Initialize RTC */ rtcInit.enable = false; /* Do not start RTC after initialization is complete. */ rtcInit.debugRun = false; /* Halt RTC when debugging. */ rtcInit.comp0Top = true; /* Wrap around on COMP0 match. */ RTC_Init(&rtcInit); /* Interrupt at given frequency. */ RTC_CompareSet(0, ((CMU_ClockFreqGet(cmuClock_RTC) / frequency) - 1) & _RTC_COMP0_MASK ); #ifndef INCLUDE_PAL_GPIO_PIN_AUTO_TOGGLE_HW_ONLY /* Enable interrupt */ NVIC_EnableIRQ(RTC_IRQn); RTC_IntEnable(RTC_IEN_COMP0); #endif RTC_CounterReset(); /* Start Counter */ RTC_Enable(true); }
// Stores the hours/mins for clock time, start time, or stop time void set_clock_time(int index, uint16_t hours, uint16_t minutes) { // Set the time clock if (index == 0) { // Midnight is time zero RTC_CounterReset(); // 3600 seconds per hour, 60 seconds per minute RTC->CNT = hours * 3600 + minutes * 60; } else if (index == 1) { // Add 1 second so that RTC interrupt source is clear, // and not to confuse with the clock minute updates time_keeper.timer_start_seconds = hours * 3600 + minutes * 60 + 1; // Set up the RTC to trigger a start event RTC_CompareSet(0, time_keeper.timer_start_seconds); // Save for next program event start_hours = hours; start_minutes = minutes; } else if (index == 2) { // Add 1 second so that RTC interrupt source is clear, // and not to confuse with the clock minute updates time_keeper.timer_stop_seconds = hours * 3600 + minutes * 60 + 1; // Save for next program event stop_hours = hours; stop_minutes = minutes; } }
void os_idle_demon(void) { RTC_Init_TypeDef init; unsigned int sleep; /* The idle demon is a system thread, running when no other thread is */ /* ready to run. */ /* Enable system clock for RTC */ /* LFXO setup */ /* Use 70% boost */ CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_LFXOBOOST_MASK) | CMU_CTRL_LFXOBOOST_70PCENT; /* Ensure LE modules are accessible */ CMU_ClockEnable(cmuClock_CORELE, true); /* Enable osc as LFACLK in CMU (will also enable oscillator if not enabled) */ CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO); /* Use a 32 division prescaler to reduce power consumption. */ CMU_ClockDivSet(cmuClock_RTC, cmuClkDiv_32); /* Enable clock to RTC module */ CMU_ClockEnable(cmuClock_RTC, true); init.enable = false; init.debugRun = false; init.comp0Top = false; /* Count to max value before wrapping */ RTC_Init(&init); /* Disable interrupt generation from RTC0 */ RTC_IntDisable(_RTC_IF_MASK); /* Enable interrupts */ NVIC_ClearPendingIRQ(RTC_IRQn); NVIC_EnableIRQ(RTC_IRQn); for (;;) { /* os_suspend stops scheduler and returns time to next event in OS_TICK units */ sleep = os_suspend(); if (sleep) { RTC_CompareSet(0, sleep - 1); RTC_IntClear(RTC_IFC_COMP0); RTC_IntEnable(RTC_IF_COMP0); RTC_CounterReset(); /* Enter EM2 low power mode - could be replaced with EM1 if required */ EMU_EnterEM2(true); /* get information how long we were in sleep */ sleep = RTC_CounterGet(); RTC_Enable(false); }; /* resume scheduler providing information how long MCU was sleeping */ os_resume(sleep); } }
error_t hw_timer_counter_reset(hwtimer_id_t timer_id) { if(timer_id >= HWTIMER_NUM) return ESIZE; if(!timer_inited) return EOFF; start_atomic(); RTC_IntDisable(RTC_IEN_COMP0 | RTC_IEN_COMP1); RTC_IntClear(RTC_IEN_COMP0 | RTC_IEN_COMP1); RTC_CounterReset(); RTC_IntEnable(RTC_IEN_COMP0); end_atomic(); }
/**************************************************************************//** * @brief Enables LFACLK and selects LFXO as clock source for RTC. * Sets up the RTC to count at 1024 Hz. * The counter should not be cleared on a compare match and keep running. * Interrupts should be cleared and enabled. * The counter should run. *****************************************************************************/ error_t hw_timer_init(hwtimer_id_t timer_id, uint8_t frequency, timer_callback_t compare_callback, timer_callback_t overflow_callback) { if(timer_id >= HWTIMER_NUM) return ESIZE; if(timer_inited) return EALREADY; if(frequency != HWTIMER_FREQ_1MS && frequency != HWTIMER_FREQ_32K) return EINVAL; start_atomic(); compare_f = compare_callback; overflow_f = overflow_callback; timer_inited = true; /* Configuring clocks in the Clock Management Unit (CMU) */ startLfxoForRtc(frequency); RTC_Init_TypeDef rtcInit = RTC_INIT_DEFAULT; rtcInit.enable = false; /* Don't enable RTC after init has run */ rtcInit.comp0Top = true; /* Clear counter on compare 0 match: cmp 0 is used to limit the value of the rtc to 0xffff */ rtcInit.debugRun = false; /* Counter shall not keep running during debug halt. */ /* Initialize the RTC */ RTC_Init(&rtcInit); //disable all rtc interrupts while we're still configuring RTC_IntDisable(RTC_IEN_OF | RTC_IEN_COMP0 | RTC_IEN_COMP1); RTC_IntClear(RTC_IFC_OF | RTC_IFC_COMP0 | RTC_IFC_COMP1); //Set maximum value for the RTC RTC_CompareSet( 0, 0x0000FFFF ); RTC_CounterReset(); RTC_IntEnable(RTC_IEN_COMP0); NVIC_EnableIRQ(RTC_IRQn); RTC_Enable(true); end_atomic(); return SUCCESS; }
/**************************************************************************//** * @brief vPortSuppressTicksAndSleep * 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; unsigned int ulRemainingCounter; 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. */ ulReloadValue = (ulTimerReloadValueForOneTick * (xExpectedIdleTime )); if (ulReloadValue > ulStoppedTimerCompensation) { ulReloadValue -= ulStoppedTimerCompensation; } /* Stop the System Tick momentarily. The time the System Tick 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*/ RTC_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) { RTC_Enable(true); /* Re-enable interrupts - see comments above __disable_interrupt() * call above. */ INT_Enable(); } else { /* Set the new reload value. */ ulReloadValue -= RTC_CounterGet(); RTC_CompareSet(0, ulReloadValue); /* Restart the counter*/ RTC_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. */ /* Store current counter value */ ulRemainingCounter = RTC_CounterGet(); /* Stop the RTC clock*/ RTC_Enable(false); /* Re-enable interrupts */ INT_Enable(); if (intTickFlag != false) { /* The tick interrupt has already executed, * Reset the alarm value with whatever remains of this tick period. */ RTC_CompareSet(0, TIMER_CAPACITY & (ulTimerReloadValueForOneTick - RTC_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 = ulRemainingCounter / ulTimerReloadValueForOneTick; /* The reload value is set to whatever fraction of a single tick * period remains. */ if (ulCompleteTickPeriods == 0) { ulReloadValue = ulTimerReloadValueForOneTick - ulRemainingCounter; } else { ulReloadValue = ulRemainingCounter - (ulCompleteTickPeriods * ulTimerReloadValueForOneTick); } RTC_CompareSet(0, ulReloadValue); } /* Restart the RTCounter */ RTC_CounterReset(); /* The tick forward by the number of tick periods that * remained in a low power state. */ vTaskStepTick(ulCompleteTickPeriods); } }
const hwtimer_info_t* hw_timer_get_info(hwtimer_id_t timer_id) { if(timer_id >= HWTIMER_NUM) return NULL; static const hwtimer_info_t timer_info = { .min_delay_ticks = 0, }; return &timer_info; } hwtimer_tick_t hw_timer_getvalue(hwtimer_id_t timer_id) { if(timer_id >= HWTIMER_NUM || (!timer_inited)) return 0; else { uint32_t value =(uint16_t)(RTC->CNT & 0xFFFF); return value; } } error_t hw_timer_schedule(hwtimer_id_t timer_id, hwtimer_tick_t tick ) { if(timer_id >= HWTIMER_NUM) return ESIZE; if(!timer_inited) return EOFF; start_atomic(); RTC_IntDisable(RTC_IEN_COMP1); RTC_CompareSet( 1, tick ); RTC_IntClear(RTC_IEN_COMP1); RTC_IntEnable(RTC_IEN_COMP1); end_atomic(); } error_t hw_timer_cancel(hwtimer_id_t timer_id) { if(timer_id >= HWTIMER_NUM) return ESIZE; if(!timer_inited) return EOFF; start_atomic(); RTC_IntDisable(RTC_IEN_COMP1); RTC_IntClear(RTC_IEN_COMP1); end_atomic(); } error_t hw_timer_counter_reset(hwtimer_id_t timer_id) { if(timer_id >= HWTIMER_NUM) return ESIZE; if(!timer_inited) return EOFF; start_atomic(); RTC_IntDisable(RTC_IEN_COMP0 | RTC_IEN_COMP1); RTC_IntClear(RTC_IEN_COMP0 | RTC_IEN_COMP1); RTC_CounterReset(); RTC_IntEnable(RTC_IEN_COMP0); end_atomic(); } bool hw_timer_is_overflow_pending(hwtimer_id_t timer_id) { if(timer_id >= HWTIMER_NUM) return false; start_atomic(); //COMP0 is used to limit thc RTC to 16 bits -> use this one to check bool is_pending = !!((RTC_IntGet() & RTC->IEN) & RTC_IFS_COMP0); end_atomic(); return is_pending; } bool hw_timer_is_interrupt_pending(hwtimer_id_t timer_id) { if(timer_id >= HWTIMER_NUM) return false; start_atomic(); bool is_pending = !!((RTC_IntGet() & RTC->IEN) & RTC_IFS_COMP1); end_atomic(); return is_pending; } INT_HANDLER(RTC_IRQHandler) { //retrieve flags. We 'OR' this with the enabled interrupts //since the COMP1 flag may be set if it wasn't used before (compare register == 0 -> ifs flag set regardless of whether interrupt is enabled) //by AND ing with the IEN we make sure we only consider the flags of the ENABLED interrupts uint32_t flags = (RTC_IntGet() & RTC->IEN); RTC_IntClear(RTC_IFC_OF | RTC_IFC_COMP0 | RTC_IFC_COMP1); //evaluate flags to see which one(s) fired: if((flags & RTC_IFS_COMP0) && (overflow_f != 0x0)) overflow_f(); if((flags & RTC_IFS_COMP1)) { RTC_IntDisable(RTC_IEN_COMP1); if(compare_f != 0x0) compare_f(); } }