Andrew Boyson / clock

Dependents:   oldheating gps motorhome heating

Committer:
andrewboyson
Date:
Thu Feb 27 08:23:47 2020 +0000
Revision:
72:8f15a8b142ab
Parent:
71:f621d2127216
Child:
74:9d336a47ab84
Prevent clock from being set if only PPS is coming through; clock sets once PPS+NMEA or NTP is used.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
andrewboyson 47:fd2af868c10a 1 #include <stdlib.h>
andrewboyson 47:fd2af868c10a 2 #include <stdbool.h>
andrewboyson 47:fd2af868c10a 3
andrewboyson 54:a3c018ceca77 4 #include "log.h"
andrewboyson 54:a3c018ceca77 5 #include "clktime.h"
andrewboyson 54:a3c018ceca77 6 #include "clk.h"
andrewboyson 54:a3c018ceca77 7 #include "clkutc.h"
andrewboyson 57:4daf2e423b27 8 #include "time64.h"
andrewboyson 71:f621d2127216 9 #include "rtc.h"
andrewboyson 47:fd2af868c10a 10
andrewboyson 47:fd2af868c10a 11 #define GPREG0 (*((volatile unsigned *) 0x40024044))
andrewboyson 47:fd2af868c10a 12
andrewboyson 47:fd2af868c10a 13 volatile int32_t slew = 0; //ns - up to +/- 2.147s of slew
andrewboyson 47:fd2af868c10a 14 volatile int32_t ppb = 0; //This gets set to the last recorded ppb in TickInit
andrewboyson 47:fd2af868c10a 15
andrewboyson 47:fd2af868c10a 16 int32_t ClkGovGetSlew() { return slew; }
andrewboyson 47:fd2af868c10a 17 int32_t ClkGovGetPpb() { return ppb; }
andrewboyson 47:fd2af868c10a 18
andrewboyson 47:fd2af868c10a 19 void ClkGovSetSlew(int32_t value) { slew = value; }
andrewboyson 47:fd2af868c10a 20 void ClkGovSetPpb (int32_t value) { ppb = value; GPREG0 = ppb; }
andrewboyson 47:fd2af868c10a 21 void ClkGovInit()
andrewboyson 47:fd2af868c10a 22 {
andrewboyson 71:f621d2127216 23 if (RtcPowerLost()) GPREG0 = 0;
andrewboyson 47:fd2af868c10a 24 ppb = GPREG0;
andrewboyson 47:fd2af868c10a 25 }
andrewboyson 47:fd2af868c10a 26
andrewboyson 47:fd2af868c10a 27 //Clock limits
andrewboyson 53:2605da6cf1c7 28 int ClkGovFreqDivisor = 10;
andrewboyson 53:2605da6cf1c7 29 int ClkGovFreqChangeMaxPpb = 1000;
andrewboyson 53:2605da6cf1c7 30 int ClkGovFreqSyncedLimPpb = 1000;
andrewboyson 53:2605da6cf1c7 31 int ClkGovFreqSyncedHysPpb = 100;
andrewboyson 53:2605da6cf1c7 32 int ClkGovSlewDivisor = 10;
andrewboyson 53:2605da6cf1c7 33 int ClkGovSlewChangeMaxMs = 10;
andrewboyson 53:2605da6cf1c7 34 int ClkGovSlewSyncedLimNs = 10000000;
andrewboyson 53:2605da6cf1c7 35 int ClkGovSlewSyncedHysNs = 1000000;
andrewboyson 53:2605da6cf1c7 36 int ClkGovSlewOffsetMaxSecs = 3;
andrewboyson 47:fd2af868c10a 37
andrewboyson 47:fd2af868c10a 38 bool ClkGovTrace = false;
andrewboyson 47:fd2af868c10a 39
andrewboyson 47:fd2af868c10a 40 bool ClkGovIsReceivingTime = false; //This is set from the external source of time
andrewboyson 47:fd2af868c10a 41 bool ClkGovTimeIsSynced = false;
andrewboyson 47:fd2af868c10a 42 bool ClkGovRateIsSynced = false;
andrewboyson 47:fd2af868c10a 43 bool ClkGovIsSynced() { return ClkGovRateIsSynced && ClkGovTimeIsSynced; }
andrewboyson 47:fd2af868c10a 44
andrewboyson 57:4daf2e423b27 45 static void setSyncedTime(clktime diff)
andrewboyson 47:fd2af868c10a 46 {
andrewboyson 57:4daf2e423b27 47 clktime absDiff = llabs(diff);
andrewboyson 57:4daf2e423b27 48 clktime limit = ClkGovSlewSyncedLimNs;
andrewboyson 57:4daf2e423b27 49 clktime hysterisis = ClkGovSlewSyncedHysNs;
andrewboyson 47:fd2af868c10a 50
andrewboyson 47:fd2af868c10a 51 if (absDiff < limit - hysterisis)
andrewboyson 47:fd2af868c10a 52 {
andrewboyson 47:fd2af868c10a 53 if (!ClkGovTimeIsSynced) LogTimeF("Time sync acquired\r\n");
andrewboyson 47:fd2af868c10a 54 ClkGovTimeIsSynced = true;
andrewboyson 47:fd2af868c10a 55 }
andrewboyson 47:fd2af868c10a 56 if (absDiff > limit + hysterisis)
andrewboyson 47:fd2af868c10a 57 {
andrewboyson 69:4e48d3859b87 58 if (ClkGovTimeIsSynced) LogTimeF("Time sync lost (difference = %+lld)\r\n", diff);
andrewboyson 47:fd2af868c10a 59 ClkGovTimeIsSynced = false;
andrewboyson 47:fd2af868c10a 60 }
andrewboyson 47:fd2af868c10a 61 }
andrewboyson 57:4daf2e423b27 62 static void setSyncedRate(clktime diff)
andrewboyson 47:fd2af868c10a 63 {
andrewboyson 47:fd2af868c10a 64
andrewboyson 57:4daf2e423b27 65 clktime absDiff = llabs(diff);
andrewboyson 57:4daf2e423b27 66 clktime limit = ClkGovFreqSyncedLimPpb;
andrewboyson 57:4daf2e423b27 67 clktime hysteresis = ClkGovFreqSyncedHysPpb;
andrewboyson 47:fd2af868c10a 68
andrewboyson 52:333a0822a06d 69 if (absDiff < limit - hysteresis)
andrewboyson 47:fd2af868c10a 70 {
andrewboyson 47:fd2af868c10a 71 if (!ClkGovRateIsSynced) LogTimeF("Rate sync acquired\r\n");
andrewboyson 47:fd2af868c10a 72 ClkGovRateIsSynced = true;
andrewboyson 47:fd2af868c10a 73 }
andrewboyson 52:333a0822a06d 74 if (absDiff > limit + hysteresis)
andrewboyson 47:fd2af868c10a 75 {
andrewboyson 47:fd2af868c10a 76 if ( ClkGovRateIsSynced) LogTimeF("Rate sync lost\r\n");
andrewboyson 47:fd2af868c10a 77 ClkGovRateIsSynced = false;
andrewboyson 47:fd2af868c10a 78 }
andrewboyson 47:fd2af868c10a 79 }
andrewboyson 47:fd2af868c10a 80
andrewboyson 57:4daf2e423b27 81 static void setSlew(clktime diff)
andrewboyson 47:fd2af868c10a 82 {
andrewboyson 57:4daf2e423b27 83 clktime toAdd = -diff / ClkGovSlewDivisor;
andrewboyson 52:333a0822a06d 84 int32_t slewMaxTicks = ClkGovSlewChangeMaxMs << CLK_TIME_ONE_MS_ISH_SHIFT;
andrewboyson 47:fd2af868c10a 85
andrewboyson 47:fd2af868c10a 86 if (toAdd > slewMaxTicks) toAdd = slewMaxTicks;
andrewboyson 47:fd2af868c10a 87 if (toAdd < -slewMaxTicks) toAdd = -slewMaxTicks;
andrewboyson 47:fd2af868c10a 88
andrewboyson 47:fd2af868c10a 89 slew = toAdd;
andrewboyson 47:fd2af868c10a 90
andrewboyson 47:fd2af868c10a 91 if (ClkGovTrace) LogTimeF("Sync setSlew diff %lld gives slew %lld gives TickSlew %ld\r\n", diff, toAdd, slew);
andrewboyson 47:fd2af868c10a 92 }
andrewboyson 57:4daf2e423b27 93 static void adjustPpb(clktime diff)
andrewboyson 47:fd2af868c10a 94 {
andrewboyson 57:4daf2e423b27 95 clktime toAdd = diff / ClkGovFreqDivisor;
andrewboyson 53:2605da6cf1c7 96 int32_t maxAdd = ClkGovFreqChangeMaxPpb;
andrewboyson 47:fd2af868c10a 97
andrewboyson 47:fd2af868c10a 98 if (toAdd > maxAdd) toAdd = maxAdd;
andrewboyson 47:fd2af868c10a 99 if (toAdd < -maxAdd) toAdd = -maxAdd;
andrewboyson 47:fd2af868c10a 100
andrewboyson 47:fd2af868c10a 101 ClkGovSetPpb(ppb - toAdd);
andrewboyson 47:fd2af868c10a 102
andrewboyson 47:fd2af868c10a 103 if (ClkGovTrace) LogTimeF("Sync setPpb diff %lld gives toAdd %lld gives TickPpb %ld\r\n", diff, toAdd, ppb);
andrewboyson 47:fd2af868c10a 104 }
andrewboyson 47:fd2af868c10a 105
andrewboyson 57:4daf2e423b27 106 static clktime lastIntClock = -1; //-1 indicates invalid value. 0 is a valid value.
andrewboyson 57:4daf2e423b27 107 static clktime lastExtClock = -1;
andrewboyson 57:4daf2e423b27 108 static void reset(clktime thisExtClock)
andrewboyson 47:fd2af868c10a 109 {
andrewboyson 47:fd2af868c10a 110 ClkTimeSet(thisExtClock);
andrewboyson 47:fd2af868c10a 111 ClkGovSetPpb(0);
andrewboyson 47:fd2af868c10a 112 lastIntClock = 0;
andrewboyson 47:fd2af868c10a 113 lastExtClock = 0;
andrewboyson 47:fd2af868c10a 114 }
andrewboyson 47:fd2af868c10a 115
andrewboyson 72:8f15a8b142ab 116 static void sync(clktime thisExtClock, bool thisExtClockIsComplete)
andrewboyson 47:fd2af868c10a 117 {
andrewboyson 47:fd2af868c10a 118
andrewboyson 72:8f15a8b142ab 119 if (!ClkTimeIsSet() && thisExtClockIsComplete) //Cold start - only ever true if the RTC was not set.
andrewboyson 47:fd2af868c10a 120 {
andrewboyson 47:fd2af868c10a 121 LogTimeF("Sync - cold start of clock so resetting\r\n");
andrewboyson 47:fd2af868c10a 122 reset(thisExtClock);
andrewboyson 47:fd2af868c10a 123 return;
andrewboyson 47:fd2af868c10a 124 }
andrewboyson 47:fd2af868c10a 125
andrewboyson 47:fd2af868c10a 126 //Get the time at the time of the interrupt
andrewboyson 57:4daf2e423b27 127 clktime thisIntClock;
andrewboyson 57:4daf2e423b27 128 clktime thisAbsClock;
andrewboyson 47:fd2af868c10a 129 ClkTimesGetFromSnapshot(&thisIntClock, &thisAbsClock);
andrewboyson 47:fd2af868c10a 130
andrewboyson 47:fd2af868c10a 131 //Calulate the time error
andrewboyson 57:4daf2e423b27 132 clktime absDiff = thisAbsClock - thisExtClock;
andrewboyson 57:4daf2e423b27 133 if (llabs(absDiff) > ((clktime)ClkGovSlewOffsetMaxSecs << CLK_TIME_ONE_SECOND_SHIFT))
andrewboyson 47:fd2af868c10a 134 {
andrewboyson 52:333a0822a06d 135 LogTimeF("Sync - offset is greater than %d seconds so resetting\r\n", ClkGovSlewOffsetMaxSecs);
andrewboyson 47:fd2af868c10a 136 reset(thisExtClock);
andrewboyson 47:fd2af868c10a 137 return;
andrewboyson 47:fd2af868c10a 138 }
andrewboyson 47:fd2af868c10a 139 setSlew(absDiff);
andrewboyson 47:fd2af868c10a 140 setSyncedTime(absDiff);
andrewboyson 47:fd2af868c10a 141
andrewboyson 47:fd2af868c10a 142 //Calculate the rate error
andrewboyson 47:fd2af868c10a 143 if (lastExtClock > -1)
andrewboyson 47:fd2af868c10a 144 {
andrewboyson 57:4daf2e423b27 145 clktime extPeriod = thisExtClock - lastExtClock;
andrewboyson 47:fd2af868c10a 146
andrewboyson 57:4daf2e423b27 147 clktime intPeriod = thisIntClock - lastIntClock;
andrewboyson 57:4daf2e423b27 148 clktime periodDiff = intPeriod - extPeriod;
andrewboyson 47:fd2af868c10a 149
andrewboyson 57:4daf2e423b27 150 clktime ppbDiff;
andrewboyson 47:fd2af868c10a 151 if (extPeriod == CLK_TIME_ONE_SECOND) ppbDiff = periodDiff; //This saves a 64bit shift and division for PPS
andrewboyson 47:fd2af868c10a 152 else ppbDiff = (periodDiff << CLK_TIME_ONE_SECOND_SHIFT) / extPeriod;
andrewboyson 47:fd2af868c10a 153
andrewboyson 47:fd2af868c10a 154 adjustPpb(ppbDiff);
andrewboyson 47:fd2af868c10a 155 setSyncedRate(ppbDiff);
andrewboyson 47:fd2af868c10a 156 }
andrewboyson 47:fd2af868c10a 157
andrewboyson 47:fd2af868c10a 158 //Save last values
andrewboyson 47:fd2af868c10a 159 lastIntClock = thisIntClock;
andrewboyson 47:fd2af868c10a 160 lastExtClock = thisExtClock;
andrewboyson 47:fd2af868c10a 161 }
andrewboyson 57:4daf2e423b27 162 void ClkGovSyncPpsI()
andrewboyson 54:a3c018ceca77 163 {
andrewboyson 57:4daf2e423b27 164 ClkTimeSaveSnapshot();
andrewboyson 57:4daf2e423b27 165 }
andrewboyson 70:d04775a75597 166 void ClkGovSyncPpsN(time64 t) //t is number of seconds utc
andrewboyson 70:d04775a75597 167 {
andrewboyson 70:d04775a75597 168 clktime time;
andrewboyson 70:d04775a75597 169 time = (clktime)t << CLK_TIME_ONE_SECOND_SHIFT;
andrewboyson 70:d04775a75597 170 time = ClkUtcToTai(time);
andrewboyson 72:8f15a8b142ab 171 sync(time, false);
andrewboyson 70:d04775a75597 172 }
andrewboyson 70:d04775a75597 173 void ClkGovSyncPpsZ()
andrewboyson 70:d04775a75597 174 {
andrewboyson 57:4daf2e423b27 175 clktime time;
andrewboyson 54:a3c018ceca77 176 time = ClkNowTai();
andrewboyson 70:d04775a75597 177 time += 1UL << (CLK_TIME_ONE_SECOND_SHIFT - 1); //Add half a second so as to round to nearest rather than round down
andrewboyson 70:d04775a75597 178 time >>= CLK_TIME_ONE_SECOND_SHIFT;
andrewboyson 70:d04775a75597 179 time <<= CLK_TIME_ONE_SECOND_SHIFT;
andrewboyson 72:8f15a8b142ab 180 sync(time, true);
andrewboyson 54:a3c018ceca77 181 }
andrewboyson 54:a3c018ceca77 182
andrewboyson 70:d04775a75597 183
andrewboyson 57:4daf2e423b27 184 void ClkGovSyncTime(clktime time)
andrewboyson 57:4daf2e423b27 185 {
andrewboyson 57:4daf2e423b27 186 ClkTimeSaveSnapshot();
andrewboyson 72:8f15a8b142ab 187 sync(time, true);
andrewboyson 57:4daf2e423b27 188 }