Hi,
I’m trying to test/confirm the accuracy of the 32kHz crystal oscillator and the 32MHz crystal oscillator by toggling a GPIO at a derived rate and at 50% duty. But I’m seeing frequencies that do not appear to make sense. Also, the tolerance range I am reading for both the HFXO and LFXO do not seem to match the electrical specifications.
I'm using the low-power PWM for the 32kHz testing and I'm using the App-PWM for the 32MHz testing.
The first bit of confusion comes from whether or not I am actually using the HFINT as opposed to the HFXO because the tolerance range of +/- 1.5% seems to matchup with what we are reading a lot more than the +/- 40 ppm (according to the HFXO section on the electrical specifications spreadsheet). We are using a 30ppm crystal.
The second bit of confusion is in regards to the frequency accuracy of the LFXO which seems to be most accurate (still not quite accurate enough) when set to the maximum frequency (255 ticks). Should this crystal be more accurate than the recorded values reflect? Also, why does the duty cycle change for each different tick rate when I am always manually setting the duty cycle to 50% after setting the frequency?
For reference, I am the nrf52832 with SDK version 17.0.2. The SoftDevice is enabled and running during the tests.
LFXO
| Ticks | 255 | 163 | 100 | 78 | ||||
| Expected | Actual | Expected | Actual | Expected | Actual | Expected | Actual | |
| Duty Cycle | 50% | 79.47% | 50% | 67.86% | 50% | 50% | 50% | 39.40% |
| Period (ms) | 15.563965 | 16.05 | 9.94873 | 6.8357 | 6.1 | 2.929602 | 4.7607 | 2.014216 |
| Frequency (Hz) | 64.25 | 62.29 | 100.52 | 146.2891 | 163.84 | 341.3412 | 210.05 | 496.468 |
HFXO
| Duty Cycle | 50% |
| Period (ms) | 10.04 - 10.05 |
| Frequency (Hz) | 99.4 - 99.5 |
/* Frequency of crystal oscillator PWM */
#define PWM_DEFAULT_FREQ_IN_HZ 100u
#define PWM_PERIOD_IN_US(hz) (uint16_t)((1.0 / (float)hz) * 1000000)
/* Period of crystal oscillator low power PWM */
#define LOW_POWER_PWM_TICKS 100u // This value was changed according to attached spreadsheet
/* The current frequency of the crystal oscillator PWM */
APP_STATIC uint32_t pwm_frequency_hz_u32 = PWM_DEFAULT_FREQ_IN_HZ;
nrfx_err_t XTAL_hfclk_init(void)
{
nrfx_err_t err_code = NRFX_SUCCESS;
app_pwm_config_t xtal_pwm_config = APP_PWM_DEFAULT_CONFIG_1CH(PWM_PERIOD_IN_US(pwm_frequency_hz_u32), DEBUG_TEST_PIN);
/* Set PWM polarity */
xtal_pwm_config.pin_polarity[PWM_CHANNEL_XTAL_DRIVE] = APP_PWM_POLARITY_ACTIVE_HIGH;
if (!xtal_hfclk_is_init_b)
{
/* Initialize the PWM */
err_code = app_pwm_init(&xtal_pwm, &xtal_pwm_config, XTAL_hfclk_pwm_callback);
if (NRFX_SUCCESS == err_code)
{
xtal_hfclk_is_init_b = true;
}
else
{
/* Uninitialize the PWM */
(void)app_pwm_uninit(&xtal_pwm);
}
}
else
{
err_code = NRFX_ERROR_ALREADY_INITIALIZED;
}
return err_code;
}
nrfx_err_t XTAL_hfclk_start(void)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (xtal_hfclk_is_init_b)
{
/* Start the PWM */
app_pwm_enable(&xtal_pwm);
/* Set the initial duty cycle */
pwm_ready_to_set_duty_b = true;
XTAL_hfclk_set_duty_cycle(50u);
}
else
{
err_code = APP_GENERAL_NOT_INITIALIZED_ERROR;
}
if (NRFX_SUCCESS == err_code)
{
/* Set the event flags */
event_xtal_hfclk_started_b = true;
hfclk_event_pending_b = true;
}
return err_code;
}
nrfx_err_t XTAL_lfclk_init(void)
{
nrfx_err_t err_code = NRFX_SUCCESS;
low_power_pwm_config_t xtal_low_power_pwm_config = LOW_POWER_PWM_DEFAULT_CONFIG(XTAL_LFCLK_TEST_PIN_MASK);
xtal_low_power_pwm_config.period = LOW_POWER_PWM_TICKS;
if (!xtal_lfclk_is_init_b)
{
/* Initialize the PWM */
err_code = low_power_pwm_init(&xtal_low_power_pwm, &xtal_low_power_pwm_config, NULL);
if (NRFX_SUCCESS == err_code)
{
xtal_lfclk_is_init_b = true;
}
else
{
/* Do nothing */
}
}
else
{
err_code = NRFX_ERROR_ALREADY_INITIALIZED;
}
return err_code;
}
nrfx_err_t XTAL_lfclk_start(void)
{
nrfx_err_t err_code = NRFX_SUCCESS;
if (xtal_lfclk_is_init_b)
{
/* Start the PWM */
err_code = low_power_pwm_start(&xtal_low_power_pwm, XTAL_LFCLK_TEST_PIN_MASK);
XTAL_lfclk_set_duty_cycle(50u);
}
else
{
err_code = APP_GENERAL_NOT_INITIALIZED_ERROR;
}
if (NRFX_SUCCESS == err_code)
{
/* Set the event flags */
event_xtal_lfclk_started_b = true;
lfclk_event_pending_b = true;
}
return err_code;
}