Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: oldheating gps motorhome heating
clk/clkgov.c@72:8f15a8b142ab, 2020-02-27 (annotated)
- 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?
| User | Revision | Line number | New 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 | } |