Crystal pull range on 3.1?

Status
Not open for further replies.

omega

New member
I have a project coming up that will need subtle changes to the clock of multiple Teensy's in order to synchronize them to a common signal. I know I can do this with parts I'm already using (a dedicated VCXO+PLL part, Pletronics FD77T, sadly discontinued), but I'm wondering if there's just enough functionality in the 3.1's OSC module to manage for prototype purposes. I see OSC_CR has 4 bits of crystal capacitance loading, which is theoretically the means by which a crystal's frequency is adjusted over a small range. The question would be: how big of a range? I don't need an exact frequency match, the PID works for cycle-lock (thus the indivudual units can oscillate frequency back and forth around the control as long as everybody ends up counting the same number of cycles over the medium term), so the mere 16 potential values don't concern me too much except as far as tuning the PID goes.

Any idea if the capacitive "pull" range is anywhere near 2x the crystal's initial tolerance? I'd try it out right now, but I'm deep into a different hardware project and am just getting this question out there while it occurs to me...

Thanks!
 
Do you have an example of how a program sets the crystal loading? I've got a Teensy setup measuring a GPS-locked signal right now so I could test the pull range for you, if there is some easy way to set it in software. So far I can tell you the temperature sensitivity is around -0.3 ppm/deg.C or so.
 
Last edited:
Do you have an example of how a program sets the crystal loading?

No, but it should be as simple as just setting bits 0..3 in OSC_CR as per section 25.71.1 page 528. RTC_CR has similar bits 10..13, and if the RTC can go ~252ppm total range with those, the main oscillator might be able to do the same.
 
how to access OSC_CR register?

I'm ignorant of the most basic things when directly accessing system registers. Is this one of those registers that has to be accessed within X cycles of startup, or something like that?

Apparently I cannot simply do
Code:
void setup() {
  int osc_set = OSC_CR;  // get osc config reg
}

void loop() {}

because OSC_CR is undefined, I get:
Code:
Arduino: 1.6.1 (Windows 7), TD: 1.21-beta12, Board: "Teensy 3.1, Serial, 96 MHz optimized (overclock), US English"

Using library FreqMeasure in folder: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\FreqMeasure (1.0.x format)

C:\Program Files (x86)\Arduino/hardware/tools/arm/bin/arm-none-eabi-g++ -c -O -g -Wall -ffunction-sections -fdata-sections -MMD -nostdlib -fno-exceptions -felide-constructors -std=gnu++0x -fno-rtti -mthumb -mcpu=cortex-m4 -D__MK20DX256__ -DTEENSYDUINO=121 -DARDUINO=10601 -DF_CPU=96000000 -DARDUINO_ARCH_AVR -DUSB_SERIAL -DLAYOUT_US_ENGLISH -IC:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3 -IC:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\FreqMeasure C:\Users\John\AppData\Local\Temp\build7701964081352784213.tmp\PulseTimer.cpp -o C:\Users\John\AppData\Local\Temp\build7701964081352784213.tmp\PulseTimer.cpp.o 

PulseTimer.ino: In function 'void setup()':

PulseTimer.ino:18:13: error: 'OSC_CR' was not declared in this scope

'OSC_CR' was not declared in this scope
 
Last edited:
Ok, by changing the OSC0_CR value from 0 to 15 (default was 10 = 0x0A = 1010b) I can pull the Teensy 3.1 frequency from -66 ppm to +75 ppm. The tuning curve is far from linear, more of a parabola. The tuning step size is 3 ppm at the low end and 28 ppm at the high end. On this particular Teensy near 20 C, by actively dithering the OSC0_CR value between 0010 and 1100 with the right duty cycle, you should be able to get an average of 0 ppm frequency offset. The ppm offset calculation was done using the Teensy to count its clocks between a fixed number of rising edges of a 1-PPS pulse from a GPS receiver.

Code:
OSC0_CR	 ppm
----------------
1111	-65.604
0111	-62.714
1011	-59.484
0011	-56
1101	-51.87
0101	-47.521
1001	-42.547
0001	-36.995
1110	-30.12
0110	-22.661
1010	-13.615  <= default setting
0010	-3.109
1100	+10.422
0100	+26.661
1000	+48.172
0000	+75.984
 
Last edited:
I too have been looking at the potential to turn a Teensy 3.1 into a very stable standard reference oscillator. I noted from the NDK manufacturers data sheet on the 16 MHz crystal that the load capacitance required for 16 MHz at 25 degC is nominally 8 pF and that the default of value of the OSC0_CR register (4006_5000h) is 0Ah meaning: (1) 2pF, (0) 4pF, (1) 8pF, (0) 16 pF - which by co-incidence is also 10pF (co-incidence = the pF values are in reverse order to normal binary number format).

