Hi,
I am running nrf52840 on a custom board, SDK 15.0.0, softdevice S140 6.0.0 and SES.
In my application I am reading an IMU ( ICM20602) and a laser ranger/Time of Flight chip (VL53l0x). The IMU is read every 10 ms in a handler triggered by an apptimer. The TOF chip is read in a loop every 20ms controlled by RTC2. As soon as the app_timer is started I get high jitter on the ticks from RTC2. I am measuring this by toggling a GPIO pin and measuring with a CRO.
My RTC code is:
#include "nrfx_rtc.h" #include "nrfx_clock.h" #include "nrf_drv_rtc.h" #include "nrf_drv_clock.h" #include "boards.h" #include "pca10056.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #include "app_timer.h" #include "VL53L0X.h" #include "rtc.h" static uint32_t last_msg = 0; static bool is_uninit = true; /** @file rtc.c ** @brief real time counter code ** @details real time counter (rtc) provides a tick for timouts and also an interrupt to wake ** from sleep ** ** @defgroup RealTimeCounter Main_Clock_API ** @{ */ const nrfx_rtc_t rtc = NRFX_RTC_INSTANCE(2); /**< Declaring an instance of nrfx_rtc for RTC2. */ bool overflow_toggle = false; /** @brief interrupt handler for rtc ** @details two types of interrupt - overflow and wake up ** @param[in] int_type type of interrupt */ static void rtc_handler(nrfx_rtc_int_type_t int_type) { GPIO_debug_toggle(); if (int_type == NRFX_RTC_INT_OVERFLOW) { if (overflow_toggle) overflow_toggle = false; else overflow_toggle = true; } // NRF_LOG_DEBUG("RTC INT: 0x%x", int_type); } /** @brief nothing handled but required by the sdk */ static void clk_handler(nrfx_clock_evt_type_t evt) { NRF_LOG_INFO("CLOCK HANDLER"); NRF_LOG_FLUSH(); } /** @brief Function configuring gpio for pin toggling. */ static void leds_config(void) { bsp_board_init(BSP_INIT_LEDS); } /** @brief Function starting the internal LFCLK XTAL oscillator. */ static void lfclk_config(void) { // always started because we are running a softdevice which starts it // ret_code_t err_code = nrfx_clock_init(clk_handler); // APP_ERROR_CHECK(err_code); // nrfx_clock_lfclk_start(); } /** @brief Function initialization and configuration of RTC driver instance for ticks. */ void rtc_config_tick(void) { uint32_t err_code; if (!is_uninit) { nrfx_rtc_disable(&rtc); nrfx_rtc_uninit(&rtc); } // get the crystal buzzing /// The clock is also used by the softdevice. Depending on what else is started we may or may not have to start it if (!nrfx_clock_lfclk_is_running()) lfclk_config(); //Initialize RTC instance nrfx_rtc_config_t config = NRFX_RTC_DEFAULT_CONFIG; config.prescaler = RTC_FREQ_TO_PRESCALER(RTC_FREQ_TICK); err_code = nrfx_rtc_init(&rtc, &config, rtc_handler); APP_ERROR_CHECK(err_code); is_uninit = false; //Enable tick event & interrupt nrfx_rtc_tick_enable(&rtc,true); // enable over flow event nrfx_rtc_overflow_enable(&rtc, true); //set the counter to 0 nrfx_rtc_counter_clear(&rtc); //Power on RTC instance nrfx_rtc_enable(&rtc); } /** @brief configure the RTC for wakeup duties * */ void rtc_config_wake(void) { uint32_t err_code; if (!is_uninit) { nrfx_rtc_disable(&rtc); nrfx_rtc_uninit(&rtc); } // get the crystal buzzing /// The clock is also used by the softdevice. Depending on what else is started we may or may not have to start it if (!nrfx_clock_lfclk_is_running()) lfclk_config(); //Initialize RTC instance nrfx_rtc_config_t config = NRFX_RTC_DEFAULT_CONFIG; config.prescaler = RTC_FREQ_TO_PRESCALER(RTC_FREQ_WAKE); err_code = nrfx_rtc_init(&rtc, &config, rtc_handler); APP_ERROR_CHECK(err_code); is_uninit = false; //set the counter to 0 nrfx_rtc_counter_clear(&rtc); //Power on RTC instance nrfx_rtc_enable(&rtc); } /** ** @brief setup counter to allow check for timeout ** @param[in] timeout in ms that we will measure against ** @param[out] mgt remember our target ticks and state of the overflow toggle ** */ void startTimeout(rtc_mgt_t *mgt, uint32_t timeout) { uint32_t ticks; if (is_uninit) rtc_config_tick(); // set target ticks if (timeout != 0) ticks = timeout; // 1 ms pr tick else ticks = 0xfffff; // large number less than max ticks mgt->tgt_ticks = nrfx_rtc_counter_get(&rtc) + ticks; mgt->overflow_flag = overflow_toggle; } /** ** @brief check whether a timeout has expired ** @param[in] mgt containing our target and the start overflow state ** returns true if timed out, false if not ** */ bool checkTimeoutExpired(rtc_mgt_t *mgt) { if (overflow_toggle != mgt->overflow_flag) { // have overflowed so say we have timed out return true; } else if (nrfx_rtc_counter_get(&rtc) >= mgt->tgt_ticks ) { // timed out return true; } else return false; } /** @brief set an interrupt to wake us up ** @param[in] sleep_ms the time to sleep in millisecs */ bool set_wakeup(uint32_t sleep_ms) { uint32_t err_code; rtc_config_wake(); err_code = nrfx_rtc_cc_set(&rtc, 0, NRFX_RTC_US_TO_TICKS(sleep_ms * 1000, RTC_FREQ_WAKE),true); if (err_code == NRFX_ERROR_TIMEOUT) { return false; } else APP_ERROR_CHECK(err_code); return true; } /** @brief returns the current tick *** @return current ticks */ uint32_t rtc_get_tick(void) { return RTC_TICKS_TO_MS(nrfx_rtc_counter_get(&rtc), RTC_FREQ_TICK); } /** @brief set the last msg counter to current tick */ void rtc_msg_rcd(void) { last_msg = nrfx_rtc_counter_get(&rtc); } /** ** @brief check whether a timeout has expired due to no message ** @param[in] mgt containing our target and the start overflow state ** @param[in] max_secs is the timeout period ** returns true if timed out, false if not ** */ bool checkLastMsg(rtc_mgt_t *mgt, uint32_t max_secs) { if (overflow_toggle != mgt->overflow_flag) { // have overflowed so say we have timed out return true; } else if (nrfx_rtc_counter_get(&rtc) > (last_msg + max_secs * 1000)) { // timed out return true; } else return false; } /** @} */
matching header - apart from prototypes
#define RTC_FREQ_TICK 1000 /// frequency in hz for ticks ie 1000 ticks per sec or one per millisec #define RTC_FREQ_WAKE 6400 /// frequency in hz gives us a resolution to handle millisecs 20ms is 128 ticks /** @struct rtc_mgt_t ** @brief management structure for timeouts. ** @details gathers the information in one place. Ticks we are going for and state of the overflow flag */ typedef struct { uint32_t tgt_ticks; bool overflow_flag; } rtc_mgt_t; /**@brief Macro to convert ticks to millisects. */ #define RTC_TICKS_TO_MS(ticks,freq) (((ticks) * 1000) / (freq))
the code that starts the app_timer:
/** @brief init the mpu and the madgwick library, init internal variables */ void Mems_Init(void) { ret_code_t err_code; NRF_LOG_INFO("mems init"); mpu9250_init(&mpu9250, &mpu9250_inst, &mpu9250_conf, NULL); TM_AHRSIMU_Init(&AHRSIMU, AHRS_BETA, AHRS_SAMPLE_RATE, 0); for (int i = 0; i < 3; i++) { acc[i] = 0.0f; gyr[i] = 0.0f; } temp = 0.0f; Mems_getCal(); // start timer for mems stuff if (mems_timer_state == DEFINED) { err_code = app_timer_create(&m_mems_timer_id, APP_TIMER_MODE_REPEATED, Mems_Iteration); APP_ERROR_CHECK(err_code); mems_timer_state = STOPPED; } if (mems_timer_state == STOPPED) { err_code = app_timer_start(m_mems_timer_id, APP_TIMER_TICKS(10), NULL); APP_ERROR_CHECK(err_code); mems_timer_state = STARTED; } }
Any help would be gratefully received
Cheers Paul