Crystal Deviations

A discussion that pops up in the Arduino forum every once in a while is the stability and precision of Arduino’s clock. The simple but expensive solution is to use a properly calibrated frequency counter or a DSO. Surprisningly the question can be investigated as well with the Blinkenlight shield and two Arduinos. I prefer a setup with two Arduinos and two Blinkenlight shields. If you have only one Blinkenlight shield you can conduct this experiment as well although with a little bit less convenience.

This experiment is somewhat more tricky than the experiments before. So lets start slowly. The idea is to turn the Blinkenlight shield into a low fidelity oscilloscope. If you do not know what an oscilloscope is you definitely should read the Wikipedia article about it. You may also want to look at some other sources. My short summary is that an oscilloscope is used to visualize waveforms. This is usually achieved by sweeping a „beam“ along the X axis and modulating the Y position with the signal’s amplitude.

Since the Blinkenlight shield has only one dimension the approach will be slightly different. Instead of modulating the Y position I will modulate the LED brightness.
So now let’s look at the sketch and the setup and then figure out the rest. Notice that the sketch must be loaded into both Arduinos.

//
//  www.blinkenlight.net
//
//  Copyright 2011 Udo Klein
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see http://www.gnu.org/licenses/


#include <avr/io.h>

//#define LEDstate(LED, phase) (LED<=phase)? 1: 0)
#define LEDstate(LED, phase) (LED==phase? 1: 0)

#define set_phase(phase) \
    PORTD = \
        (LEDstate( 0, phase)   ) + \
        (LEDstate( 1, phase)<<1) + \
        (LEDstate( 2, phase)<<2) + \
        (LEDstate( 3, phase)<<3) + \
        (LEDstate( 4, phase)<<4) + \
        (LEDstate( 5, phase)<<5) + \
        (LEDstate( 6, phase)<<6) + \
        (LEDstate( 7, phase)<<7);  \
    PORTB = \
        (LEDstate( 8, phase)   ) + \
        (LEDstate( 9, phase)<<1) + \
        (LEDstate(10, phase)<<2) + \
        (LEDstate(11, phase)<<3) + \
        (LEDstate(12, phase)<<4) + \
        (LEDstate(13, phase)<<5);  \
    PORTC = \
        (LEDstate(14, phase)   ) + \
        (LEDstate(15, phase)<<1) + \
        (LEDstate(16, phase)<<2) + \
        (LEDstate(17, phase)<<3) + \
        (LEDstate(18, phase)<<4) + \
        (LEDstate(19, phase)<<5);

    // Each port assignment will take only 3 cycles.
    // 1 cycle per port because the compiler will
    // hold suitable values in the registers and
    // thus use the "out" statement that will take
    // only 1 cycle (instead of the slower store
    // statements)
    // The goto statement requires 2 cycles.
    // --> 20*3+2 = 62 cycles per pass
    // --> 258.064 kHz scan frequency (for a CPU clock of 16 MHz)

    // With other words: a clock deviation of 1ppm will shift
    // the phase by one period within 3,875 seconds. Since
    // a period passes through 20 LEDs this will result in a shift
    // of ~5 tics per second which is easily visible.

    // A clock deviation of 1Hz (=1/16ppm) will shift the phase
    // by ~1 tic in 3 seconds. Still easily visible.

    // A clock deviation of 100ppm will shift the phase
    // within 0,03875s or with 25.8 Hz which is only visible as
    // a flicker. If such high deviations are to be detected
    // the period length of a cycle has to be multiplied by 10.
    // This is achieved by using divider10 instead of divider1.

void setup() {
    DDRD = 0b11111111; // set digital  0- 7 to output
    DDRB = 0b00111111; // set digital  8-13 to output
    DDRC = 0b00111111; // set digital 14-19 to output (coincidences with analog 0-5)

    cli();  // disable any interrupts to avoid phase jitter
}

