This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

RTC tick is out by 24hz when set to 1000hz

Hi,

     I am running NRF52840 on a custom board with SDK 15.0.0, Softdevice S140 6.0.0 and SES.

I am measuring using a toggled GPIO pin measured on a CRO. This produces a wave form at 511-512hz - one transition up or down per tick - giving me an external measured tick rate of ~1024hz.

I have tried this with both the Synth and RC softdevice sources. For synth the config is:

// </h> 
//==========================================================

// <h> Clock - SoftDevice clock configuration

//==========================================================
// <o> NRF_SDH_CLOCK_LF_SRC  - SoftDevice clock source.
 
// <0=> NRF_CLOCK_LF_SRC_RC 
// <1=> NRF_CLOCK_LF_SRC_XTAL 
// <2=> NRF_CLOCK_LF_SRC_SYNTH 

#ifndef NRF_SDH_CLOCK_LF_SRC
#define NRF_SDH_CLOCK_LF_SRC 2
#endif

// <o> NRF_SDH_CLOCK_LF_RC_CTIV - SoftDevice calibration timer interval. 
#ifndef NRF_SDH_CLOCK_LF_RC_CTIV
#define NRF_SDH_CLOCK_LF_RC_CTIV 0
#endif

// <o> NRF_SDH_CLOCK_LF_RC_TEMP_CTIV - SoftDevice calibration timer interval under constant temperature. 
// <i> How often (in number of calibration intervals) the RC oscillator shall be calibrated
// <i>  if the temperature has not changed.

#ifndef NRF_SDH_CLOCK_LF_RC_TEMP_CTIV
#define NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 0
#endif

// <o> NRF_SDH_CLOCK_LF_ACCURACY  - External clock accuracy used in the LL to compute timing.
 
// <0=> NRF_CLOCK_LF_ACCURACY_250_PPM 
// <1=> NRF_CLOCK_LF_ACCURACY_500_PPM 
// <2=> NRF_CLOCK_LF_ACCURACY_150_PPM 
// <3=> NRF_CLOCK_LF_ACCURACY_100_PPM 
// <4=> NRF_CLOCK_LF_ACCURACY_75_PPM 
// <5=> NRF_CLOCK_LF_ACCURACY_50_PPM 
// <6=> NRF_CLOCK_LF_ACCURACY_30_PPM 
// <7=> NRF_CLOCK_LF_ACCURACY_20_PPM 
// <8=> NRF_CLOCK_LF_ACCURACY_10_PPM 
// <9=> NRF_CLOCK_LF_ACCURACY_5_PPM 
// <10=> NRF_CLOCK_LF_ACCURACY_2_PPM 
// <11=> NRF_CLOCK_LF_ACCURACY_1_PPM 

#ifndef NRF_SDH_CLOCK_LF_ACCURACY
#define NRF_SDH_CLOCK_LF_ACCURACY 2
#endif

// </h> 

My code for the RTC 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;
}

/** @} */

Apart from prototypes the matching header defines:

#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))

GPIO debug toggle in the event handle just toggles the pin:

/** @brief switch debug/spare pin high or low to allow measurement with a cro
*/
void GPIO_debug_toggle(void) {
static bool is_on = false;

    if (is_on) {
       is_on = false;
       GPIO_control(GPIO_DEBUG_SPARE, GPIO_PIN_LOW);
    } else {
       is_on = true;
       GPIO_control(GPIO_DEBUG_SPARE, GPIO_PIN_HIGH);
    }
}

We are keen to get the clock as close to 1000 hz as possible. I am aware that I should expect a jitter of 15-46us. The trace is very stable just out by the period.

Any help most gratefully received.

Cheers Paul

Related