├── 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 | [](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
--------------------------------------------------------------------------------