Redboard Artemis Nano : 32KHz on board precision

Hi,

I’ve made many time tests using RTC powered by the onboard 32KHz XTAL.

I’ve used RTC interrupt to trigger a routine that switchs on the built LED for 1 sec (that 1sec is only a simple delay function call - not accurate).

On long hours tests, time is deriving.

I modified the CALX’apollo3 register to correct that derivation (negative value), but time is still deriving.

Have you met that kinf of problems with the internal RTC and the onboard 32KHz ?

What is the precision of that onboard 32KHz please ?

Here is the arduino code I used :

#include "RTC.h" //Include RTC library included with the Aruino_Apollo3 core

APM3_RTC myRTC; //Create instance of RTC class

void setup() {

  // Error Adjustment XTAL (from personal measures)
  CLKGEN->CLKKEY         = CLKGEN_CLKKEY_CLKKEY_Key;
  CLKGEN->CALXT          = -12;
  CLKGEN->CLKKEY         = 0;

  am_hal_rtc_int_disable( AM_HAL_RTC_INT_ALM );

  myRTC.getTime();
  myRTC.setTime( 12, 25, 0, 0, 11, 11, 19 );  // h m s centiemesec jourdumois mois année
  // myRTC.setToCompilerTime(); //Easily set RTC using the system __DATE__ and __TIME__ macros from compiler

  
  // Set minute alarm
  am_hal_rtc_alarm_interval_set( AM_HAL_RTC_ALM_RPT_MIN );

  // Enable RTC IT
  am_hal_rtc_int_enable( AM_HAL_RTC_INT_ALM );

  //Enable the RTC interrupt in the NVIC.
  NVIC_EnableIRQ(RTC_IRQn);

  // TEST
  pinMode(LED_BUILTIN, OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:

  //Serial.printf( "%04X\r\n", CLKGEN->CALXT );

  myRTC.getTime();

  Serial.printf("It is now ");
  Serial.printf("%d:", myRTC.hour);
  Serial.printf("%02d:", myRTC.minute);
  Serial.printf("%02d.", myRTC.seconds);
  Serial.printf("%02d", myRTC.hundredths);

  Serial.printf(" %02d/", myRTC.month);
  Serial.printf("%02d/", myRTC.dayOfMonth);
  Serial.printf("%02d", myRTC.year);

  Serial.printf(" Day of week: %d =", myRTC.weekday);
  Serial.printf(" %s", myRTC.textWeekday);

  Serial.println();



  // Infinite loop to go sleeping
  while( 1 )
  {
    am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); //Sleepy time
  }

  
}

//Called once number of milliseconds has passed
extern "C" void am_rtc_isr(void)
{
  digitalWrite(LED_BUILTIN, HIGH);
  
  // Lower the flag
  am_hal_rtc_int_clear( AM_HAL_RTC_INT_ALM );
  
  myRTC.getTime();

  Serial.printf("It is now ");
  Serial.printf("%d:", myRTC.hour);
  Serial.printf("%02d:", myRTC.minute);
  Serial.printf("%02d.", myRTC.seconds);
  Serial.printf("%02d", myRTC.hundredths);

  Serial.printf(" %02d/", myRTC.month);
  Serial.printf("%02d/", myRTC.dayOfMonth);
  Serial.printf("%02d", myRTC.year);

  Serial.printf(" Day of week: %d =", myRTC.weekday);
  Serial.printf(" %s", myRTC.textWeekday);

  Serial.println();

  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);  
  
    
}

There are lots of sources of error. Cheap crystals are usually ±100 parts per million in initial accuracy. Over a day that contains 86400 seconds, that initial frequency error could amount to ±8.6 seconds per day. Better crystals are ±20 ppm. That would still be ±1.7 seconds per day. I don’t know the accuracy spec on the crystal. What kind of divergence rate are you seeing?

Things to check:

Are you positive that the RTC clock source is the crystal and not an RC oscillator source?

