nrfx_err_t nrfx_rtc_cc_set(nrfx_rtc_t const * const p_instance, uint32_t channel, uint32_t val, bool enable_irq) { NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED); NRFX_ASSERT(channel<p_instance->cc_channel_count); nrfx_err_t err_code; uint32_t int_mask = RTC_CHANNEL_INT_MASK(channel); nrf_rtc_event_t event = RTC_CHANNEL_EVENT_ADDR(channel); nrf_rtc_event_disable(p_instance->p_reg, int_mask); nrf_rtc_int_disable(p_instance->p_reg, int_mask); val = RTC_WRAP(val); if (m_cb[p_instance->instance_id].reliable) { nrf_rtc_cc_set(p_instance->p_reg,channel,val); uint32_t cnt = nrf_rtc_counter_get(p_instance->p_reg); int32_t diff = cnt - val; if (cnt < val) { diff += RTC_COUNTER_COUNTER_Msk; } if (diff < m_cb[p_instance->instance_id].tick_latency) { err_code = NRFX_ERROR_TIMEOUT; NRFX_LOG_WARNING("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); return err_code; } } else { nrf_rtc_cc_set(p_instance->p_reg,channel,val); } if (enable_irq) { nrf_rtc_event_clear(p_instance->p_reg,event); nrf_rtc_int_enable(p_instance->p_reg, int_mask); } nrf_rtc_event_enable(p_instance->p_reg,int_mask); NRFX_LOG_INFO("RTC id: %d, channel enabled: %lu, compare value: %lu.", p_instance->instance_id, channel, val); err_code = NRFX_SUCCESS; NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); return err_code; }
int z_clock_driver_init(struct device *device) { struct device *clock; ARG_UNUSED(device); clock = device_get_binding(CONFIG_CLOCK_CONTROL_NRF_K32SRC_DRV_NAME); if (!clock) { return -1; } clock_control_on(clock, (void *)CLOCK_CONTROL_NRF_K32SRC); /* TODO: replace with counter driver to access RTC */ nrf_rtc_prescaler_set(RTC, 0); nrf_rtc_cc_set(RTC, 0, CYC_PER_TICK); nrf_rtc_event_enable(RTC, RTC_EVTENSET_COMPARE0_Msk); nrf_rtc_int_enable(RTC, RTC_INTENSET_COMPARE0_Msk); /* Clear the event flag and possible pending interrupt */ nrf_rtc_event_clear(RTC, NRF_RTC_EVENT_COMPARE_0); NVIC_ClearPendingIRQ(RTC1_IRQn); IRQ_CONNECT(RTC1_IRQn, 1, rtc1_nrf_isr, 0, 0); irq_enable(RTC1_IRQn); nrf_rtc_task_trigger(RTC, NRF_RTC_TASK_CLEAR); nrf_rtc_task_trigger(RTC, NRF_RTC_TASK_START); if (!IS_ENABLED(TICKLESS_KERNEL)) { set_comparator(counter() + CYC_PER_TICK); } return 0; }
static void AlarmStartAt(uint32_t aTargetTime, AlarmIndex aIndex) { uint32_t targetCounter; uint32_t now; nrf_rtc_int_disable(RTC_INSTANCE, sChannelData[aIndex].mCompareInt); nrf_rtc_event_enable(RTC_INSTANCE, sChannelData[aIndex].mCompareEventMask); sTimerData[aIndex].mTargetTime = aTargetTime; targetCounter = TimeToTicks(sTimerData[aIndex].mTargetTime, aIndex); nrf_rtc_cc_set(RTC_INSTANCE, sChannelData[aIndex].mChannelNumber, targetCounter); now = AlarmGetCurrentTimeRtcProtected(aIndex); if (AlarmShallStrike(now, aIndex)) { HandleCompareMatch(aIndex, true); } else { nrf_rtc_int_enable(RTC_INSTANCE, sChannelData[aIndex].mCompareInt); } }
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { TickType_t wakeupTime; /* Make sure the SysTick reload value does not overflow the counter. */ if( xExpectedIdleTime > portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { xExpectedIdleTime = portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP; } /* Block the scheduler now */ portDISABLE_INTERRUPTS(); /* Stop tick events */ nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); /* Configure CTC interrupt */ wakeupTime = nrf_rtc_counter_get(portNRF_RTC_REG) + xExpectedIdleTime; wakeupTime &= portNRF_RTC_MAXTICKS; nrf_rtc_cc_set(portNRF_RTC_REG, 0, wakeupTime); nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0); nrf_rtc_int_enable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); if( eTaskConfirmSleepModeStatus() == eAbortSleep ) { portENABLE_INTERRUPTS(); } else { TickType_t xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); if( xModifiableIdleTime > 0 ) { __DSB(); #ifdef SOFTDEVICE_PRESENT /* With SD there is no problem with possibility of interrupt lost. * every interrupt is counted and the counter is processed inside * sd_app_evt_wait function. */ portENABLE_INTERRUPTS(); sd_app_evt_wait(); #else /* No SD - we would just block interrupts globally. * BASEPRI cannot be used for that because it would prevent WFE from wake up. */ __disable_irq(); portENABLE_INTERRUPTS(); do{ __WFE(); } while(0 == (NVIC->ISPR[0] | NVIC->ISPR[1])); __enable_irq(); #endif } configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); portENABLE_INTERRUPTS(); } // We can do operations below safely, because when we are inside vPortSuppressTicksAndSleep // scheduler is already suspended. nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); nrf_rtc_int_enable (portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); }
ret_code_t nrf_drv_rtc_cc_set(nrf_drv_rtc_t const * const p_instance, uint32_t channel, uint32_t val, bool enable_irq) { ASSERT(m_cb[p_instance->instance_id].state != NRF_DRV_STATE_UNINITIALIZED); ASSERT(channel<p_instance->cc_channel_count); uint32_t int_mask = RTC_CHANNEL_INT_MASK(channel); nrf_rtc_event_t event = RTC_CHANNEL_EVENT_ADDR(channel); nrf_rtc_event_disable(p_instance->p_reg, int_mask); nrf_rtc_int_disable(p_instance->p_reg, int_mask); val = RTC_WRAP(val); if (m_cb[p_instance->instance_id].reliable) { nrf_rtc_cc_set(p_instance->p_reg,channel,val); uint32_t cnt = nrf_rtc_counter_get(p_instance->p_reg); int32_t diff = cnt - val; if (cnt < val) { diff += RTC_COUNTER_COUNTER_Msk; } if (diff < m_cb[p_instance->instance_id].tick_latency) { return NRF_ERROR_TIMEOUT; } } else { nrf_rtc_cc_set(p_instance->p_reg,channel,val); } if (enable_irq) { nrf_rtc_event_clear(p_instance->p_reg,event); nrf_rtc_int_enable(p_instance->p_reg, int_mask); } nrf_rtc_event_enable(p_instance->p_reg,int_mask); return NRF_SUCCESS; }
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { TickType_t wakeupTime; /* Make sure the SysTick reload value does not overflow the counter. */ if( xExpectedIdleTime > portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { xExpectedIdleTime = portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP; } /* Block the scheduler now */ portDISABLE_INTERRUPTS(); /* Stop tick events */ nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); /* Configure CTC interrupt */ wakeupTime = nrf_rtc_counter_get(portNRF_RTC_REG) + xExpectedIdleTime; wakeupTime &= portNRF_RTC_MAXTICKS; nrf_rtc_cc_set(portNRF_RTC_REG, 0, wakeupTime); nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0); nrf_rtc_int_enable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); if( eTaskConfirmSleepModeStatus() == eAbortSleep ) { portENABLE_INTERRUPTS(); } else { TickType_t xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); if( xModifiableIdleTime > 0 ) { __DSB(); do{ __WFE(); } while(0 == (NVIC->ISPR[0])); } configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); portENABLE_INTERRUPTS(); } // We can do operations below safely, because when we are inside vPortSuppressTicksAndSleep // scheduler is already suspended. nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); nrf_rtc_int_enable (portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); }
void common_rtc_set_interrupt(uint32_t us_timestamp, uint32_t cc_channel, uint32_t int_mask) { // The internal counter is clocked with a frequency that cannot be easily // multiplied to 1 MHz, therefore besides the translation of values // (microsecond <-> ticks) a special care of overflows handling must be // taken. Here the 32-bit timestamp value is complemented with information // about current the system up time of (ticks + number of overflows of tick // counter on upper bits, converted to microseconds), and such 64-bit value // is then translated to counter ticks. Finally, the lower 24 bits of thus // calculated value is written to the counter compare register to prepare // the interrupt generation. uint64_t current_time64 = common_rtc_64bit_us_get(); // [add upper 32 bits from the current time to the timestamp value] uint64_t timestamp64 = us_timestamp + (current_time64 & ~(uint64_t)0xFFFFFFFF); // [if the original timestamp value happens to be after the 32 bit counter // of microsends overflows, correct the upper 32 bits accordingly] if (us_timestamp < (uint32_t)(current_time64 & 0xFFFFFFFF)) { timestamp64 += ((uint64_t)1 << 32); } // [microseconds -> ticks, always round the result up to avoid too early // interrupt generation] uint32_t compare_value = (uint32_t)CEIL_DIV((timestamp64) * RTC_INPUT_FREQ, 1000000); // The COMPARE event occurs when the value in compare register is N and // the counter value changes from N-1 to N. Therefore, the minimal safe // difference between the compare value to be set and the current counter // value is 2 ticks. This guarantees that the compare trigger is properly // setup before the compare condition occurs. uint32_t closest_safe_compare = common_rtc_32bit_ticks_get() + 2; if ((int)(compare_value - closest_safe_compare) <= 0) { compare_value = closest_safe_compare; } nrf_rtc_cc_set(COMMON_RTC_INSTANCE, cc_channel, RTC_WRAP(compare_value)); nrf_rtc_event_enable(COMMON_RTC_INSTANCE, int_mask); }
int _sys_clock_driver_init(struct device *device) { struct device *clock; ARG_UNUSED(device); clock = device_get_binding(CONFIG_CLOCK_CONTROL_NRF5_K32SRC_DRV_NAME); if (!clock) { return -1; } clock_control_on(clock, (void *)CLOCK_CONTROL_NRF5_K32SRC); rtc_past = 0; #ifdef CONFIG_TICKLESS_IDLE expected_sys_ticks = 1; #endif /* CONFIG_TICKLESS_IDLE */ /* TODO: replace with counter driver to access RTC */ SYS_CLOCK_RTC->PRESCALER = 0; nrf_rtc_cc_set(SYS_CLOCK_RTC, RTC_CC_IDX, sys_clock_hw_cycles_per_tick); nrf_rtc_event_enable(SYS_CLOCK_RTC, RTC_EVTENSET_COMPARE0_Msk); nrf_rtc_int_enable(SYS_CLOCK_RTC, RTC_INTENSET_COMPARE0_Msk); /* Clear the event flag and possible pending interrupt */ RTC_CC_EVENT = 0; NVIC_ClearPendingIRQ(NRF5_IRQ_RTC1_IRQn); IRQ_CONNECT(NRF5_IRQ_RTC1_IRQn, 1, rtc1_nrf5_isr, 0, 0); irq_enable(NRF5_IRQ_RTC1_IRQn); nrf_rtc_task_trigger(SYS_CLOCK_RTC, NRF_RTC_TASK_CLEAR); nrf_rtc_task_trigger(SYS_CLOCK_RTC, NRF_RTC_TASK_START); return 0; }
/* * Set RTC Counter Compare (CC) register to a given value in RTC ticks. */ static void rtc_compare_set(u32_t rtc_ticks) { u32_t rtc_now; /* Try to set CC value. We assume the procedure is always successful. */ nrf_rtc_cc_set(SYS_CLOCK_RTC, RTC_CC_IDX, rtc_ticks); rtc_now = RTC_COUNTER; /* The following checks if the CC register was set to a valid value. * The first test checks if the distance between the current RTC counter * and the value (in the future) set in the CC register is too small to * guarantee a compare event being triggered. * The second test checks if the current RTC counter is higher than the * value written to the CC register, i.e. the CC value is in the past, * by checking if the unsigned subtraction wraps around. * If either of the above are true then instead of waiting for the CC * event to trigger in the form of an interrupt, trigger it directly * using the NVIC. */ if ((((rtc_ticks - rtc_now) & RTC_MASK) < RTC_MIN_DELTA) || (((rtc_ticks - rtc_now) & RTC_MASK) > RTC_HALF)) { NVIC_SetPendingIRQ(NRF5_IRQ_RTC1_IRQn); } }
static inline void set_comparator(u32_t cyc) { nrf_rtc_cc_set(RTC, 0, cyc); }
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { /* * Implementation note: * * To help debugging the option configUSE_TICKLESS_IDLE_SIMPLE_DEBUG was presented. * This option would make sure that even if program execution was stopped inside * this function no more than expected number of ticks would be skipped. * * Normally RTC works all the time even if firmware execution was stopped * and that may lead to skipping too much of ticks. */ TickType_t enterTime; /* Make sure the SysTick reload value does not overflow the counter. */ if ( xExpectedIdleTime > portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { xExpectedIdleTime = portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP; } /* Block the scheduler now */ portDISABLE_INTERRUPTS(); /* Configure CTC interrupt */ enterTime = nrf_rtc_counter_get(portNRF_RTC_REG); if ( eTaskConfirmSleepModeStatus() == eAbortSleep ) { portENABLE_INTERRUPTS(); } else { TickType_t xModifiableIdleTime; TickType_t wakeupTime = (enterTime + xExpectedIdleTime) & portNRF_RTC_MAXTICKS; /* Stop tick events */ nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); /* Configure CTC interrupt */ nrf_rtc_cc_set(portNRF_RTC_REG, 0, wakeupTime); nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0); nrf_rtc_int_enable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); __DSB(); /* 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 ) { #ifdef SOFTDEVICE_PRESENT sd_app_evt_wait(); #else do{ __WFE(); } while (0 == (NVIC->ISPR[0])); #endif } configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); portENABLE_INTERRUPTS(); /* Correct the system ticks */ portENTER_CRITICAL(); { TickType_t diff; TickType_t hwTicks = nrf_rtc_counter_get(portNRF_RTC_REG); nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_TICK); nrf_rtc_int_enable (portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); if(enterTime > hwTicks) { hwTicks += portNRF_RTC_MAXTICKS + 1U; } diff = (hwTicks - enterTime); if((configUSE_TICKLESS_IDLE_SIMPLE_DEBUG) && (diff > xExpectedIdleTime)) { diff = xExpectedIdleTime; } if (diff > 0) { vTaskStepTick(diff); } } portEXIT_CRITICAL(); } }