void loop() {
    l0: set_phase( 0);
        set_phase( 1);
        set_phase( 2);
        set_phase( 3);
        set_phase( 4);
        set_phase( 5);
        set_phase( 6);
        set_phase( 7);
        set_phase( 8);
        set_phase( 9);
        set_phase(10);
        set_phase(11);
        set_phase(12);
        set_phase(13);
        set_phase(14);
        set_phase(15);
        set_phase(16);
        set_phase(17);
        set_phase(18);
        set_phase(19);  goto l0;
}

This sketch uses macros like in the Knight Rider example in order to generate pulses as fast as possible. It will always pull one IO pin up and all other pins down. Then it will pull the next IO pin up and all others down. It will repeat so indefinitely. If you would disassemble the generated machine code you would find that each set_phase macro will resolve into 3 „OUT“ statements. Each macro will use 3 cycles. The goto will take another 2 cycles. Hence the whole sequence will repeat every 62 cycles. Since the Arduino runs at 16 Mhz the pulse frequency of each pin will be 16/62 Mhz = 258.0645 kHz. The pins will be high 3/62 of the time except for pin 20 which will be high 5/62 of the time. Effectively this is a very fast Knight Rider effect.

So this establishes the sweep along the X axis. If you look at the Blinkenlight shield right now you will see that it is somewhat dimmer as usual. This is because of the only 5% duty cycle of the LEDs. You will also notice no flicker. This is due to the high sweep frequency.
Now we need to modulate this somehow. We will do so by disconnecting the common cathode jumper of the shield. We will then connect the common cathode to one of the IO pins of the other Arduino.

Blinkenlight Shield Schematic

Blinkenlight Shield Schematic

That is we connect Pin 2 of Jumper 1 to one of the IO pins of the other Arduino.

What would happen if both Arduinos are 100% in sync? Let’s try it. But where to get an Arduino that is 100% in sync? Well, replace the second by the first. That is connect the common cathode to Pin 0 of the same Arduino. What you will then see is that all LEDs but LED 0 will be lit.

What would happen if both Arduinos are in sync with regard to frequency but started up at slightly different times? Again loopback the first Arduino but this time use a different pin.

Why are all but one LEDs lit up? As I said the pins will be high with ~5% duty cycle and low with ~95%. However a LED will only light up if the cathode is low and the anode is high. For the LED connected to only one pin this implies that it will be always dark. For all other LEDs it implies that they will receive power if their IO pin is high. This is because at this time all other IO pins will be low. Hence the IO pin connected to the cathode will be low as well and thus the LED will light up briefly.

//
//	www.blinkenlight.net
//
//	Copyright 2011 Udo Klein
//
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program. If not, see http://www.gnu.org/licenses/

#include

#define LEDstate(LED, phase) (LED==phase? 1: 0)