If you have a frequency counter, I would route some form of the crystal clock out to a GPIO and measure it. I would also use the frequency counter to prove that your calibration step is operating the way you think it should be.

Finally, temperature effects are significant on a crystal. You will find that you can change the output frequency of your crystal pretty significantly by touching it with your warm or cold finger tip. If your system experiences any significant temperature changes during your testing periods, the RTC time will diverge.

I just measured mine with a freq counter. It reports 32768.814 Hz, which is 24.8 ppm error. Board temp is 71 degrees F. If the crystal is spec’d at 100ppm error (typical for a cheap xtal), that is a good result. Without calibration, it means that my RTC will run fast by 2.14 seconds per day.

Here is the code required to drive the 32788 XTAL clock to artemis pad 7, which is the pin silkscreened as “MOSI” on a Redboard. Call this routine from your main() program, and the clock will get driven to the specified output:

void clkOut()
{
  // Define pad7 (Redboard/Blackboard silkscreen D11/MOSI) to be CLKOUT
  const am_hal_gpio_pincfg_t g_AM_BSP_GPIO_CLKOUT =
  {
      .uFuncSel            = AM_HAL_PIN_7_CLKOUT,
      .ePullup             = AM_HAL_GPIO_PIN_PULLUP_NONE,
      .eDriveStrength      = AM_HAL_GPIO_PIN_DRIVESTRENGTH_2MA,
      .eGPOutcfg           = AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL,
  };

  uint32_t status;
  
  am_hal_gpio_pinconfig(7, g_AM_BSP_GPIO_CLKOUT);
  status = am_hal_clkgen_clkout_enable(true, AM_HAL_CLKGEN_CLKOUT_XTAL_32768);
}

Nice work Robin.

To put a bow on it, the XTAL calibration system has been tested to work as defined in the datasheet. Here is an example of how to use it:

My system is running 24.8ppm fast. To computer the correction factor, I divide my error by the magic constant 0.9535 to get my correction factor: 26.05. This value gets rounded to the closest integer, 26. The correction field is signed, and I am running fast, so the signed 1-bit correction value becomes -26. As represented in 11 bits, this becomes 0x7E9. Writing that value to the CALXT register causes calibration to take place. Important note: the only frequencies that get calibrated are the frequencies that are divided down from the basic XTAL 32768 source, not the XTAL 32768 frequency itself. To see the effects of calibration on the freq counter, we drive a divided-down freq such as 16384 to our clkout pin. Here is the same sample code showing how to generate a calibrated output freq. Obviously, the calibration constant only applies to my specific board, and I skipped the test to make sure that the final calibration value was within range.

void clkOut()
{
  // Define pad7 (Blackboard silkscreen D11/MOSI) to be CLKOUT
  const am_hal_gpio_pincfg_t g_AM_BSP_GPIO_CLKOUT =
  {
      .uFuncSel            = AM_HAL_PIN_7_CLKOUT,
      .ePullup             = AM_HAL_GPIO_PIN_PULLUP_NONE,
      .eDriveStrength      = AM_HAL_GPIO_PIN_DRIVESTRENGTH_2MA,
      .eGPOutcfg           = AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL,
  };

  uint32_t status;
  
  // Install my correction factor as a signed, 11-bit number:
  CLKGEN->CALXT_b.CALXT = -26 & 0x7FF;

  am_hal_gpio_pinconfig(7, g_AM_BSP_GPIO_CLKOUT);
  status = am_hal_clkgen_clkout_enable(true, AM_HAL_CLKGEN_CLKOUT_XTAL_16384);
}

Now the frequency counter shows an output that bounces around a bit, but appears to my eyeball to be centered around 16384.00 Hz. That’s because the calibration mechanism is designed to take place over a 32-second interval. The entire 32-second interval will be calibrated accurately to be 32 seconds, but any given second within that interval may be a bit slow or a bit fast.

So to get back to the original question, if the RTC was somehow being clocked with an undivided 32768 XTAL clock, then it would appear as though the calibration mechanism was having no effect.