I measured my Teensy 3.1 crystal frequency as being 15,999,762 Hz (+/- 1 Hz) at 24 degC - this seems to confirm that the default is a little too high at 10pF and the table from JBeale seems to agree (table indicates between 6pF and 8pF for 0ppm). A bit of stray capacitance on the board itself is also to be expected {though must be small :)!}. Here is my measurement garphically. At 700Hz marker the reading is in Hz exactly what the digital display shows and note the scale is 1 Hz increments above and below (approx 0.1ppm):

Teensy16MHzCrystal.jpg

Excellent quality signal. The beauty of measuring frequency is you can see immediately what effect temperature makes (a breeze cools, and momentary finger touch warms) and it is highly sensitive. You could argue that this is a great temperature sensor (once calibrated). I calibrate my freq using the RWM radio signal in Europe which is caesium controlled carrier (as good as GPS) - amongst other calibration signals.

My thoughts were to put Teensy 3.1 into a small thermally insulated box, with something like a DS18B20 temp sensor, and a power FET transistor (as a heater) controlled from a GPIO pin. This would make an MCXO (Microcontrolled Crystal Osc) similar to an OCXO (Oven Controlled).

I have in the recent past phase locked loop'd a 10 MHz OCXO to the BBC long wave 198 KHz radio in UK (GPS disciplined Rubidium) and got signal harmonics measured accurate to a few Hz at 1.5 GHz. I also use a UBlox NEO-7 GPS for comparison (very cheap on EBay).
 
I'm curious - did you try to set OSC0_CR to 8pf ?#

oops.. sorry, missed the post #8 :)
 
Last edited:
I must confess, Teensy 3.1 has simply copied the OSC_CR setting used on Teensy 3.0, without much attempt to really verify if it's the most accurate setting. No significant change to the PCB layout that would impact stray capacitance was made, so this seemed like a fairly reasonable thing. But maybe it's not really optimal.

For Teensy 3.0, I did spend some time with an ordinary benchtop frequency counter testing the various OSC0_CR settings. It has a TCXO as a reference.

We changed PCB manufacturers in mid-2014, shortly after Teensy 3.1 was released. Some 3.1's were made with the old boards, but most are the newer ones. None of the PCBs have precise layer-to-layer capacitance specs. Teensy 3.0 and 3.1 use a 4 layer PCB where the entire area underneath the crystal is layer 2 ground plane, so it's possible the PCB capacitance changed slightly. It's also possible each batch could vary a bit. It's also possible the on-chip stray capacitance might change slightly.

I also need to confess, I don't have a highly accurate reference frequency standard here. I do have used cell tower timing gear that I got surplus, with an oven controlled, GPS disciplined oscillator and 10 MHz output. But with so much to do on Teensy software, I've never had the time to figure out how to get it wired up and a 10 MHz reference into my frequency counter (which is a BK Precision model 1823A). With a TO-DO list a mile long and some huge features planned, it seems unlikely I'm going to manage to set up a true 10 MHz reference for accurate testing here.

So really, I've going to rely on you guys who have accurate measurement equipment. My hope is a solid consensus can form. I'm happy to edit the default value, if you guys are collectively confident it's an improvement.
 
The two purple T3.1 boards I got from OSH Park a few weeks ago measured -14.5 ppm, -0.16 ppm/C @ 21 C and -14.1 ppm, -0.16 ppm/C at 22 C respectively.

A sample of two isn't much of a manufacturing lot size, but if mine are typical, then the default value for OSC0_CR should be 0x02 instead of 0x0A as it is now. That would cut the frequency error by a factor of 3 (near 20 C, for these boards).
 
Last edited:
I changed the OSC_CR register (4006_500h) to a value of 0x02 (was 0x0A) which effectively makes the MCU value = 8pF. Ambient temp at the time was 24 degC and this is the result:

Teensy31 8pF.jpg

I also noted that the default value in the Freescale Ref Manual is 0x00. I measured the frequency before changing the register value and after a warm up period (circa 15 minutes) was (surprisingly) 15,999,762 again - same as before. I also note that the change to 8pF actually increases slightly the magnitude of the received signal (which would be expected being less of a load).

