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