Thanks a lot for your answers and examples that light me a lot about precision I can expect.

My first measures were made with the stopwatch of my mobile, based on local clock, which is setup to be synchronised on network time. That is to be empirical and really not accurate on short times. But can be acceptable on long measure periods (many hours till 1 day or more).

For 1 day, deviation is less than 1 second which is finally good. My last measure is based on a 3 days long period which deviation is about 1.2 second. If I made no error, it means about 4.6ppm.

I took a look on precision of watches : good mechanicals are about 12ppm, good electronics are much less.

RTC clock is powered by a 100Hz clock coming from the 32,768Khz. So correction register applies to it.

I’ll try to measure a derived output signal to appreciate that correction.

Thanks again for all your informations.

JL

I had to go re-read the data sheet, because if the RTC clock really comes directly from the 32768 XTAL clock, then it can’t be calibrated because the calibration mechanism only applies to the divided down frequencies. The data sheet says

If the XT Oscillator is selected, 100 Hz is generated by dividing the 2048 Hz division of the XT…

That makes sense now: in RTC XTAL mode, the RTC timekeeping is based on a calibrated 2048Hz subdivision of the uncalibrated XTAL clock.

robin,

I tried running this code on my Artemis Redboard. I only see output on pad 7 (D11/MOSI) if I set the XTAL clockout select to AM_HAL_CLKGEN_CLKOUT_XTAL_32768. I haven’t tried all of the frequency selects, yet. But, I have tried 16384 and 8192. Do you have any idea what is going on?

Weird. It’s working for me.

Did you make sure to use the proper symbols when selecting the clock source? Not every clock source can be driven to CLKOUT, although the two you chose should work. Here is the complete list of possible clock selectors:

typedef enum
{
    AM_HAL_CLKGEN_CLKOUT_LFRC_1024  = 0x0,      // LFRC
    AM_HAL_CLKGEN_CLKOUT_XTAL_16384,            // XTAL / 2
    AM_HAL_CLKGEN_CLKOUT_XTAL_8192,             // XTAL / 4
    AM_HAL_CLKGEN_CLKOUT_XTAL_4096,             // XTAL / 8
    AM_HAL_CLKGEN_CLKOUT_XTAL_2048,             // XTAL / 16
    AM_HAL_CLKGEN_CLKOUT_XTAL_1024,             // XTAL / 32
    AM_HAL_CLKGEN_CLKOUT_RTC_1HZ    = 0x10,     // RTC
    AM_HAL_CLKGEN_CLKOUT_XTAL_0_015 = 0x16,     // XTAL / 2097152 = 0.015625 Hz
    AM_HAL_CLKGEN_CLKOUT_XTAL_32768,            // XTAL
    AM_HAL_CLKGEN_CLKOUT_CG_100,                // ClkGen 100Hz
    AM_HAL_CLKGEN_CLKOUT_LFRC_512 = 0x23,       // LFRC / 2     = 512 Hz
    AM_HAL_CLKGEN_CLKOUT_LFRC_32,               // LFRC / 32    =  32 Hz
    AM_HAL_CLKGEN_CLKOUT_LFRC_2,                // LFRC / 512   =   2 Hz
    AM_HAL_CLKGEN_CLKOUT_LFRC_0_03,             // LFRC / 32768 = 0.03125 Hz
    AM_HAL_CLKGEN_CLKOUT_XTAL_128,              // XTAL / 256   = 128 Hz
    AM_HAL_CLKGEN_CLKOUT_XTAL_4,                // XTAL / 8192  =  4 Hz
    AM_HAL_CLKGEN_CLKOUT_XTAL_0_5,              // XTAL / 65536 =  0.5 Hz
    // The next 5 are Uncalibrated LFRC
    AM_HAL_CLKGEN_CLKOUT_ULFRC_64,              // ULFRC / 16   = 64 Hz (uncal LFRC)
    AM_HAL_CLKGEN_CLKOUT_ULFRC_8,               // ULFRC / 128  =  8 Hz (uncal LFRC)
    AM_HAL_CLKGEN_CLKOUT_ULFRC_1,               // ULFRC / 1024 =  1 Hz (uncal LFRC)
    AM_HAL_CLKGEN_CLKOUT_ULFRC_0_25,            // ULFRC / 4096 = 0.25 Hz (uncal LFRC)
    AM_HAL_CLKGEN_CLKOUT_ULFRC_0_0009,          // ULFRC / 1M   = 0.000976 Hz (uncal LFRC)
    //
    AM_HAL_CLKGEN_CLKOUT_LFRC_0_0004 = 0x31,    // LFRC / 2M    = 0.00048828125 Hz
    // Following are Not Autoenabled ("NE")
    AM_HAL_CLKGEN_CLKOUT_XTALNE_32768 = 0x35,   // XTALNE / 1   = 32768 Hz
    AM_HAL_CLKGEN_CLKOUT_XTALNE_2048,           // XTALNE / 16  =  2048 Hz
    AM_HAL_CLKGEN_CLKOUT_LFRCNE_32,             // LFRCNE / 32  =    32 Hz
    AM_HAL_CLKGEN_CLKOUT_LFRCNE_1024 = 0x39     // LFRCNE / 1   =  1024 Hz
} am_hal_clkgen_clkout_e;