A word of caution - this will drift around so don't expect it always to be exact to 1 Hz. The Freq does jump about on first power up. Ageing and the other known factors will come into play. The heat from the MCU chip itself, as it does more or less work, will also affect things. I have ordered some DS18B20 devices so it will be fun to see how stable an MCXO can be :)

The Funcube Pro+ USB hardware (nothing to do with me!) and HDSDR software make an excellent spectrum analyser as well as being a very good software defined radio. Pickup is capacitive via a short insulated wire simply draped over the crystal. Software is free. Hardware is expensive but it has some high quality SAW filters built in. In the USA, WWV has a signal on 15 MHz for calibration.
 
For sake of completeness, I have just now tested the value of 0x0C = 6pF and the frequency measured was 16,000,132 Hz (+/- 1 Hz). It does seem to be quite stable too which is good news (not as much drifting around as I expected).
 
Do you know what registers need to be changed if I want to replace the 16MHz crystal by a 20MHz TCXO ?
 
I don't think this is just a matter of changing the external osc from 16 MHz to 20 MHz. There is a x6 multiplier that changes the cpu clock to 96 MHz internally so I think you would run into speed trouble.
 
I don't think this is just a matter of changing the external osc from 16 MHz to 20 MHz. There is a x6 multiplier that changes the cpu clock to 96 MHz internally so I think you would run into speed trouble.

Actually I did not find any good TCXO @ 16MHz, I have only a good TCXO (0.15 ppm stability) @ 20MHz. So I Changed the 16MHz XO of the teensy board into this 20MHz TCXO.
Apparently I just have to change the PRDIV register in the "kinetis.h" file. The PRDIV value selects the division to be made from the crystal oscillator to built the PLL output signal.
With the 16MHz XO : PRDIV=3 --> division by 4 --> PLLoutput = 16MHz/4=4MHz.
With the 20MHz TCXO: PRDIV=4 --> division by 5 --> PLL output= 20MHz/5=4MHz.
I thought this could work this way but I made thoses changes and now my teensy board USB connection is not recognized by the arduino+teensyduino software !
Do you have any idea ......?
 
With the 16MHz XO : PRDIV=3 --> division by 4 --> PLLoutput = 16MHz/4=4MHz.
With the 20MHz TCXO: PRDIV=4 --> division by 5 --> PLL output= 20MHz/5=4MHz.
I thought this could work this way but I made thoses changes and now my teensy board USB connection is not recognized by the arduino+teensyduino software !
Do you have any idea ......?

You're on the right track. PRDIV should make this work.

However, you can't change the code in the Mini54 chip, which takes control in bootloader mode. Teensy is designed so the Mini54 can't be altered by normal loading. It will always expect a 16 MHz crystal.

For reprogramming using Teensy Loader, you're going to have to use a 16 MHz clock. Maybe you could wire up a mux circuit with 2 oscillators, and a pullup resistor on its control signal? When the Mini54 chip takes control, all the pins go into high impedance mode, so the resistor can cause the mux to pass a 16 MHz clock.

Then you can edit the startup code to drive that pin low before starting the PLL, so your 20 MHz high accuracy clock will be used.

When your code is finalized, you could remove the mux and 16 MHz osc. It'll only be needed for programming.
 
