I want to measure the accuracy of the 32.768 kHz oscillator.
Is there a way to output it on a port-pin, even when it comes out via a divider?
I can't touch the crystal as it would change the loading.
I want to measure the accuracy of the 32.768 kHz oscillator.
Is there a way to output it on a port-pin, even when it comes out via a divider?
I can't touch the crystal as it would change the loading.
Try something like the code below. It should output 512Hz square wave on pin CLKOUT_PIN. To change frequency, adjust TICKS and PRESCALER. If there is no softdevice and no OS running there should be no jitter.
#define CC 0
#define PRESCALER 31
#define TICKS 1
#define CLKOUT_PIN 15
static nrf_drv_rtc_config_t m_rtc_config = NRF_DRV_RTC_DEFAULT_CONFIG;
// RTC0 is used by softdevice, RTC1 is used by RTOS
static nrf_drv_rtc_t const m_rtc = NRF_DRV_RTC_INSTANCE(2);
static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
nrf_gpio_pin_toggle(CLKOUT_PIN);
uint32_t err_code = nrf_drv_rtc_cc_set(
&m_rtc,
CC,
(nrf_rtc_cc_get(m_rtc.p_reg, CC) + TICKS) & RTC_COUNTER_COUNTER_Msk,
true);
APP_ERROR_CHECK(err_code);
}
void rtc_init(void)
{
ret_code_t err_code;
m_rtc_config.prescaler = PRESCALER; // prescaler 31 for 1024 ticks per second
err_code = nrf_drv_rtc_init(&m_rtc, &m_rtc_config, rtc_handler);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_rtc_cc_set(&m_rtc, CC, TICKS, true);
APP_ERROR_CHECK(err_code);
nrf_drv_rtc_enable(&m_rtc);
}
int main(void)
{
ret_code_t err_code = nrf_drv_clock_init();
APP_ERROR_CHECK(err_code);
nrf_gpio_cfg_output(CLKOUT_PIN);
rtc_init();
while (1) {};
}
Try something like the code below. It should output 512Hz square wave on pin CLKOUT_PIN. To change frequency, adjust TICKS and PRESCALER. If there is no softdevice and no OS running there should be no jitter.
#define CC 0
#define PRESCALER 31
#define TICKS 1
#define CLKOUT_PIN 15
static nrf_drv_rtc_config_t m_rtc_config = NRF_DRV_RTC_DEFAULT_CONFIG;
// RTC0 is used by softdevice, RTC1 is used by RTOS
static nrf_drv_rtc_t const m_rtc = NRF_DRV_RTC_INSTANCE(2);
static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
nrf_gpio_pin_toggle(CLKOUT_PIN);
uint32_t err_code = nrf_drv_rtc_cc_set(
&m_rtc,
CC,
(nrf_rtc_cc_get(m_rtc.p_reg, CC) + TICKS) & RTC_COUNTER_COUNTER_Msk,
true);
APP_ERROR_CHECK(err_code);
}
void rtc_init(void)
{
ret_code_t err_code;
m_rtc_config.prescaler = PRESCALER; // prescaler 31 for 1024 ticks per second
err_code = nrf_drv_rtc_init(&m_rtc, &m_rtc_config, rtc_handler);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_rtc_cc_set(&m_rtc, CC, TICKS, true);
APP_ERROR_CHECK(err_code);
nrf_drv_rtc_enable(&m_rtc);
}
int main(void)
{
ret_code_t err_code = nrf_drv_clock_init();
APP_ERROR_CHECK(err_code);
nrf_gpio_cfg_output(CLKOUT_PIN);
rtc_init();
while (1) {};
}
On a similar note: we have to measure the 32KHz crystal accuracy on every product (nRF5x or not). What we do is have a 1 second interrupt handler driven by the RTC or similar feature on other processors. That interrupt handler pulses a unused GPIO every time it fires. This lets our HW folks measure the accuracy of the crystal over the temperature range of the product, and we can apply calibration for temperature and offset if needed based on what we learn in the HW testing.
If there is no softdevice and no OS running there should be no jitter.
Is there anything that can be done about the jitter when there is a softdevice or OS running?
I thought that using the PPI/GPIOTE peripherals to toggle the GPIO pin would clear it up, but I still see jitter.
To be clear, my code does toggle P1.07 at 8192Hz but occasionally (about once per second) I get jitter (positive or negative pules that are significantly longer or shorter than the other pulses)
Here are extracts from my code to give you an idea of what I'm doing:
//============================================================================= // Private Definitions //============================================================================= // RTC0 is used by the SoftDevice, RTC1 by the app_timer library, // RTC2 is normally used by counter.c for the BLE test but has been re-factored to use TIMER2. #define RTC_INSTANCE 2 // Real-time counter resource to use #define RTC_CC_INSTANCE 0 // Capture/Compoare register to use within RTC_INSTANCE #define COMPARE_COUNTERTIME (1UL) //Get Compare event COMPARE_TIME seconds after the counter starts from 0 #define LFCLK_TEST_PIN NRF_GPIO_PIN_MAP(1,7) //P1.07 LFCLK test pin //============================================================================= // Private Prototypes //============================================================================= static void lfclk_config(void); static void rtc_config(void); static void interconnect_config(void); static void gpiote_config(void); //============================================================================= // Private Data //============================================================================= // RTC0 is used by the SoftDevice, RTC1 by the app_timer library, // RTC2 is normally used by counter.c for the BLE test but has been re-factored to use TIMER2. // Declaring an instance of nrf_drv_rtc for RTC_INSTANCE (Real Time Counter) static const nrf_drv_rtc_t m_rtc = NRF_DRV_RTC_INSTANCE(RTC_INSTANCE); //============================================================================= // Public Functions //============================================================================= /***************************************************************************** * Function: RTC_Init * * Description: RTC Init * * Parameters: none * * Returns: nothing *****************************************************************************/ void BSP_RTC_Init(void) { // configure LFCLK_TEST_PIN to toggle gpiote_config(); // make sure 32.768kHz clock is enabled and using external crystal lfclk_config(); // configure/initialize PPI to link the real time counter with the timer/counter interconnect_config(); // configure/initialize Real Time Counter rtc_config(); BB_RAM->rtc_was_just_set = 0; } /***************************************************************************** * Function: rtc_handler * * Description: for handling the RTC0 interrupts. Triggered on TICK and COMPARE0 match. * NOTE: any RAM used in this call needs to be powered during battery protect * shutdown (see BSP_EnterShutdown()) * * Parameters: int_type == what caused the interrupt * * Returns: nothing *****************************************************************************/ void rtc_handler(nrfx_rtc_int_type_t int_type) { if (int_type == NRFX_RTC_INT_TICK) { ///nrf_gpio_pin_toggle(LFCLK_TEST_PIN); } else if (int_type == CONCAT_2(NRFX_RTC_INT_COMPARE,RTC_CC_INSTANCE)) { BB_RAM->rtc_value++; BB_RAM->rtc_compliment = ~(BB_RAM->rtc_value); BB_RAM->tenth_seconds = 0; } } //============================================================================= // Private Functions //============================================================================= /***************************************************************************** * Function: lfclk_config * * Description: starting the internal LFCLK XTAL oscillator. * * Parameters: none * * Returns: nothing *****************************************************************************/ static void lfclk_config(void) { ret_code_t err_code = nrf_drv_clock_init(); if(err_code != NRF_ERROR_MODULE_ALREADY_INITIALIZED) { APP_ERROR_CHECK(err_code); } nrf_drv_clock_lfclk_request(NULL); } /***************************************************************************** * Function: rtc_config * * Description: initialization and configuration of RTC driver instance. * * Parameters: none * * Returns: nothing *****************************************************************************/ static void rtc_config(void) { uint32_t err_code; //Initialize RTC instance nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG; config.prescaler = 1; //4095; // max prescaler so F = 32768/(4095+1) = 8Hz err_code = nrf_drv_rtc_init(&m_rtc, &config, rtc_handler); APP_ERROR_CHECK(err_code); // Enable tick event & interrupt nrf_drv_rtc_tick_enable(&m_rtc,true); //Set compare channel to trigger interrupt after COMPARE_COUNTERTIME seconds err_code = nrf_drv_rtc_cc_set(&m_rtc,RTC_CC_INSTANCE,COMPARE_COUNTERTIME * 16384 /*8*/,true); APP_ERROR_CHECK(err_code); // Enable overflow event & interrupt // nrf_drv_rtc_overflow_enable(&m_rtc,true); // Power on RTC instance nrf_drv_rtc_enable(&m_rtc); } /***************************************************************************** * Function: interconnect_config * * Description: Connect the compare event from RTC_INSTANCE to the clear task of the RTC_INSTANCE * * Parameters: none * * Returns: nothing *****************************************************************************/ static void interconnect_config(void) { // this PPI connects the overflow event from RTC_INSTANCE to the count task of TIMER_INSTANCE nrf_ppi_channel_t m_ppi_channel; uint32_t err_code = NRF_SUCCESS; err_code = nrf_drv_ppi_init(); APP_ERROR_CHECK(err_code); // Configure 1st available PPI channel to clear the RTC2 counter on RTC compare // get a free PPI channel err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel); APP_ERROR_CHECK(err_code); // get the address of the compare event uint32_t cmp_evt = nrf_drv_rtc_event_address_get(&m_rtc, CONCAT_2(NRF_RTC_EVENT_COMPARE_,RTC_CC_INSTANCE)); // get the address of the clear task uint32_t clt_tsk = nrf_drv_rtc_task_address_get(&m_rtc, NRF_RTC_TASK_CLEAR); // link the compare event with the clear task err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, cmp_evt, clt_tsk); APP_ERROR_CHECK(err_code); // Enable configured PPI channel err_code = nrf_drv_ppi_channel_enable(m_ppi_channel); APP_ERROR_CHECK(err_code); // Setup the gpiote/RTC channel------------------------------------- nrf_ppi_channel_t m_ppi_channel_gpiote; // get a free PPI channel err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel_gpiote); APP_ERROR_CHECK(err_code); // get the address of the tick event uint32_t tick_evt = nrf_drv_rtc_event_address_get(&m_rtc, NRF_RTC_EVENT_TICK); // get the address of the gpiote toggle task uint32_t toggle_tsk = nrfx_gpiote_out_task_addr_get(LFCLK_TEST_PIN); // link the tick event with the toggle task err_code = nrf_drv_ppi_channel_assign(m_ppi_channel_gpiote, tick_evt, toggle_tsk); APP_ERROR_CHECK(err_code); // Enable configured PPI channel err_code = nrf_drv_ppi_channel_enable(m_ppi_channel_gpiote); APP_ERROR_CHECK(err_code); } static void gpiote_config(void) { ret_code_t err_code; if(!nrf_drv_gpiote_is_init()) { err_code = nrf_drv_gpiote_init(); APP_ERROR_CHECK(err_code); } nrfx_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true); err_code = nrfx_gpiote_out_init(LFCLK_TEST_PIN, &out_config); APP_ERROR_CHECK(err_code); nrfx_gpiote_out_task_enable(LFCLK_TEST_PIN); }
Never mind.
I found that the jitter is caused from me also using PPI to clear RTC2 when a compare match occurs.
I'll ask that as a separate question but I'll leave my original question here to help anyone looking for a less-jittery way to test the 32kHz crystal