Also, did you make sure the enable the output using ‘true’ or ‘1’ as the first parameter to the call to am_hal_clkgen_clkout_enable()? My example doesn’t test the return call for errors, so you might want to do that too.

I just changed my to use the LFRC 1K output and now I see an output of 1027 Hz to 1029Hz. It bounces around more than I thought it would. I also just tried the ULFRC/16 which should be 64 Hz. The output freq is 64.3 ±0.1 Hz.

Did your program do something to the key register that might have disallowed access to the clkgen registers?

Does your frequency counter need a sensitivity adjustment for the other frequencies? It seems unlikely, but maybe it might.

I found the issue. I was assuming that a power-on reset was not required to put the MCU into a clean initial state. So, I was only using the soft reset after bootload and/or the button on the external reset pin. Once I unplugged/plugged the USB cable, I started getting good frequency data from GPIO 7. I haven’t set up a freq counter, but I do have a logic analyzer that can give me much of the same information at a bit less precision. I do think I read in the data sheet somewhere that some internal register states are maintained across non-POR resets or sleep modes. Not sure about the details, I have to research it more.

After incorporating a quick XTAL calibration adjustment, my GPIO 7 CLKOUT was coming out to an average of 16.38 kHz. It’s not as precise as a freq counter, but it’s all I have right now.

Edit:

After looking into this, I’ve determined that this was not completely related to POR. It looks like the BSP function, am_bsp_low_power_init() is stopping the XTAL. If I restart the XTAL after that function call using ```
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_XTAL_START, 0)

robin_hodgson:
I had to go re-read the data sheet, because if the RTC clock really comes directly from the 32768 XTAL clock, then it can’t be calibrated because the calibration mechanism only applies to the divided down frequencies. The data sheet says

If the XT Oscillator is selected, 100 Hz is generated by dividing the 2048 Hz division of the XT…

That makes sense now: in RTC XTAL mode, the RTC timekeeping is based on a calibrated 2048Hz subdivision of the uncalibrated XTAL clock.

Hi Robin,

I think you were right the first time, the 100Hz is a 32768Hz divided frequency, so calibration mechanism should apply to it ? Moreover, in datasheet :

  • fig 63 (p523) shows that Calibration Registers apply to XT Chain and so CLKOUT and RTC.

  • fig 64 (p524) shows XT Calibration applied to Divider block from XT_16KHz to XT_100Hz (wich is used for RTC). So using only 32768 Hz is not corrected by Calibration registers as you have already mentionned it, but all other divided frequencies are.