├── README.md ├── board ├── board_1.pdf ├── board_2.pdf ├── bottomview.png └── topview.png ├── firmware ├── adc.c ├── adc.h ├── dac.c ├── dac.h ├── fifo.c ├── fifo.h ├── fp64lib.h ├── gps.c ├── gps.h ├── lcd.c ├── lcd.h ├── led.c ├── led.h ├── main.c ├── main.h ├── spi.c ├── spi.h ├── tim32.c ├── tim32.h └── tim32_stop_start_cycles_analysis.asm ├── measurements ├── long term stability │ └── todo.txt └── short term stability │ ├── data.dat │ ├── fswp_1_noise_spectrum_Sphi.png │ ├── fswp_2_allan_deviation.png │ ├── fswp_3_noise_spectrum_Sv.png │ ├── fswp_4_noise_spectrum_Sy.png │ └── fswp_screenshot.png ├── pictures ├── back.jpg ├── front_off.jpg ├── front_on_heating_1.jpg ├── front_on_heating_2.jpg ├── front_on_locked.jpg ├── front_on_measuring.jpg ├── front_on_ocxo_stable.jpg ├── ocxo.jpg ├── open_1.jpg ├── open_2.jpg ├── u-center_1.jpg └── u-center_2.jpg └── schematics └── schematics.pdf /README.md: -------------------------------------------------------------------------------- 1 | # GPSDO 2 | A Global Positioning System (GPS) Disciplined Oscillator (DO) 3 | 4 |

5 | 6 |

7 | 8 |

9 | 10 |

11 | 12 | # Video 13 | [![Video @ YouTube](http://img.youtube.com/vi/EQjGhx4-qjA/0.jpg)](http://www.youtube.com/watch?v=EQjGhx4-qjA "10 MHz GPS Disciplined OCXO based on u-blox NEO-7N Chip") 14 | 15 | # Schematics 16 | Please see subdirectory *schematics*. 17 | 18 | # Board 19 | The PCB was designed in Autodesk EAGLE 9.6.0 and manufactured by [JLCPCB](https://jlcpcb.com/). 20 | 21 |

22 | 23 |

24 | 25 |

26 | 27 |

28 | 29 |

30 | 31 |

32 | 33 |

34 | 35 |

36 | 37 | # Software 38 | The microcontroller is programmed in C using Atmel Studio 7.0.1931 and compiled with avr-gcc as part of the avr8-gnu-toolchain. 39 | 40 | # Operation 41 |

42 | 43 |

44 | 45 |

46 | 47 |

48 | 49 |

50 | 51 |

52 | 53 |

54 | 55 |

56 | 57 |

58 | 59 |

60 | 61 | # GPS Status 62 |

63 | 64 |

65 | 66 |

67 | 68 |

69 | 70 | # OCXO 71 |

72 | 73 |

74 | 75 | # Short-Term Stability 76 |

77 | 78 |