#define set_phase(phase) \
	PORTD = \
		(LEDstate( 0, phase)   ) + \
		(LEDstate( 1, phase)<		(LEDstate( 2, phase)<		(LEDstate( 3, phase)<		(LEDstate( 4, phase)<		(LEDstate( 5, phase)<		(LEDstate( 6, phase)<		(LEDstate( 7, phase)<	PORTB = \
		(LEDstate( 8, phase)   ) + \
		(LEDstate( 9, phase)<		(LEDstate(10, phase)<		(LEDstate(11, phase)<		(LEDstate(12, phase)<		(LEDstate(13, phase)<	PORTC = \
		(LEDstate(14, phase)   ) + \
		(LEDstate(15, phase)<		(LEDstate(16, phase)<		(LEDstate(17, phase)<		(LEDstate(18, phase)<		(LEDstate(19, phase)<								   \
		delay(200);

void setup() {
	DDRD = 0b11111111; // set digital  0- 7 to output
	DDRB = 0b00111111; // set digital  8-13 to output
	DDRC = 0b00111111; // set digital 14-19 to output (coincidences with analog 0-5)
}

void loop() {
	l0:	set_phase( 0);
		set_phase( 1);
		set_phase( 2);
		set_phase( 3);
		set_phase( 4);
		set_phase( 5);
		set_phase( 6);
		set_phase( 7);
		set_phase( 8);
		set_phase( 9);
		set_phase(10);
		set_phase(11);
		set_phase(12);
		set_phase(13);
		set_phase(14);
		set_phase(15);
		set_phase(16);
		set_phase(17);
		set_phase(18);
		set_phase(19);	goto l0;
}

But now back to the full speed sketch and the two Arduino setup. You should now notice that the dark spot is moving slowly.

If you can not see this effect then read on. I will cover this – and the corresponding fix – in part two of the experiment. Actually for my two Arduinos the experiment fails thus there is no video for part 1. Still chances are that you can see the effect.
So what is going on? Well, the two Arduinos are not 100% in sync. Thus the moving pattern. For an in depth explanation you may want to read up the Wikipedia Article on „Moiré pattern“.

In the end it boils down that we feed each LED with a signal of frequency of ~ 258.0645 kHz. The LED acts like a comparator thus mixing the signals. The (light) output is the product of two frequency shifted signals. One frequency being the sum ~516.129 kHz and the other the difference ~0Hz. The 512kHz signal is an ultra fast PWM and thus invisible. The difference is close to 0 Hz and thus visible as the LED blinking slowly. Since the 20 LEDs are phase shifted by 1/20 phase the „blinks“ will be phase shifted as well. Thus the dark spot appears to be moving.

More mathematical inclined readers may notice that I am somewhat cheating. After all standard theory always talks about sinusodial waves but we have rectangular pulses. If you go deeper into theory and look at the fourier transforms then you will see that the first approximation of the pulses are actually sinusodial with the same frequency. If this does not make sense to you ignore it or take it as a motivation to study natural sciences / engineering 😉 If it does make sense to you, recall the Nyquist Shannon sampling theorem and notice that the sampling frequency is to low for signal reconstruction. Still this approach does NOT violate the theorem since we never reconstruct the signal. Actually we exploit the aliasing effects that happen in this case.

The underlying theory is quantitative. With other words a frequency deviation of 1 Hz implies that our dot will cross all 20 LEDs once per second. A frequency deviation of 10 Hz would imply that it will cross the LEDs 10 times per second. A deviation of 0.1 Hz means it moves just two LEDs per second.

It is even possible to infer which Arduino is faster. If the Arduino with the shield is slower then the black dot will appear to move along LED 0, 1, 2, …, 19. Otherwise if the Arduino feeding the common cathode is slower the black dot will appear to move in the opposite direction.
Since osciallators are usually not rated in Hz but „parts per million“ here is a conversion table:

Deviation from 258.0645 kHz [Hz] Relative Deviation [ppm] Deviation from 16 Mhz [Hz]
0.016129 0.0625 1
0.025806 0.1 1.6
0.1 0.3875 6.2
0.16129 0.625 10
0.258065 1 16
1 3.875 62
1.61290 6.25 100
2.580645 10 160
10 38.75 620
16.12903 62.5 1000
25.806452 100 1600

As you can read from the table’s first colum we can easily spot a crystal deviation of 1ppm. Deviations below 0.1 ppm can be spotted by enough patience or with a timelapse camera. On the other hand deviations beyond 100 ppm can not be easily spotted with this setup. In order to achieve this we need to lower the sweep frequency.

After all this theory we can now play around with this setup? What happens if you put a finger on the crystal? What happens if you touch the crystal’s pins with a pencil? What happens if you hit the crystal slightly?

Some of the manipulations have clearly visible results other are harder to see. This is for several reasons. Two that can be easily fixed are: (1) the sweep frequency which is sometimes to high and (2) that it would be much easier to see a moving bright spot instead of a moving dark spot.

See part 2 of this experiment for further refinements on investigating crystal deviations.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.