diff --git a/firmware/inc/drift.h b/firmware/inc/drift.h new file mode 100644 index 0000000..2680b03 --- /dev/null +++ b/firmware/inc/drift.h @@ -0,0 +1,36 @@ +/* + * Monitors xosc drift while transmitting + * Copyright (C) 2016 Richard Meadows + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef DRIFT_H +#define DRIFT_H + +#include "samd20.h" +#include "si_trx.h" + +int16_t drift_get_channel_offset(void); +void drift_set_fconfig(struct si_frequency_configuration* config); +void drift_measure_start(void); +void drift_measure_stop(void); + +#endif /* DRIFT_H */ diff --git a/firmware/inc/xosc.h b/firmware/inc/xosc.h index cc77c47..052c8ba 100644 --- a/firmware/inc/xosc.h +++ b/firmware/inc/xosc.h @@ -28,8 +28,8 @@ #include "samd20.h" enum xosc_measurement_t { - XOSC_MEASURE_GCLK0, XOSC_MEASURE_TIMEPULSE, + XOSC_MEASURE_LFTIMER, }; typedef void (*measurement_result_t)(uint32_t result); @@ -54,7 +54,8 @@ void gclk1_init(void); void gclk2_init(void); /** Measurement */ -void measure_xosc(enum xosc_measurement_t measurement_t, measurement_result_t callback); +void measure_xosc(enum xosc_measurement_t measurement_t, measurement_result_t callback, uint8_t oneshot); +void measure_xosc_disable(enum xosc_measurement_t measurement_t); /** LF Timer */ void lf_tick_start(void); diff --git a/firmware/src/data.c b/firmware/src/data.c index edf7ec3..35e71be 100644 --- a/firmware/src/data.c +++ b/firmware/src/data.c @@ -55,8 +55,9 @@ void collect_data_async(void) #endif /* GPS_TYPE_UBX */ /* Measure XOSC against gps timepulse */ - //measure_xosc(XOSC_MEASURE_TIMEPULSE, xosc_measure_callback); + /* single shot, ongoing until two rising edges on GPS TIMEPULSE */ /* NOT IN USE CURRENTLY */ + //measure_xosc(XOSC_MEASURE_TIMEPULSE, xosc_measure_callback, 1); /* Analogue Measurements */ start_adc_sequence(); diff --git a/firmware/src/drift.c b/firmware/src/drift.c new file mode 100644 index 0000000..4b3c624 --- /dev/null +++ b/firmware/src/drift.c @@ -0,0 +1,146 @@ +/* + * Monitors xosc drift while transmitting + * Copyright (C) 2016 Richard Meadows + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "samd20.h" +#include "hw_config.h" +#include "si_trx.h" +#include "xosc.h" +#include "drift.h" + +/** + * Initial reference, in Hz + */ +uint32_t initial_xosc_frequency; +/** + * Current offset, in Hz at the xosc output + * +ve if xosc too fast, -ve if xosc too slow + */ +float current_frequency_offset; +/** + * Current frequency configuration + */ +struct si_frequency_configuration* _fconfig = NULL; +/** + * Current offset, in rf channels for the current frequency configuration + * +ve if xosc too fast, -ve if xosc too slow + */ +volatile int16_t current_channel_offset; + + +/** + * Moving average + */ +#define MOVING_AVERAGE_N (8) +int32_t frequency_moving_average[MOVING_AVERAGE_N]; +uint32_t frequency_moving_average_index = 0; + + +/** + * The result is the xosc frequency in Hz + */ +void xosc_drift_measure_callback(uint32_t result) +{ + int32_t offset; + + if (initial_xosc_frequency == 0) { /* Initial reference */ + initial_xosc_frequency = result; + offset = 0; + frequency_moving_average[0] = 0; + frequency_moving_average_index++; + } else { + offset = result - initial_xosc_frequency; + + /* add to moving average */ + frequency_moving_average[(frequency_moving_average_index % 8)] = offset; + + /* calculate average */ + uint32_t n = (frequency_moving_average_index > 8) ? 8: frequency_moving_average_index; + float sum = 0; + for (uint32_t i = 0; i < n; i++) { + sum += frequency_moving_average[i]; + } + + /* record result */ + current_frequency_offset = sum / n; + frequency_moving_average_index++; + } + + if (_fconfig != NULL) { /* if we seem to have a fconfig */ + float rfband_offset_hz = ((float)current_frequency_offset * _fconfig->frequency) + / XOSC_FREQUENCY; + + float channel_offset_f = rfband_offset_hz / _fconfig->lsb_tuning_resolution; + current_channel_offset = (int16_t)round(channel_offset_f); + } +} + +/** + * Returns the current channel delta caused by drift. + * + * The subtract this from the intended channel to compensate. + */ +int16_t drift_get_channel_offset(void) +{ + return current_channel_offset; +} + + +/** + * Set the current frequency configuration. + * + * drift_get_channel_offset will always return zero if this is not set + */ +void drift_set_fconfig(struct si_frequency_configuration* config) +{ + _fconfig = config; + current_channel_offset = 0; +} +/** + * Start xosc drift measurements + */ +void drift_measure_start(void) +{ + initial_xosc_frequency = 0; + current_frequency_offset = 0; + current_channel_offset = 0; + frequency_moving_average_index = 0; + + /* start ontinuous measurement (oneshot=0) */ + measure_xosc(XOSC_MEASURE_LFTIMER, xosc_drift_measure_callback, 0); +} +/** + * Stop xosc drift measurements + */ +void drift_measure_stop(void) +{ + /* clear offsets to zero while we're stopped */ + initial_xosc_frequency = 0; + current_frequency_offset = 0; + current_channel_offset = 0; + + /* stop measurement */ + measure_xosc_disable(XOSC_MEASURE_LFTIMER); +} diff --git a/firmware/src/init.c b/firmware/src/init.c index 9e7c571..a001dba 100644 --- a/firmware/src/init.c +++ b/firmware/src/init.c @@ -111,7 +111,6 @@ void init(enum init_type init_t) gclk0_to_hf_clock(); /* and switch, clock at 8MHz */ gclk1_init(); gclk2_init(); - rtc_init(); /* Clock up to 14MHz with 0 wait states */ system_flash_set_waitstates(SYSTEM_WAIT_STATE_1_8V_14MHZ); diff --git a/firmware/src/rtc.c b/firmware/src/rtc.c index 019d3c9..53c3d9f 100644 --- a/firmware/src/rtc.c +++ b/firmware/src/rtc.c @@ -46,7 +46,7 @@ void rtc_init(void) for (uint8_t i = 0; i < RTC_NUM_OF_COMP16; i++) { config_events.generate_event_on_compare[i] = false; } - for (uint8_t i = 0; i < 7; i++) { + for (uint8_t i = 0; i < 8; i++) { config_events.generate_event_on_periodic[i] = false; } config_events.generate_event_on_periodic[7] = true; diff --git a/firmware/src/telemetry.c b/firmware/src/telemetry.c index 25b667b..34400d1 100644 --- a/firmware/src/telemetry.c +++ b/firmware/src/telemetry.c @@ -33,6 +33,7 @@ #include "aprs.h" #include "ax25.h" #include "pips.h" +#include "drift.h" #include "si_trx.h" #include "si_trx_defs.h" #include "system/gclk.h" @@ -455,6 +456,9 @@ void telemetry_init(void) /* Setup fconfig for telemetry. This is static */ si_trx_get_frequency_configuration(&telemetry_fconfig, TELEMETRY_FREQUENCY); + + /* We only compensate drift for telemetry, set fconfig for this */ + drift_set_fconfig(&telemetry_fconfig); } diff --git a/firmware/src/xosc.c b/firmware/src/xosc.c index ef323ee..f7dabbb 100644 --- a/firmware/src/xosc.c +++ b/firmware/src/xosc.c @@ -35,6 +35,7 @@ #include "tc/tc_driver.h" #include "hw_config.h" #include "xosc.h" +#include "rtc.h" #include "watchdog.h" enum measure_state_t { @@ -42,6 +43,7 @@ enum measure_state_t { MEASURE_MEASUREMENT, } measure_state = MEASURE_WAIT_FOR_FIRST_EVENT; enum xosc_measurement_t _measurement_t; +uint8_t _measurement_oneshot; measurement_result_t _callback; /** @@ -358,17 +360,38 @@ void timepulse_extint_event_source_disable(void) { extint_disable(); } +/** + * Configure 1Hz events from the LF Timer + */ +void lftimer_event_source(void) +{ + /* Start the RTC */ + rtc_init(); + + /* Route the RTC PER7 event to event channel 0 */ + events_allocate(0, + EVENTS_EDGE_DETECT_NONE, /* Don't care for async path */ + EVENTS_PATH_ASYNCHRONOUS, + 0xB, /* RTC PER7 event */ + 0); +} +void lftimer_event_source_disable(void) { +} + + + /** * Triggers a measurements the number of cycles on XOSC * * A callback from the timer interrupt is available. Obviously don't dwell here too long. */ void measure_xosc(enum xosc_measurement_t measurement_t, - measurement_result_t callback) { + measurement_result_t callback, uint8_t oneshot) { measure_state = MEASURE_WAIT_FOR_FIRST_EVENT; _measurement_t = measurement_t; _callback = callback; + _measurement_oneshot = oneshot; /* Timer 2 runs on GLCK1 */ bool t2_capture_channel_enables[] = {true, true}; @@ -408,11 +431,11 @@ void measure_xosc(enum xosc_measurement_t measurement_t, /* Configure an event source */ switch (measurement_t) { - case XOSC_MEASURE_GCLK0: -// osc8m_event_source(); // osc8m issues events - break; case XOSC_MEASURE_TIMEPULSE: - timepulse_extint_event_source(); // timepulse issues events + timepulse_extint_event_source(); /* timepulse issues events at 1Hz */ + break; + case XOSC_MEASURE_LFTIMER: + lftimer_event_source(); /* lftier issues events at 1Hz */ break; } @@ -423,12 +446,12 @@ void measure_xosc_disable(enum xosc_measurement_t measurement_t) { tc_disable(TC2); switch (measurement_t) { - case XOSC_MEASURE_GCLK0: -// osc8m_event_source_disable(); - break; case XOSC_MEASURE_TIMEPULSE: timepulse_extint_event_source_disable(); break; + case XOSC_MEASURE_LFTIMER: + lftimer_event_source_disable(); + break; } } @@ -449,17 +472,17 @@ void TC2_Handler(void) { measure_state = MEASURE_MEASUREMENT; /* Start measurement */ break; case MEASURE_MEASUREMENT: - /* Measurement done. Read off data */ + /* Measurement made. Read off data */ capture_value = tc_get_capture_value(TC2, 0); /* Calcuate the frequency of GLCK1 relative to this source */ switch (_measurement_t) { - case XOSC_MEASURE_GCLK0: - source_freq = capture_value * XOSC_GCLK_DIVIDE; - break; case XOSC_MEASURE_TIMEPULSE: source_freq = capture_value * XOSC_GCLK_DIVIDE * GPS_TIMEPULSE_FREQ; break; + case XOSC_MEASURE_LFTIMER: + source_freq = capture_value * XOSC_GCLK_DIVIDE; + break; } /* Callback if we have one */ @@ -467,8 +490,10 @@ void TC2_Handler(void) { _callback(source_freq); } - /* Disable measurement system */ - measure_xosc_disable(_measurement_t); + if (_measurement_oneshot != 0) { /* single shot */ + /* Disable measurement system */ + measure_xosc_disable(_measurement_t); + } } } }