GPS Disciplined Direct Digital Synthesisor
Below is some code I cobbled together to produce GPS lock a Silicon Labs DDS. This was inspired by a need George M1GEO had for a frequency stable DDS. This work is a derivative of the code found here, and adapted to suit our needs and the DDS we had to hand.
#include <Wire.h>
#include <si5351.h>
#include <avr/interrupt.h>
#include <avr/io.h>
// Set up MCU pins
#define ppsPin 2
#define Resolution 6
#define Offset A2
unsigned int tcount = 2;
unsigned long MeasuredFreq = 250000000;
unsigned long TestFreq= 250000000;
unsigned long mult = 0, Freq_1, Freq_2;
static bool s_GpsOneSecTick = false;
int32_t currentCal = 0;
Si5351 si5351;
void PPSinterrupt() {
s_GpsOneSecTick = true; // New second by GPS.
tcount++;
if (tcount == 4) // Start counting the 2.5 MHz $
{
TCCR1B = 7; //Clock on rising edge of pin 5
}
else if (tcount == 14) //The 40 second gate time$
{
Serial.println("mult = "+mult);
TCCR1B = 0; //Turn off counter
MeasuredFreq = mult * 0x10000 + TCNT1; //Calculate correction factor
MeasuredFreq *= 10;
TCNT1 = 0; //Reset count to zero
mult = 0;
tcount = 0; //Reset the seconds counter
}
}
// Timer 1 overflow intrrupt vector.
ISR(TIMER1_OVF_vect)
{
mult++; //Increment multiplier
TIFR1 = (1 << TOV1); //Clear overlow flag
}
void setup() {
Serial.begin(9600);
Serial.println("Initialising");
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
si5351.set_freq(TestFreq, SI5351_CLK0);
//Set up Timer1 as a frequency counter - input at pin 5
TCCR1B = 0; //Disable Timer5 during setup
TCCR1A = 0; //Reset
TCNT1 = 0; //Reset counter to zero
TIFR1 = 1; //Reset overflow
TIMSK1 = 1; //Turn on overflow flag
// Inititalize GPS 1pps input
pinMode(ppsPin, INPUT);
// Set 1PPS pin 2 for external interrupt input
attachInterrupt(digitalPinToInterrupt(ppsPin), PPSinterrupt, RISING);
}
void loop() {
int64_t offset = (MeasuredFreq - TestFreq) * 1e3;
int32_t ppb = TestFreq/1e6;
ppb = (int32_t) offset / ppb;
if (s_GpsOneSecTick) {
s_GpsOneSecTick = false;
if (tcount == 1) {
Serial.println("Test Freq:");
Serial.println(TestFreq);
Serial.println("Xtal Measured at:");
Serial.println(MeasuredFreq);
Serial.println("ppb");
Serial.println(ppb);
// Update the SI5351A after every GPS correction
currentCal += ppb;
si5351.set_correction(currentCal, SI5351_PLL_INPUT_XO);
si5351.set_freq(TestFreq, SI5351_CLK0);
}
} else {
}
}