I too have been working on this and find similar results. Here is my particular T3.1 with a 10 MHz rubidium reference and varying OSC0_CR capacitance from 2 to 32 pF (I didn't use the 0 pF setting). The Teensy heats up (about 10 ºC above ambient) -- so this test is at a (settled) temperature of about 35 ºC.

Code:
! Mean ADC Temperature = 38016
FREF = 10000000
 Cap =  1, SCAP =  8:  9999569.6 Hz
 Cap =  2, SCAP =  4:  9999774.9
 Cap =  3, SCAP = 12:  9999930.5
 Cap =  4, SCAP =  2: 10000060.7
 Cap =  5, SCAP = 10: 10000161.9
 Cap =  6, SCAP =  6: 10000249.5
 Cap =  7, SCAP = 14: 10000321.1
 Cap =  8, SCAP =  1: 10000197.9
 Cap =  9, SCAP =  9: 10000441.2
 Cap = 10, SCAP =  5: 10000489.5
 Cap = 11, SCAP = 13: 10000531.6
 Cap = 12, SCAP =  3: 10000572.0
 Cap = 13, SCAP = 11: 10000605.8
 Cap = 14, SCAP =  7: 10000637.2
 Cap = 15, SCAP = 15: 10000665.2
Basically (as others have found), SCAP between 2 and 12 alternates from being just too fast to being too slow. I use a modified version of the FrequencyCounter lib to measure frequency on the Teensy -- after calibration I dither between these two codes at the appropriate duty cycle to generate a clock with correct average frequency. If temperature is stable, I can keep readings within 0.1 Hz (10 ppb @ 10 MHz).

My intent is to calibrate the crystal at a few temperatures and interpolate the drift between these temperatures. To do this, I don't need accurate (just repeatable) temperature measurement, so I'll to use the Teensy's own internal temperature measurement (ADC channel 0x26); I just need to keep the crystal and MCU temperature coupled.

If anyone else is doing this with a rubidium (or OCXO) oscillator, you'll find that those signals are typically < 1 V p-p sinusoids. It is possible to use the Teensy's internal comparator to square up those signals and drive the 'normal' input of the FrequencyCounter (pin 13). Comparator1 can take an input on Teensy pin 23 (A9), and output it squared up on pin 10. The comparator works (not guaranteed) to 25 MHz for me. You just connect pin 10 to pin 13, and connect the oscillator (via 1 kohm; probably not really necessary) to pin 23. Here's that code for reference:

Code:
void ComparatorSetup(int threshold) { // threshold 0..63
  // Output is on pin10
  // input is pin A9 (core pin 23)
  // reference is 6-bit internal DAC
  // DAC reference VIN1 = VREF (1.19 V), VIN2 = VDD (3.3)
  
  SIM_SCGC4 |= SIM_SCGC4_CMP; //Clock to Comparator
  CORE_PIN10_CONFIG = PORT_PCR_MUX(6); //Alternate function 6: Teensy pin10 = CMP1_OUT, PTC4

  CMP1_CR0 = 0b00000000; // FILTER_CNT=0; HYSTCTR=0
  CMP1_CR1 = 0b00010111; // SE=0, high power, COUTA, output pin, enable; mode #2A
  // read CMP1_SCR LSB is analog comparator output state
  CMP1_DACCR = 0b11000000+threshold; // enable DAC, VIN2 selected (=VDD), reference = threshold*VDD/64 -- 19 suits X72
  if (threshold==0) CMP1_DACCR = 0; //Disable DAC ==> reference = 0 V
  //PTC2 (pin 23) is default CMP1_IN0
  CMP1_MUXCR = 0b01000111; // Input pins select; plus = IN0 (pin 23), neg = DAC (code 7) 
}
 
I've had good luck using an TXCO on either the MCU crystal as well as RTC crystal inputs. I wonder to what extent you can't simply mux a TXCO signal among the many RTC inputs. Or similarly share a GPS 1hz output signal as a reference for all units to synchronize.
 
I'm trying to do it all with no (minimal) additional hardware. Basically, if I can couple the crystal and MCU's temperature, I'll have a (digitally-controlled) TCXO, and a frequency counter that just requires a Teensy, a couple of resistors and a capacitor.
 
Ah. Did that a while ago. See my TXCO hilarity thread. You should be able to achieve 0.5ppm accuracy if you stay within the 0-40*C range and use a good thermometer like the DS18b20. The internal MCU thermometer might work well enough, however.

While I developed a library to do all that stuff I eventually transitioned to a hardware solution. It's one less ball to juggle in the loop. I use a TXCO oscillator for the RTC that I still plan on correcting a little using a GPS reference. Good for 5 ppm out of the box and likely good for 1ppm or better with minimal temperature corrections.
 
Last edited:
When making accuracy measurements, I find it is useful to have another reference to compare with.

One simple project I made is a 60 KHz TRF receiver (a ferrite loopstick tuned circuit with an FET first stage, followed by 3 stages of op amps). This gives me the UK time signal (USA = WWVB on 60 KHz) which has a carrier stable to 2 parts in 10e12. Though the signal is generally noisy, you can easily see each cycle of the 60 KHz carrier and you can then calibrate a 10 MHz OCXO against this - divide by 1000 divider chips to give 10 KHz and use this as a scope trigger. When the OCXO is tuned to about 1ppb, the drift of each 60 KHz cycle will be one quarter cycle in about 1 hour and 9 minutes (OCXO is slow if 60 KHz drifts to the left).

Another useful UK signal is BBC Digital Audio (DAB) broadcast on 225.648 MHz. This is locked to GPS, so a good digital radio receiver can pick up the vestigal carrier mixed in the spectrum. Compare this against the 23rd harmonic of a 10 MHz OCXO. I reckon it can be adjusted to better than 5ppb.
 
Status
Not open for further replies.
Back
Top