79 | -------------------------------------------------------------------------------- /board/board_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/board/board_1.pdf -------------------------------------------------------------------------------- /board/board_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/board/board_2.pdf -------------------------------------------------------------------------------- /board/bottomview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/board/bottomview.png -------------------------------------------------------------------------------- /board/topview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/board/topview.png -------------------------------------------------------------------------------- /firmware/adc.c: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: ADC (internal) driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #include "main.h" 8 | 9 | void adc_init(void) 10 | { 11 | /* AVCC with external capacitor at AREF pin */ 12 | ADMUX &= ~(1 << REFS1); 13 | ADMUX |= (1 << REFS0); 14 | 15 | /* clock prescaler: 128 -> 10 MHz / 128 = 78.125 kHz */ 16 | ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); 17 | } 18 | 19 | uint16_t adc_get_value(uint8_t channel) 20 | { 21 | if (channel == ADC_CHANNEL_OCXO_CURRENT) { 22 | ADMUX = 0b01001101; /* differential input: ADC3-ADC2 */ 23 | } 24 | else if (channel == ADC_CHANNEL_OCXO_VCTRL) { 25 | ADMUX = 0b01000001; /* single ended input: ADC1 */ 26 | } 27 | 28 | /* enable ADC */ 29 | ADCSRA |= (1 << ADEN); 30 | 31 | /* start conversion and wait until conversion is finished */ 32 | ADCSRA |= (1 << ADSC); 33 | while (ADCSRA & (1 << ADSC)); 34 | 35 | /* read ADC value */ 36 | uint16_t adc_value = 0; 37 | adc_value = ADCL; 38 | adc_value += (ADCH << 8); 39 | 40 | /* disable ADC */ 41 | ADCSRA &= ~(1 << ADEN); 42 | 43 | /* return read value */ 44 | return adc_value; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /firmware/adc.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: ADC (internal) driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #ifndef ADC_H 8 | #define ADC_H 9 | 10 | #include "main.h" 11 | 12 | #define ADC_CHANNEL_OCXO_CURRENT 0 13 | #define ADC_CHANNEL_OCXO_VCTRL 1 14 | 15 | #define ADC_DIFF_GAIN 10 16 | #define ADC_RESOLUTION 1024 17 | #define ADC_VREF 5 18 | #define ADC_VADC2 2.481 19 | #define ADC_VLSB ADC_VREF/1024 20 | #define ADC_VALUE_TO_OCXO_CURRENT_SE(x) ((float)(x-(ADC_RESOLUTION/2))) * ADC_VLSB / ACS712_FACTOR; 21 | #define ADC_VALUE_TO_OCXO_CURRENT_DIFF(x) (((((float)(x)) / (ADC_DIFF_GAIN * (ADC_RESOLUTION/2) / ADC_VREF)) + ADC_VADC2) - ACS712_VOFFSET) / ACS712_FACTOR; 22 | #define ADC_VALUE_TO_OCXO_VCTRL_SE(x) ((float)(x)) * ADC_VLSB; 23 | 24 | extern void adc_init(void); 25 | extern uint16_t adc_get_value(uint8_t channel); 26 | 27 | #endif -------------------------------------------------------------------------------- /firmware/dac.c: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: DAC (AD5541) driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #include "main.h" 8 | 9 | void dac_init(void) 10 | { 11 | /* set nCS pin as output */ 12 | DAC_DDR_nCS |= (1 << DAC_PIN_nCS); 13 | 14 | /* disable SPI communication */ 15 | dac_spi_disable(); 16 | } 17 | 18 | void dac_spi_enable(void) 19 | { 20 | DAC_PORT_nCS &= ~(1 << DAC_PIN_nCS); 21 | } 22 | 23 | void dac_spi_disable(void) 24 | { 25 | DAC_PORT_nCS |= (1 << DAC_PIN_nCS); 26 | } 27 | 28 | void dac_set_voltage(float voltage) 29 | { 30 | uint16_t D = (uint16_t)(voltage * ((float)DAC_RESOLUTION) / ((float)DAC_VREF)); 31 | dac_write(D); 32 | } 33 | 34 | void dac_set_digital_value(uint16_t value) 35 | { 36 | dac_write(value); 37 | } 38 | 39 | void dac_write(uint16_t data) 40 | { 41 | dac_spi_enable(); 42 | spi_write((data >> 8) & 0xff); 43 | spi_wait_for_transmission_complete(); 44 | spi_write(data & 0x00ff); 45 | spi_wait_for_transmission_complete(); 46 | dac_spi_disable(); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /firmware/dac.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: DAC (AD5541) driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #ifndef DAC_H 8 | #define DAC_H 9 | 10 | #include "main.h" 11 | 12 | #define DAC_DDR_nCS DDRB 13 | #define DAC_PORT_nCS PORTB 14 | #define DAC_PIN_nCS 3 15 | 16 | #define DAC_VREF 5 17 | #define DAC_RESOLUTION 65536 18 | #define DAC_VLSB ((float)(DAC_VREF/DAC_RESOLUTION)) 19 | 20 | extern void dac_init(void); 21 | extern void dac_spi_enable(void); 22 | extern void dac_spi_disable(void); 23 | extern void dac_set_voltage(float voltage); 24 | extern void dac_set_digital_value(uint16_t value); 25 | extern void dac_write(uint16_t data); 26 | 27 | #endif -------------------------------------------------------------------------------- /firmware/fifo.c: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: FIFO ringbuffer with averaging. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #include "main.h" 8 | 9 | float64_t fifo_data[FIFO_MAX_SIZE]; 10 | uint16_t fifo_index; 11 | uint16_t fifo_current_size; 12 | 13 | void fifo_init(void) 14 | { 15 | /* initialize fifo */ 16 | for (uint16_t i = 0; i < FIFO_MAX_SIZE; i++) { 17 | fifo_data[i] = 0; 18 | } 19 | 20 | /* point on first element */ 21 | fifo_index = 0; 22 | 23 | /* fifo still empty */ 24 | fifo_current_size = 0; 25 | } 26 | 27 | void fifo_add(float64_t data) 28 | { 29 | /* add data at current position */ 30 | fifo_data[fifo_index] = data; 31 | 32 | /* point on next position */ 33 | fifo_index++; 34 | 35 | /* point to the start if the end is reached */ 36 | if (fifo_index == FIFO_MAX_SIZE) 37 | fifo_index = 0; 38 | 39 | /* increase current fifo size */ 40 | if (fifo_current_size < FIFO_MAX_SIZE) 41 | fifo_current_size++; 42 | } 43 | 44 | float64_t fifo_get_mean(void) 45 | { 46 | float64_t mean = fp64_sd(0.0); 47 | for (uint16_t i = 0; i < fifo_current_size; i++) { 48 | mean = fp64_add(mean, fifo_data[i]); 49 | } 50 | return fp64_div(mean, fp64_sd((float)fifo_current_size)); 51 | } 52 | -------------------------------------------------------------------------------- /firmware/fifo.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: FIFO ringbuffer with averaging. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #ifndef FIFO_H 8 | #define FIFO_H 9 | 10 | #include "main.h" 11 | 12 | #define FIFO_MAX_SIZE (MAIN_AVERAGING_TIME_IN_MINUTES*60/MAIN_GATE_TIME_IN_SECONDS) 13 | 14 | extern void fifo_init(void); 15 | extern void fifo_add(float64_t data); 16 | extern float64_t fifo_get_mean(void); 17 | 18 | #endif -------------------------------------------------------------------------------- /firmware/fp64lib.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2019-2020 Uwe Bissinger 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in 11 | the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of the copyright holders nor the names of 14 | contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. */ 28 | 29 | /* $Id$ */ 30 | 31 | /* IEEE float format single 32 | Bit 31: sign 0 = + / 1 = - 33 | Bits 30-23: exponent -126 <= e <= 127, base 127 34 | Bits 23-0: mantissa 35 | 31 27 23 19 15 11 7 3 36 | 7 4 3 0 7 4 3 0 7 4 3 0 7 4 3 0 37 | SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM 38 | 39 | IEEE float format double 40 | Bit 63: sign S 0 = + / 1 = - 41 | Bits 62-51: exponent E -1022 <= e <= 1023, base 1023 42 | Bits 51-0: mantissa M without leading 1 bit at 52 43 | 63 59 55 51 47 43 ... 7 3 44 | 7 4 3 0 7 4 3 0 7 4 3 0 ... 7 4 3 0 45 | SEEE EEEE EEEE MMMM MMMM MMMM ... MMMM MMMM 46 | 47 | Interpretations: 48 | E = 0 M = 0 --> +/- 0 (zero) 49 | E = 0 M > 0 --> +/- 0,M*2^(-1022) denormalized numbers 50 | 0= 0 --> +/- 1,M*2^(E-1023) normal numbers 51 | E = 2047 M = 0 --> +/- Inf infinity 52 | E = 2047 M > 0 --> NaN not a number 53 | 54 | fp64lib functions do not raise exceptions and do not change the 55 | errno variable. Therefore the majority of them are declared 56 | with const attribute as "do not examine any values except their 57 | arguments, and have no effects except the return value", for better 58 | optimization by GCC. 59 | */ 60 | 61 | #ifndef fp64lib_h_included 62 | #define fp64lib_h_included 63 | 64 | #ifdef __cplusplus 65 | extern "C"{ 66 | #endif 67 | 68 | #ifndef __ATTR_CONST__ 69 | # define __ATTR_CONST__ __attribute__((__const__)) 70 | #endif 71 | 72 | #include 73 | #include 74 | #include 75 | 76 | typedef uint64_t float64_t; /* IEEE 754 double precision floating point number */ 77 | typedef float float32_t; /* IEEE 754 single precision floating point number */ 78 | 79 | #define float64_EULER_E ((float64_t)0x4005bf0a8b145769LLU) // 2.7182818284590452 80 | #define float64_NUMBER_PI ((float64_t)0x400921fb54442d18LLU) // 3.1415926535897932 81 | #define float64_NUMBER_PIO2 ((float64_t)0x3ff921fb54442d18LLU) // 3.1415926535897932/2 82 | #define float64_NUMBER_2PI ((float64_t)0x401921fb54442d18LLU) // 3.1415926535897932*2 83 | #define float64_NUMBER_ONE ((float64_t)0x3ff0000000000000LLU) // 1.0 84 | #define float64_MINUS_NUMBER_ONE ((float64_t)0xbff0000000000000LLU) // 1.0 85 | #define float64_NUMBER_PLUS_ZERO ((float64_t)0x0000000000000000LLU) // 0.0 86 | #define float64_ONE_POSSIBLE_NAN_REPRESENTATION ((float64_t)0x7fffffffffffffffLLU) // NaN 87 | #define float64_PLUS_INFINITY ((float64_t)0x7ff0000000000000LLU) // +INF 88 | #define float64_MINUS_INFINITY ((float64_t)0xfff0000000000000LLU) // -INF 89 | 90 | /* return values for fpclassify */ 91 | #define FP_INFINITE 2 92 | #define FP_NAN 1 93 | #define FP_NORMAL 4 94 | #define FP_SUBNORMAL 3 95 | #define FP_ZERO 0 96 | 97 | // basic mathematic functions 98 | float64_t fp64_add( float64_t a, float64_t b ) __ATTR_CONST__; 99 | float64_t fp64_sub( float64_t a, float64_t b ) __ATTR_CONST__; 100 | float64_t fp64_mul( float64_t a, float64_t b ) __ATTR_CONST__; 101 | float64_t fp64_div( float64_t a, float64_t b ) __ATTR_CONST__; 102 | float64_t fp64_fmod( float64_t a, float64_t b ) __ATTR_CONST__; 103 | float64_t fp64_fmodn( float64_t a, float64_t b, unsigned long *np ) __ATTR_CONST__; 104 | 105 | // isXXX & compare functions 106 | int fp64_classify( float64_t x ) __ATTR_CONST__; 107 | int fp64_isinf( float64_t x) __ATTR_CONST__; 108 | int fp64_isnan( float64_t x) __ATTR_CONST__; 109 | int fp64_isfinite( float64_t x) __ATTR_CONST__; 110 | int8_t fp64_compare( float64_t x, float64_t b) __ATTR_CONST__; 111 | int fp64_signbit (float64_t x) __ATTR_CONST__; 112 | float64_t fp64_fmin(float64_t x, float64_t y) __ATTR_CONST__; 113 | float64_t fp64_fmax(float64_t x, float64_t y) __ATTR_CONST__; 114 | 115 | // basic functions with 1 argument 116 | float64_t fp64_neg( float64_t x ) __ATTR_CONST__; 117 | float64_t fp64_abs( float64_t x ) __ATTR_CONST__; 118 | float64_t fp64_inverse( float64_t x ) __ATTR_CONST__; 119 | float64_t fp64_sqrt( float64_t x ) __ATTR_CONST__; 120 | float64_t fp64_square( float64_t x ) __ATTR_CONST__; 121 | float64_t fp64_trunc( float64_t x ) __ATTR_CONST__; 122 | float64_t fp64_cut_noninteger_fraction( float64_t x ) __ATTR_CONST__; //alias to fp64_trunc 123 | float64_t fp64_ceil( float64_t x ) __ATTR_CONST__; 124 | float64_t fp64_floor( float64_t x ) __ATTR_CONST__; 125 | float64_t fp64_round( float64_t x ) __ATTR_CONST__; 126 | long fp64_lround( float64_t A ) __ATTR_CONST__; 127 | long fp64_lrint( float64_t A ) __ATTR_CONST__; 128 | float64_t fp64_cbrt( float64_t x ) __ATTR_CONST__; 129 | 130 | // trigonometric functions 131 | float64_t fp64_sin( float64_t x ) __ATTR_CONST__; 132 | float64_t fp64_cos( float64_t x ) __ATTR_CONST__; 133 | float64_t fp64_tan( float64_t x ) __ATTR_CONST__; 134 | float64_t fp64_cotan( float64_t x ) __ATTR_CONST__; 135 | float64_t fp64_atan( float64_t x ) __ATTR_CONST__; 136 | float64_t fp64_asin( float64_t x ) __ATTR_CONST__; 137 | float64_t fp64_acos( float64_t x ) __ATTR_CONST__; 138 | float64_t fp64_log( float64_t x ) __ATTR_CONST__; 139 | float64_t fp64_exp( float64_t x ) __ATTR_CONST__; 140 | float64_t fp64_log10( float64_t x ) __ATTR_CONST__; 141 | float64_t fp64_log2( float64_t x ) __ATTR_CONST__; 142 | float64_t fp64_exp10( float64_t x ) __ATTR_CONST__; 143 | float64_t fp64_pow10( float64_t x ) __ATTR_CONST__; 144 | float64_t fp64_sinh( float64_t x ) __ATTR_CONST__; 145 | float64_t fp64_cosh( float64_t x ) __ATTR_CONST__; 146 | float64_t fp64_tanh( float64_t x ) __ATTR_CONST__; 147 | float64_t fp64_fmodx_pi2( float64_t x, unsigned long *np ) __ATTR_CONST__; 148 | 149 | // functions with 2 arguments 150 | float64_t fp64_ldexp( float64_t x, int exp ) __ATTR_CONST__; 151 | float64_t fp64_frexp( float64_t x, int *pexp ) __ATTR_CONST__; 152 | float64_t fp64_fdim( float64_t A, float64_t B ) __ATTR_CONST__; 153 | float64_t fp64_pow( float64_t x, float64_t y ) __ATTR_CONST__; 154 | float64_t fp64_hypot( float64_t x, float64_t y ) __ATTR_CONST__; 155 | float64_t fp64_atan2( float64_t y, float64_t x ) __ATTR_CONST__; 156 | float64_t fp64_modf (float64_t x, float64_t *iptr); 157 | 158 | // functions with 3 arguments 159 | float64_t fp64_fma (float64_t A, float64_t B, float64_t C) __ATTR_CONST__; 160 | 161 | // conversion functions 162 | float64_t fp64_int64_to_float64( long long x ) __ATTR_CONST__; // (signed) long long to float64_t 163 | float64_t fp64_int32_to_float64( long x) __ATTR_CONST__; // (signed) long to float64_t 164 | float64_t fp64_long_to_float64( long x ) __ATTR_CONST__; // alias to fp64_int32_to_float64 165 | float64_t fp64_uint16_to_float64( uint16_t x ) __ATTR_CONST__; // unsigned short to float64_t 166 | float64_t fp64_int16_to_float64( int16_t x) __ATTR_CONST__; // (signed) short to float64_t 167 | 168 | float64_t fp64_uint64_to_float64( unsigned long long x ) __ATTR_CONST__;// unsigned long long to float64_t 169 | float64_t fp64_uint32_to_float64( unsigned long x ) __ATTR_CONST__; // unsigned long to float64_t 170 | 171 | long long fp64_to_int64( float64_t A) __ATTR_CONST__; // float_t to int64_t 172 | long fp64_to_int32( float64_t A) __ATTR_CONST__; // float_t to int32_t 173 | int fp64_to_int16( float64_t A) __ATTR_CONST__; // float_t to int16_t 174 | char fp64_to_int8( float64_t A) __ATTR_CONST__; // float_t to int8_t 175 | long fp64_float64_to_long( float64_t A) __ATTR_CONST__; // alias to fp64_to_int32 176 | 177 | unsigned long long fp64_to_uint64( float64_t A) __ATTR_CONST__; // float_t to uint64_t 178 | unsigned long fp64_to_uint32( float64_t A) __ATTR_CONST__; // float_t to uint32_t 179 | unsigned int fp64_to_uint16( float64_t A) __ATTR_CONST__; // float_t to uint16_t 180 | unsigned char fp64_to_uint8( float64_t A) __ATTR_CONST__; // float_t to uint8_t 181 | 182 | float64_t fp64_sd( float x ) __ATTR_CONST__; // float to float64_t 183 | float fp64_ds( float64_t x ) __ATTR_CONST__; // float64_t to float 184 | 185 | // to and from string 186 | char *fp64_to_decimalExp( float64_t x, uint8_t maxDigits, uint8_t expSep, int16_t *exp10 ); 187 | char *fp64_to_string( float64_t x, uint8_t max_nr_chars, uint8_t max_leading_mantisse_zeros ) __ATTR_CONST__; 188 | float64_t fp64_atof( char *str ); 189 | float64_t fp64_strtod( char *str, char **endptr ); 190 | 191 | #include "fp64def.h" 192 | 193 | #ifdef __cplusplus 194 | } // extern "C" 195 | #endif 196 | 197 | #endif 198 | -------------------------------------------------------------------------------- /firmware/gps.c: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: GPS (u-blox NEO-7N) driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #include "main.h" 8 | 9 | GPS_INTERRUPT_EDGE_t gps_interrupt_edge = GPS_INTERRUPT_EDGE_UNDEFINED; 10 | 11 | void gps_init(void) 12 | { 13 | /* set nRESET pin as output */ 14 | GPS_DDR_nRESET |= (1 << GPS_PIN_nRESET); 15 | 16 | /* set TIMEPULSE pin as input */ 17 | GPS_DDR_TIMEPULSE &= ~(1 << GPS_PIN_TIMEPULSE); 18 | 19 | /* set nCS pin as output */ 20 | GPS_DDR_nCS |= (1 << DAC_PIN_nCS); 21 | 22 | /* enable GPS */ 23 | gps_enable(); 24 | } 25 | 26 | void gps_enable(void) 27 | { 28 | GPS_PORT_nRESET = GPS_PORT_nRESET | (1 << GPS_PIN_nRESET); 29 | } 30 | 31 | void gps_disable(void) 32 | { 33 | GPS_PORT_nRESET = GPS_PORT_nRESET & ~(1 << GPS_PIN_nRESET); 34 | } 35 | 36 | void gps_timepulse_interrupt_enable(GPS_INTERRUPT_EDGE_t edge) 37 | { 38 | if (edge == GPS_INTERRUPT_EDGE_RISING) { 39 | 40 | /* trigger rising edge */ 41 | EICRA |= (1 << ISC00); 42 | EICRA |= (1 << ISC01); 43 | gps_interrupt_edge = GPS_INTERRUPT_EDGE_RISING; 44 | } 45 | else if (edge == GPS_INTERRUPT_EDGE_FALLING) { 46 | 47 | /* trigger falling edge */ 48 | EICRA &= ~(1 << ISC00); 49 | EICRA |= (1 << ISC01); 50 | gps_interrupt_edge = GPS_INTERRUPT_EDGE_FALLING; 51 | } 52 | 53 | /* enable external interrupt INT0 */ 54 | EIMSK |= (1 << INT0); 55 | 56 | /* clear interrupt flag */ 57 | EIFR |= (1 << INT0); 58 | } 59 | 60 | void gps_timepulse_interrupt_disable(void) 61 | { 62 | /* disable external interrupt INT0 */ 63 | EIMSK &= ~(1 << INT0); 64 | gps_interrupt_edge = GPS_INTERRUPT_EDGE_UNDEFINED; 65 | } 66 | 67 | void gps_spi_enable(void) 68 | { 69 | GPS_PORT_nCS &= ~(1 << GPS_PIN_nCS); 70 | } 71 | 72 | void gps_spi_disable(void) 73 | { 74 | GPS_PORT_nCS |= (1 << GPS_PIN_nCS); 75 | } 76 | 77 | -------------------------------------------------------------------------------- /firmware/gps.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: GPS (u-blox NEO-7N) driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #ifndef GPS_H 8 | #define GPS_H 9 | 10 | #include "main.h" 11 | 12 | #define GPS_DDR_nRESET DDRB 13 | #define GPS_PORT_nRESET PORTB 14 | #define GPS_PIN_nRESET 0 15 | 16 | #define GPS_DDR_TIMEPULSE DDRD 17 | #define GPS_PORT_TIMEPULSE PORTD 18 | #define GPS_PIN_TIMEPULSE 2 19 | 20 | #define GPS_DDR_nCS DDRB 21 | #define GPS_PORT_nCS PORTB 22 | #define GPS_PIN_nCS 4 23 | 24 | typedef enum 25 | { 26 | GPS_INTERRUPT_EDGE_UNDEFINED, 27 | GPS_INTERRUPT_EDGE_RISING, 28 | GPS_INTERRUPT_EDGE_FALLING 29 | } GPS_INTERRUPT_EDGE_t; 30 | extern GPS_INTERRUPT_EDGE_t gps_interrupt_edge; 31 | 32 | extern void gps_init(void); 33 | extern void gps_enable(void); 34 | extern void gps_disable(void); 35 | extern void gps_timepulse_interrupt_enable(GPS_INTERRUPT_EDGE_t edge); 36 | extern void gps_timepulse_interrupt_disable(void); 37 | extern void gps_spi_enable(void); 38 | extern void gps_spi_disable(void); 39 | 40 | #endif -------------------------------------------------------------------------------- /firmware/lcd.c: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: LCD driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #include "main.h" 8 | 9 | static void lcd_enable(void) 10 | { 11 | LCD_PORT_E |= (1 << LCD_PIN_E); 12 | _delay_us(LCD_ENABLE_US); 13 | LCD_PORT_E &= ~(1 << LCD_PIN_E); 14 | } 15 | 16 | void lcd_init(void) 17 | { 18 | /* configure pins */ 19 | LCD_DDR_RS |= (1 << LCD_PIN_RS); 20 | LCD_DDR_RW |= (1 << LCD_PIN_RW); 21 | LCD_DDR_E |= (1 << LCD_PIN_E); 22 | LCD_DDR_DB = 0xff; 23 | 24 | LCD_PORT_RS &= ~(1 << LCD_PIN_RS); 25 | LCD_PORT_RW &= ~(1 << LCD_PIN_RW); 26 | LCD_PORT_E &= ~(1 << LCD_PIN_E); 27 | LCD_PORT_DB = 0x00; 28 | 29 | /* wait for bootup */ 30 | _delay_ms(LCD_BOOTUP_MS); 31 | 32 | /* software reset */ 33 | lcd_command(LCD_SOFT_RESET); 34 | _delay_ms(LCD_SOFT_RESET_MS1); 35 | lcd_enable(); 36 | _delay_ms(LCD_SOFT_RESET_MS2); 37 | lcd_enable(); 38 | _delay_ms(LCD_SOFT_RESET_MS3); 39 | 40 | /* configure display */ 41 | lcd_command(LCD_SET_FUNCTION | LCD_FUNCTION_8BIT); 42 | _delay_ms(LCD_SET_8BITMODE_MS); 43 | lcd_command(LCD_SET_FUNCTION | LCD_FUNCTION_8BIT | LCD_FUNCTION_2LINE | LCD_FUNCTION_5X7); 44 | lcd_command(LCD_SET_DISPLAY | LCD_DISPLAY_ON | LCD_CURSOR_OFF | LCD_BLINKING_OFF); 45 | lcd_command(LCD_SET_ENTRY | LCD_ENTRY_INCREASE | LCD_ENTRY_NOSHIFT); 46 | lcd_clear(); 47 | } 48 | 49 | void lcd_data(uint8_t data) 50 | { 51 | LCD_PORT_RS |= (1 << LCD_PIN_RS); 52 | LCD_PORT_DB = data; 53 | _delay_us(LCD_WRITEDATA_US); 54 | lcd_enable(); 55 | } 56 | 57 | void lcd_command(uint8_t command) 58 | { 59 | LCD_PORT_RS &= ~(1 << LCD_PIN_RS); 60 | LCD_PORT_DB = command; 61 | _delay_us(LCD_WRITEDATA_US); 62 | lcd_enable(); 63 | } 64 | 65 | void lcd_clear(void) 66 | { 67 | lcd_command(LCD_CLEAR_DISPLAY); 68 | _delay_ms(LCD_CLEAR_DISPLAY_MS); 69 | } 70 | 71 | void lcd_home(void) 72 | { 73 | lcd_command(LCD_CURSOR_HOME); 74 | _delay_ms(LCD_CURSOR_HOME_MS); 75 | } 76 | 77 | void lcd_setcursor(uint8_t x, uint8_t y) 78 | { 79 | uint8_t data; 80 | 81 | switch (y) 82 | { 83 | case 1: 84 | data = LCD_SET_DDADR + LCD_DDADR_LINE1 + x; 85 | break; 86 | 87 | case 2: 88 | data = LCD_SET_DDADR + LCD_DDADR_LINE2 + x; 89 | break; 90 | 91 | default: 92 | return; 93 | } 94 | 95 | lcd_command(data); 96 | } 97 | 98 | void lcd_int(uint16_t data) 99 | { 100 | char str[20]; 101 | sprintf(str, "%d", data); 102 | lcd_string(str); 103 | } 104 | 105 | void lcd_float(float data) 106 | { 107 | char str[20]; 108 | sprintf(str, "%.2f", data); 109 | lcd_string(str); 110 | } 111 | 112 | void lcd_float64_freq_MHz(float64_t freq, uint8_t number_of_chars) 113 | { 114 | char *fp64_string = fp64_to_string(fp64_div(freq, fp64_sd((float)1e6)), number_of_chars, 5); 115 | char buffer_freq[20]; 116 | 117 | strcpy(buffer_freq, fp64_string); 118 | 119 | if (buffer_freq[0] == '9' && buffer_freq[1] == '.') 120 | lcd_string_append(buffer_freq, " ", 0); 121 | 122 | if (buffer_freq[2] != '.') 123 | lcd_string_append(buffer_freq, ".", 2); 124 | 125 | uint8_t buffer_freq_len = strlen(buffer_freq); 126 | for (uint8_t i = 0; i < number_of_chars-buffer_freq_len; i++) 127 | lcd_string_append(buffer_freq, "0", buffer_freq_len+i); 128 | 129 | if (buffer_freq[0] == '1' && buffer_freq[1] == '0') 130 | lcd_string_append(buffer_freq, "0", number_of_chars); 131 | 132 | lcd_string(buffer_freq); 133 | lcd_string(" MHz"); 134 | } 135 | 136 | void lcd_string(const char *data) 137 | { 138 | while(*data != '\0') 139 | lcd_data(*data++); 140 | } 141 | 142 | void lcd_string_append(char str[], const char insert[], int pos) 143 | { 144 | char buf[40] = { 0 }; 145 | 146 | strncpy(buf, str, pos); 147 | int len = strlen(buf); 148 | strcpy(buf+len, insert); 149 | len += strlen(insert); 150 | strcpy(buf+len, str+pos); 151 | strcpy(str, buf); 152 | } -------------------------------------------------------------------------------- /firmware/lcd.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: LCD driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #ifndef LCD_H 8 | #define LCD_H 9 | 10 | #include "main.h" 11 | 12 | #define LCD_DDR_RS DDRA 13 | #define LCD_PORT_RS PORTA 14 | #define LCD_PIN_RS 5 15 | 16 | #define LCD_DDR_RW DDRA 17 | #define LCD_PORT_RW PORTA 18 | #define LCD_PIN_RW 6 19 | 20 | #define LCD_DDR_E DDRA 21 | #define LCD_PORT_E PORTA 22 | #define LCD_PIN_E 7 23 | 24 | #define LCD_DDR_DB DDRC 25 | #define LCD_PORT_DB PORTC 26 | 27 | #define LCD_BOOTUP_MS 15 28 | #define LCD_ENABLE_US 20 29 | #define LCD_WRITEDATA_US 46 30 | #define LCD_COMMAND_US 42 31 | 32 | #define LCD_SOFT_RESET_MS1 5 33 | #define LCD_SOFT_RESET_MS2 1 34 | #define LCD_SOFT_RESET_MS3 1 35 | #define LCD_SET_4BITMODE_MS 5 36 | #define LCD_SET_8BITMODE_MS 5 37 | 38 | #define LCD_CLEAR_DISPLAY_MS 2 39 | #define LCD_CURSOR_HOME_MS 2 40 | 41 | #define LCD_DDADR_LINE1 0x00 42 | #define LCD_DDADR_LINE2 0x40 43 | 44 | #define LCD_CLEAR_DISPLAY 0x01 45 | #define LCD_CURSOR_HOME 0x02 46 | #define LCD_SET_ENTRY 0x04 47 | 48 | #define LCD_ENTRY_DECREASE 0x00 49 | #define LCD_ENTRY_INCREASE 0x02 50 | #define LCD_ENTRY_NOSHIFT 0x00 51 | #define LCD_ENTRY_SHIFT 0x01 52 | 53 | #define LCD_SET_DISPLAY 0x08 54 | 55 | #define LCD_DISPLAY_OFF 0x00 56 | #define LCD_DISPLAY_ON 0x04 57 | #define LCD_CURSOR_OFF 0x00 58 | #define LCD_CURSOR_ON 0x02 59 | #define LCD_BLINKING_OFF 0x00 60 | #define LCD_BLINKING_ON 0x01 61 | 62 | #define LCD_SET_SHIFT 0x10 63 | 64 | #define LCD_CURSOR_MOVE 0x00 65 | #define LCD_DISPLAY_SHIFT 0x08 66 | #define LCD_SHIFT_LEFT 0x00 67 | #define LCD_SHIFT_RIGHT 0x04 68 | 69 | #define LCD_SET_FUNCTION 0x20 70 | 71 | #define LCD_FUNCTION_4BIT 0x00 72 | #define LCD_FUNCTION_8BIT 0x10 73 | #define LCD_FUNCTION_1LINE 0x00 74 | #define LCD_FUNCTION_2LINE 0x08 75 | #define LCD_FUNCTION_5X7 0x00 76 | #define LCD_FUNCTION_5X10 0x04 77 | 78 | #define LCD_SOFT_RESET 0x30 79 | 80 | #define LCD_SET_CGADR 0x40 81 | 82 | #define LCD_GC_CHAR0 0 83 | #define LCD_GC_CHAR1 1 84 | #define LCD_GC_CHAR2 2 85 | #define LCD_GC_CHAR3 3 86 | #define LCD_GC_CHAR4 4 87 | #define LCD_GC_CHAR5 5 88 | #define LCD_GC_CHAR6 6 89 | #define LCD_GC_CHAR7 7 90 | 91 | #define LCD_SET_DDADR 0x80 92 | 93 | extern void lcd_init(void); 94 | extern void lcd_clear(void); 95 | extern void lcd_home(void); 96 | extern void lcd_setcursor(uint8_t x, uint8_t y); 97 | extern void lcd_data(uint8_t data); 98 | extern void lcd_string(const char *data); 99 | extern void lcd_int(uint16_t data); 100 | extern void lcd_float(float data); 101 | extern void lcd_float64_freq_MHz(float64_t freq, uint8_t number_of_chars); 102 | extern void lcd_command(uint8_t command); 103 | extern void lcd_string_append(char str[], const char insert[], int pos); 104 | 105 | #endif -------------------------------------------------------------------------------- /firmware/led.c: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: LED driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #include "main.h" 8 | 9 | void led_init(void) 10 | { 11 | /* set LED pins as outputs */ 12 | LED_DDR |= (1 << LED_1); 13 | LED_DDR |= (1 << LED_2); 14 | LED_DDR |= (1 << LED_3); 15 | 16 | /* switch of all LEDs */ 17 | LED_1_OFF; 18 | LED_2_OFF; 19 | LED_3_OFF; 20 | } 21 | -------------------------------------------------------------------------------- /firmware/led.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: LED driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #ifndef LED_H 8 | #define LED_H 9 | 10 | #include "main.h" 11 | 12 | #define LED_PORT PORTD 13 | #define LED_DDR DDRD 14 | #define LED_1 3 15 | #define LED_2 4 16 | #define LED_3 5 17 | 18 | #define LED_1_ON LED_PORT |= (1 << LED_1) 19 | #define LED_2_ON LED_PORT |= (1 << LED_2) 20 | #define LED_3_ON LED_PORT |= (1 << LED_3) 21 | 22 | #define LED_1_OFF LED_PORT &= ~(1 << LED_1) 23 | #define LED_2_OFF LED_PORT &= ~(1 << LED_2) 24 | #define LED_3_OFF LED_PORT &= ~(1 << LED_3) 25 | 26 | #define LED_1_TOGGLE LED_PORT ^= (1 << LED_1) 27 | #define LED_2_TOGGLE LED_PORT ^= (1 << LED_2) 28 | #define LED_3_TOGGLE LED_PORT ^= (1 << LED_3) 29 | 30 | extern void led_init(void); 31 | 32 | #endif -------------------------------------------------------------------------------- /firmware/main.c: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: Main program of the 10 MHz GPS Disciplined Oscillator. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #include "main.h" 8 | 9 | /* the time the 32-bit counter is running */ 10 | #define MAIN_GPS_PPS_AVERAGE_IN_SECONDS MAIN_GATE_TIME_IN_SECONDS 11 | 12 | /* the number of chars of the displayed frequency */ 13 | #define MAIN_LCD_FREQ_NUMBER_OF_CHARS 11 14 | 15 | /* time in ms to display status messages */ 16 | #define MAIN_LCD_TIME_STATUS_MESSAGES_MS 2500 17 | 18 | /* time after which the display is updated */ 19 | #define MAIN_LCD_UPDATE_TIME_MS 100 20 | 21 | /* DAC value */ 22 | uint16_t main_dac_vctrl_digital_value = (uint16_t)(DAC_RESOLUTION/2); 23 | 24 | /* flag whether GPS is available */ 25 | uint8_t main_gps_available = 0; 26 | 27 | /* counter for the GPS PPS pulses */ 28 | uint8_t main_gps_edge_counter = 0; 29 | 30 | /* counter for LCD blinking */ 31 | uint8_t main_lcd_blink_counter = 0; 32 | 33 | /* prototypes of additional functions */ 34 | void init(void); 35 | void update_lcd_first_line(void); 36 | int8_t compare_frequencies(float64_t measured, float64_t reference); 37 | 38 | /* main program */ 39 | int main(void) 40 | { 41 | /* initialize all modules */ 42 | init(); 43 | 44 | /* welcome message */ 45 | lcd_clear(); 46 | lcd_setcursor(0,1); 47 | lcd_string(" Muin "); 48 | _delay_ms(MAIN_LCD_TIME_STATUS_MESSAGES_MS); 49 | 50 | /* OCXO heating */ 51 | lcd_clear(); 52 | while (1) 53 | { 54 | _delay_ms(MAIN_LCD_UPDATE_TIME_MS); 55 | 56 | /* get ADC value and convert value to current */ 57 | uint16_t adc_value = adc_get_value(ADC_CHANNEL_OCXO_CURRENT); 58 | float current = ADC_VALUE_TO_OCXO_CURRENT_DIFF(adc_value); 59 | 60 | /* display current current in second line */ 61 | lcd_setcursor(0,2); 62 | lcd_string("at "); 63 | lcd_setcursor(3,2); 64 | lcd_float(current); 65 | lcd_string(" A ..."); 66 | 67 | if (current > 0.4) { 68 | LED_1_TOGGLE; 69 | lcd_setcursor(0,1); 70 | lcd_string("OCXO heating "); 71 | } 72 | else { 73 | LED_1_ON; 74 | lcd_setcursor(0,1); 75 | lcd_string("OCXO stable "); 76 | _delay_ms(MAIN_LCD_TIME_STATUS_MESSAGES_MS); 77 | break; 78 | } 79 | } 80 | 81 | /* OCXO stable, waiting for GPS */ 82 | lcd_clear(); 83 | while(1) 84 | { 85 | _delay_ms(MAIN_LCD_UPDATE_TIME_MS); 86 | 87 | /* update OCXO current and control voltage in first line of LCD */ 88 | update_lcd_first_line(); 89 | 90 | /* GPS not yet available */ 91 | if (!main_gps_available) { 92 | lcd_setcursor(0,2); 93 | lcd_string("Awaiting GPS ..."); 94 | } 95 | else { 96 | lcd_setcursor(0,2); 97 | lcd_string("GPS available "); 98 | _delay_ms(MAIN_LCD_TIME_STATUS_MESSAGES_MS); 99 | break; 100 | } 101 | } 102 | 103 | /* OCXO stable and GPS available */ 104 | fifo_init(); 105 | lcd_clear(); 106 | while(1) 107 | { 108 | _delay_ms(MAIN_LCD_UPDATE_TIME_MS); 109 | 110 | /* update OCXO current and control voltage in first line of LCD */ 111 | update_lcd_first_line(); 112 | 113 | /* (average) clocks per second / per GPS pulse */ 114 | lcd_setcursor(0,2); 115 | lcd_string(" "); 116 | 117 | float64_t ticks_average = fifo_get_mean(); 118 | if (!fp64_isnan(ticks_average)) { 119 | 120 | /* display current frequency in the display's second line */ 121 | lcd_setcursor(0,2); 122 | lcd_float64_freq_MHz(ticks_average, MAIN_LCD_FREQ_NUMBER_OF_CHARS); 123 | 124 | /* compare measured frequency to ideal frequency of 10 MHz */ 125 | int8_t comp = compare_frequencies(ticks_average, fp64_sd((float)F_CPU)); 126 | 127 | /* adjust OCXO control voltage */ 128 | if (comp == -1) { 129 | if (main_dac_vctrl_digital_value < (DAC_RESOLUTION-1)) 130 | main_dac_vctrl_digital_value++; 131 | dac_set_digital_value(main_dac_vctrl_digital_value); 132 | 133 | lcd_setcursor(8,1); 134 | main_lcd_blink_counter++; 135 | if (main_lcd_blink_counter < 4) 136 | lcd_string("+"); 137 | if (main_lcd_blink_counter == 10) 138 | main_lcd_blink_counter = 0; 139 | LED_3_OFF; 140 | } 141 | else if (comp == 1) { 142 | if (main_dac_vctrl_digital_value > 0) 143 | main_dac_vctrl_digital_value--; 144 | dac_set_digital_value(main_dac_vctrl_digital_value); 145 | 146 | lcd_setcursor(8,1); 147 | main_lcd_blink_counter++; 148 | if (main_lcd_blink_counter < 4) 149 | lcd_string("-"); 150 | if (main_lcd_blink_counter == 10) 151 | main_lcd_blink_counter = 0; 152 | LED_3_OFF; 153 | } 154 | else if (comp == 0) { 155 | LED_3_ON; 156 | } 157 | } 158 | else { 159 | lcd_setcursor(0,2); 160 | lcd_string("Measuring ... "); 161 | } 162 | } 163 | } 164 | 165 | /* additional functions */ 166 | void init(void) 167 | { 168 | /* init LEDs */ 169 | led_init(); 170 | 171 | /* init LCD */ 172 | lcd_init(); 173 | 174 | /* init SPI */ 175 | spi_init(); 176 | 177 | /* init ADC */ 178 | adc_init(); 179 | 180 | /* init GPS */ 181 | gps_init(); 182 | 183 | /* init DAC */ 184 | dac_init(); 185 | dac_set_digital_value(main_dac_vctrl_digital_value); 186 | 187 | /* init 32-bit counter */ 188 | tim32_init(); 189 | 190 | /* init FIFO with ideal clock frequency */ 191 | fifo_init(); 192 | 193 | /* enable TIMEPULSE interrupt */ 194 | gps_timepulse_interrupt_enable(GPS_INTERRUPT_EDGE_RISING); 195 | 196 | /* enable all interrupts */ 197 | sei(); 198 | } 199 | 200 | void update_lcd_first_line(void) 201 | { 202 | lcd_setcursor(0,1); 203 | lcd_string(" "); 204 | 205 | /* OCXO supply current */ 206 | uint16_t adc_value_ocxo = adc_get_value(ADC_CHANNEL_OCXO_CURRENT); 207 | float current = ADC_VALUE_TO_OCXO_CURRENT_DIFF(adc_value_ocxo); 208 | if (current > 0.25 && current < 1.5) { 209 | lcd_setcursor(0,1); 210 | lcd_float(current); 211 | lcd_string(" A"); 212 | } 213 | 214 | /* measured control voltage */ 215 | uint16_t adc_value_vctrl = adc_get_value(ADC_CHANNEL_OCXO_VCTRL); 216 | float voltage = ADC_VALUE_TO_OCXO_VCTRL_SE(adc_value_vctrl); 217 | if (voltage > 0.0 && voltage < 5.0) { 218 | lcd_setcursor(10,1); 219 | lcd_float(voltage); 220 | lcd_string(" V"); 221 | } 222 | } 223 | 224 | int8_t compare_frequencies(float64_t measured, float64_t reference) 225 | { 226 | float64_t fp64_difference = fp64_sub(measured, reference); 227 | float64_t fp64_difference_uHz = fp64_mul(fp64_difference, fp64_sd((float)1e6)); 228 | int64_t int64_difference_uHz = fp64_to_int64(fp64_difference_uHz); 229 | 230 | if (int64_difference_uHz < 0) 231 | return -1; 232 | 233 | if (int64_difference_uHz > 0) 234 | return 1; 235 | 236 | return 0; 237 | } 238 | 239 | /* interrupt service routines */ 240 | ISR(INT0_vect) 241 | { 242 | /* ISR called on GPS PPS edge */ 243 | if (gps_interrupt_edge == GPS_INTERRUPT_EDGE_RISING) { 244 | 245 | /* interrupt indicates that GPS is available now */ 246 | main_gps_available = 1; 247 | 248 | main_gps_edge_counter++; 249 | if (main_gps_edge_counter == MAIN_GPS_PPS_AVERAGE_IN_SECONDS) { 250 | 251 | /* reset GPS PPS counter */ 252 | main_gps_edge_counter = 0; 253 | 254 | /* ------------------------------------------------------------------------------------------------------------ */ 255 | /* ------- 56 clock cycles between counter stop and start (with optimize -O1) extracted from *.lss file ------- */ 256 | /* ------------------------------------------------------------------------------------------------------------ */ 257 | 258 | /* stop counter */ 259 | tim32_stop(); /* 9 cycles -> 5 before/4 after counter start */ 260 | 261 | /* get number of ticks from 32-bit counter */ 262 | uint32_t temp_tim32_ticks = tim32_get_ticks(); /* 27 cycles */ 263 | 264 | /* reset 32-bit counter */ 265 | tim32_clear(); /* 19 cycles */ 266 | 267 | /* start counter */ 268 | tim32_start(); /* 10 -> 6 before/4 after counter stop */ 269 | 270 | /* ------------------------------------------------------------------------------------------------------------ */ 271 | 272 | if (temp_tim32_ticks == 0) { 273 | /* ignore first (empty) value */ 274 | } 275 | else { 276 | /* add corrected number of ticks to the fifo */ 277 | fifo_add(fp64_div(fp64_sd((float)(temp_tim32_ticks + 56)), fp64_sd((float)MAIN_GPS_PPS_AVERAGE_IN_SECONDS))); 278 | } 279 | } 280 | 281 | /* trigger on rising edge */ 282 | LED_2_ON; 283 | gps_timepulse_interrupt_enable(GPS_INTERRUPT_EDGE_FALLING); 284 | } 285 | else if (gps_interrupt_edge == GPS_INTERRUPT_EDGE_FALLING) { 286 | 287 | /* trigger on falling edge */ 288 | LED_2_OFF; 289 | gps_timepulse_interrupt_enable(GPS_INTERRUPT_EDGE_RISING); 290 | } 291 | } 292 | 293 | ISR(TIMER1_OVF_vect) 294 | { 295 | /* ISR called on hardware timer (1) overflow */ 296 | tim32_ticks += 65536; 297 | } 298 | -------------------------------------------------------------------------------- /firmware/main.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: Generic includes and defines. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #ifndef _MAIN_H 8 | #define _MAIN_H 9 | 10 | #define F_CPU 10000000L 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "led.h" 22 | #include "adc.h" 23 | #include "dac.h" 24 | #include "gps.h" 25 | #include "lcd.h" 26 | #include "spi.h" 27 | #include "tim32.h" 28 | #include "fifo.h" 29 | 30 | #define MAIN_GATE_TIME_IN_SECONDS 60 31 | #define MAIN_AVERAGING_TIME_IN_MINUTES 1 32 | 33 | #define ACS712_FACTOR 0.185 34 | #define ACS712_VOFFSET 2.513 35 | 36 | extern void init(void); 37 | extern int main(void); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /firmware/spi.c: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: SPI driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #include "main.h" 8 | 9 | void spi_init(void) 10 | { 11 | /* set MOSI pin as output */ 12 | SPI_DDR_MOSI = SPI_DDR_MOSI | (1 << SPI_PIN_MOSI); 13 | 14 | /* set MISO pin as input */ 15 | SPI_DDR_MISO = SPI_DDR_MISO & ~(1 << SPI_PIN_MISO); 16 | 17 | /* set SLK pin as output */ 18 | SPI_DDR_CLK = SPI_DDR_CLK | (1 << SPI_PIN_CLK); 19 | 20 | #if SPI_HARDWARE == 1 21 | /* send MSB first */ 22 | SPCR &= ~(1 << DORD); 23 | 24 | /* master */ 25 | SPCR |= (1 << MSTR); 26 | 27 | /* frequency = 10 MHz / 4 */ 28 | SPCR &= ~(1 << SPR1); 29 | SPCR &= ~(1 << SPR0); 30 | 31 | /* enable SPI */ 32 | SPCR |= (1 << SPE); 33 | #else 34 | /* not needed */ 35 | #endif 36 | } 37 | 38 | void spi_write(uint8_t data) 39 | { 40 | #if SPI_HARDWARE == 1 41 | SPDR = data; 42 | #else 43 | for (uint8_t i = 0; i < 8; i++) { 44 | 45 | if (((data >> (7-i)) & 0x01) == 1) 46 | SPI_PORT_MOSI |= (1 << SPI_PIN_MOSI); 47 | else 48 | SPI_PORT_MOSI &= ~(1 << SPI_PIN_MOSI); 49 | 50 | SPI_PORT_CLK |= (1 << SPI_PIN_CLK); 51 | SPI_PORT_CLK &= ~(1 << SPI_PIN_CLK); 52 | } 53 | #endif 54 | } 55 | 56 | uint8_t spi_read(void) 57 | { 58 | #if SPI_HARDWARE == 1 59 | return SPDR; 60 | #else 61 | /* not implemented */ 62 | return 0; 63 | #endif 64 | } 65 | 66 | void spi_wait_for_transmission_complete(void) 67 | { 68 | #if SPI_HARDWARE == 1 69 | /* wait for transmission complete */ 70 | while (!(SPSR & (1 << SPIF))); 71 | #else 72 | /* not needed */ 73 | #endif 74 | } 75 | 76 | -------------------------------------------------------------------------------- /firmware/spi.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: SPI driver. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #ifndef SPI_H 8 | #define SPI_H 9 | 10 | #include "main.h" 11 | 12 | #define SPI_HARDWARE 0 13 | 14 | #define SPI_DDR_MOSI DDRB 15 | #define SPI_PORT_MOSI PORTB 16 | #define SPI_PIN_MOSI 5 17 | 18 | #define SPI_DDR_MISO DDRB 19 | #define SPI_PORT_MISO PORTB 20 | #define SPI_PIN_MISO 6 21 | 22 | #define SPI_DDR_CLK DDRB 23 | #define SPI_PORT_CLK PORTB 24 | #define SPI_PIN_CLK 7 25 | 26 | extern void spi_init(void); 27 | extern void spi_write(uint8_t data); 28 | extern uint8_t spi_read(void); 29 | extern void spi_wait_for_transmission_complete(void); 30 | 31 | #endif -------------------------------------------------------------------------------- /firmware/tim32.c: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: 32-bit timer. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #include "main.h" 8 | 9 | /* software counter */ 10 | uint32_t tim32_ticks = 0; 11 | 12 | void tim32_init(void) 13 | { 14 | /* reset timer */ 15 | TCCR1A = 0; 16 | TCCR1B = 0; 17 | 18 | /* clear ticks */ 19 | tim32_clear(); 20 | 21 | /* overflow interrupt enable */ 22 | TIMSK1 |= (1 << TOIE1); 23 | } 24 | 25 | void tim32_start(void) 26 | { 27 | /* running with no prescaler (10 MHz) */ 28 | TCCR1B = 0x01; 29 | } 30 | 31 | void tim32_stop(void) 32 | { 33 | /* no clock source */ 34 | TCCR1B = 0; 35 | } 36 | 37 | void tim32_clear(void) 38 | { 39 | /* clear hardware and software counter */ 40 | TCNT1 = 0; 41 | tim32_ticks = 0; 42 | } 43 | 44 | uint32_t tim32_get_ticks(void) 45 | { 46 | /* return sum of hardware and software counter */ 47 | return TCNT1 + tim32_ticks; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /firmware/tim32.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------- 2 | Description: 32-bit timer. 3 | Date: 04/05/2020 4 | Author: Phillip Durdaut 5 | ----------------------------------------------------------------------------------*/ 6 | 7 | #ifndef TIM32_H 8 | #define TIM32_H 9 | 10 | #include "main.h" 11 | 12 | /* software counter */ 13 | extern uint32_t tim32_ticks; 14 | 15 | extern void tim32_init(void); 16 | extern void tim32_start(void); 17 | extern void tim32_stop(void); 18 | extern void tim32_clear(void); 19 | extern uint32_t tim32_get_ticks(void); 20 | 21 | #endif -------------------------------------------------------------------------------- /firmware/tim32_stop_start_cycles_analysis.asm: -------------------------------------------------------------------------------- 1 | ; Analysis of the number of clock cycles between stop and start of the 32-bit timer, extracted from the *.lss file. 2 | ; Compare with AVR Instruction Set Manual. 3 | ; Phillip Durdaut, 2020 4 | 5 | ;/* stop counter */ 6 | ;tim32_stop(); 7 | 632: 67 d0 rcall .+206 ; 0x702 ; 3 cycles 8 | 9 | ;void tim32_stop(void) 10 | ;TCCR1B = 0; 11 | 702: 10 92 81 00 sts 0x0081, r1 ; 0x800081 <__TEXT_REGION_LENGTH__+0x7e0081> ; 2 cycles 12 | 706: 08 95 ret ; 4 cycles 13 | 14 | 15 | 16 | ;/* get number of ticks from 32-bit counter */ 17 | ;main_tim32_ticks = tim32_get_ticks(); 18 | 634: 81 d0 rcall .+258 ; 0x738 ; 3 cycles 19 | 20 | ;uint32_t tim32_get_ticks(void) 21 | ;return TCNT1 + tim32_ticks; 22 | 738: 20 91 84 00 lds r18, 0x0084 ; 0x800084 <__TEXT_REGION_LENGTH__+0x7e0084> ; 2 cycles 23 | 73c: 30 91 85 00 lds r19, 0x0085 ; 0x800085 <__TEXT_REGION_LENGTH__+0x7e0085> ; 2 cycles 24 | 740: 80 91 77 01 lds r24, 0x0177 ; 0x800177 ; 2 cycles 25 | 744: 90 91 78 01 lds r25, 0x0178 ; 0x800178 ; 2 cycles 26 | 748: a0 91 79 01 lds r26, 0x0179 ; 0x800179 ; 2 cycles 27 | 74c: b0 91 7a 01 lds r27, 0x017A ; 0x80017a ; 2 cycles 28 | 750: bc 01 movw r22, r24 ; 1 cycle 29 | 752: cd 01 movw r24, r26 ; 1 cycle 30 | 754: 62 0f add r22, r18 ; 1 cycle 31 | 756: 73 1f adc r23, r19 ; 1 cycle 32 | 758: 81 1d adc r24, r1 ; 1 cycle 33 | 75a: 91 1d adc r25, r1 ; 1 cycle 34 | 75c: 08 95 ret ; 4 cycles 35 | 36 | 670: 6b 01 movw r12, r22 ; 1 cycle 37 | 672: 7c 01 movw r14, r24 ; 1 cycle 38 | 39 | 40 | 41 | ;/* reset 32-bit counter */ 42 | ;tim32_clear(); 43 | 646: 60 d0 rcall .+192 ; 0x708 ; 3 cycles 44 | 45 | ;void tim32_clear(void) 46 | ;TCNT1 = 0; 47 | 708: 10 92 85 00 sts 0x0085, r1 ; 0x800085 <__TEXT_REGION_LENGTH__+0x7e0085> ; 2 cycles 48 | 70c: 10 92 84 00 sts 0x0084, r1 ; 0x800084 <__TEXT_REGION_LENGTH__+0x7e0084> ; 2 cycles 49 | ;tim32_ticks = 0; 50 | 710: 10 92 77 01 sts 0x0177, r1 ; 0x800177 ; 2 cycles 51 | 714: 10 92 78 01 sts 0x0178, r1 ; 0x800178 ; 2 cycles 52 | 718: 10 92 79 01 sts 0x0179, r1 ; 0x800179 ; 2 cycles 53 | 71c: 10 92 7a 01 sts 0x017A, r1 ; 0x80017a ; 2 cycles 54 | 720: 08 95 ret ; 4 cycles 55 | 56 | 57 | 58 | ;/* start counter */ 59 | ;tim32_start(); 60 | 648: 58 d0 rcall .+176 ; 0x6fa ; 3 cycles 61 | 62 | ;void tim32_start(void) 63 | ;TCCR1B = 0x01; 64 | 714: 81 e0 ldi r24, 0x01 ; 1 ; 1 cycle 65 | 716: 80 93 81 00 sts 0x0081, r24 ; 0x800081 <__TEXT_REGION_LENGTH__+0x7e0081> ; 2 cycles 66 | 71a: 08 95 ret ; 4 cycles 67 | -------------------------------------------------------------------------------- /measurements/long term stability/todo.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/measurements/long term stability/todo.txt -------------------------------------------------------------------------------- /measurements/short term stability/fswp_1_noise_spectrum_Sphi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/measurements/short term stability/fswp_1_noise_spectrum_Sphi.png -------------------------------------------------------------------------------- /measurements/short term stability/fswp_2_allan_deviation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/measurements/short term stability/fswp_2_allan_deviation.png -------------------------------------------------------------------------------- /measurements/short term stability/fswp_3_noise_spectrum_Sv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/measurements/short term stability/fswp_3_noise_spectrum_Sv.png -------------------------------------------------------------------------------- /measurements/short term stability/fswp_4_noise_spectrum_Sy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/measurements/short term stability/fswp_4_noise_spectrum_Sy.png -------------------------------------------------------------------------------- /measurements/short term stability/fswp_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/measurements/short term stability/fswp_screenshot.png -------------------------------------------------------------------------------- /pictures/back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/back.jpg -------------------------------------------------------------------------------- /pictures/front_off.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/front_off.jpg -------------------------------------------------------------------------------- /pictures/front_on_heating_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/front_on_heating_1.jpg -------------------------------------------------------------------------------- /pictures/front_on_heating_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/front_on_heating_2.jpg -------------------------------------------------------------------------------- /pictures/front_on_locked.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/front_on_locked.jpg -------------------------------------------------------------------------------- /pictures/front_on_measuring.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/front_on_measuring.jpg -------------------------------------------------------------------------------- /pictures/front_on_ocxo_stable.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/front_on_ocxo_stable.jpg -------------------------------------------------------------------------------- /pictures/ocxo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/ocxo.jpg -------------------------------------------------------------------------------- /pictures/open_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/open_1.jpg -------------------------------------------------------------------------------- /pictures/open_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/open_2.jpg -------------------------------------------------------------------------------- /pictures/u-center_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/u-center_1.jpg -------------------------------------------------------------------------------- /pictures/u-center_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/pictures/u-center_2.jpg -------------------------------------------------------------------------------- /schematics/schematics.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yildi1337/GPSDO/d635f4abfddacc18446e7ebbe865ae3255ba2ec8/schematics/schematics.pdf --------------------------------------------------------------------------------