Uses Timer 0 and the RTC to keep accurate time. It can accept a PPS from an external source like a GPS or a regular time stamp from an external source like an NTP server. It also provides timer functions to 96MHz up to 44 seconds using the CPU clock.

Dependents:   oldheating gps motorhome heating

Description

The clock library provides a number of separate functions:

  • hrtimer An unsigned 32bit high resolution timer which wraps around every 44 seconds from which all the time is derived.
  • mstimer An unsigned 32bit low resolution timer which wraps around every 49 days
  • clktimer A signed 64bit timer (TAI) which doesn't wrap (or not until 2242 when clock time breaks)
  • scan Calculates the max, min and average scan times.
  • rtc A real time clock to provide backup
  • tm Routines to manipulate struct tm local and utc times
  • clk A clock which is synchronised to an external source

High resolution timer

hrtimer uses TIM0 as a 32bit timer which counts at the cpu frequency 96MHz and rolls over after about 44s.
It has an init routine called from ClkInit to start it, thereafter it free runs.
No dependencies.

Millisecond timer

mstimer uses the high resolution timer to count the number of ms since power up. Its unsigned 32bit count rolls over after about 49 days.
It has a main routine called from ClkMain.
Depends on timer.

Clock timer

clktimer uses the signed 64 bit clock time.
Depends on clock and hence hrtimer.

Scan times

scan uses the high resolution timer to calculate the max, min and average scan times.
It has a main routine called from ClkMain.
Depends on hrtimer.

Real time clock

rtc contains routines to save and restore the time in the battery backed real time clock.
Parameters are struct tm.
No dependencies.

Local and UTC manipulation

tm contains

  • the typedef time64 which contains the count of seconds since 1970; just like time_t but based on int64_t to avoid the 2038 problem
  • a number of functions for manipulating time64 and struct tm times

No dependencies.

Clk

clk contains

  • settings
  • functions to save and restore the time to the RTC. Depends on timer, rtc and tm.

clktime increments the time by 1 each second via clk.c from timer.c.
It increments the signed 64 bit time count using the ppb and slew (governed by clkgov.c).
When the time is requested it uses its count and a proportion of the elapsed second from the high resolution timer to calculate the exact time.
See time-formats.text for the clock time format.

clkgov governs the ppb and slew to synchronise the clock time with an external source.
PPB is stored in GPREG0 whenever it is set and retrieved during initialisation.
It takes external time from either:

  • a long term source such as NTP
  • a pulse per second (PPS) such as GPS

clkntp converts clock time to NTP time and vice versa.

clktm converts clock time to struct tm and vice versa

clkutc maintains the era offset (leap seconds count).
The era offset and other information is stored in GPREG1 whenever it is set and retrieved during initialisation.
It contains:

  • the current era offset
  • for the next epoch:
    • its start month (as year and month since 1970)
    • its state: normal; waiting to leap forward; waiting to leap back; leaping forward (second 60)
  • conversion routines between tai and utc (clk time is tai)

Clock time formats

Criteria

Resolution

PPS
We get an interrupt each second which we can resolve to a microsecond. The divisor is 1000. To carry this resolution into the governor we need 1 ppb.
NTP
Suppose we are adding compensation every second, sampling every 4 hours and want to represent 3ms of error with a divisor of 10: that would need a resolution of 23 ppb.
The best temperature compensated crystal oscillators can manage about 1ppm (see Wikipedia) long term or 10 ppb short term.

Lifetime

Needs to keep going during the lifetime of this, or other related, projects. At least a century (so 2100) but more than a few centuries is likely to be pointless

Ease of transforming to NTP, time_t

A count of decimal times - ms, us, ns or ps - can only be transformed using multiplication or division by 1000s. NTP and time_t use binary fractions about a fixed decimal point.

Ease of representing ppm or ppb

A count of decimal times is best but a count of fractions is near enough as 10 bits (1024) is very close to being 1000. As long as it is only needed for a correction such as ppb the approximation would only manifest itself as a 7% error.

The version chosen

1 bit sign, 33 bits for seconds, 30 bits for fraction

+/- 272 years at 1ns or 1 ppb per second
Clock era is 1970

Advantages:

  • adequately representing the freq adjustments for pps
  • simple transformation to NTP and time_t
  • approximates to ns or, with a bit shift, to us or ms
  • adequately covers the next two centuries
  • one unit represents 1 ppb for display

Disadvantage:

  • none

Alternatives considered

1 bit sign, 43 bits for seconds, 20 bits for fraction

+/- 278,731 years at 1us or 1 ppm per second

Advantages:

  • a wide coverage
  • simple transformation to NTP and time_t
  • approximates to us or, with a bitwise shift, to ms
  • one unit represents 1 ppm for display

Disadvantage:

  • not able to reflect the freq adjustments for pps.

1 bit sign, 35bits for seconds, 28bits for fraction

+/- 1089 years at 3ns or 3ppb per second
looks like SSSS SSSS S.FFF FFFF in hex

Advantages:

  • easily represented in hex
  • a wide coverage
  • simple transformation to NTP and time_t

Disadvantage:

  • one unit doesn't approximate to anything simple

32 bits for seconds, 32 bits for fraction

Ntp time with an era of 1900
1900 to 2036 with a resolution of 250ps or 0.25 ppb

Advantages:

  • Already NTP and easily converted to time_t

Disadvantage:

  • Will rollover in 2036

Use 96MHz int64 count

+/- 3044 years with a resolution of 10ns or 10ppb per second

Advantages:

  • a wide coverage

Disadvantage:

  • cannot use simple bit shifts to transform to NTP and time_t
  • not transferable to a system with a different clock rate

Use a count of ns

+/- 292 years at 1ns or 1ppb per second

Advantages:

  • adequately representing the freq adjustments for pps
  • easily usable with ppb and ns
  • a wide coverage

Disadvantage:

  • cannot use simple bit shifts to transform to NTP and time_t
Committer:
andrewboyson
Date:
Mon Jul 27 10:30:10 2020 +0000
Revision:
76:c2035b7754fe
Parent:
73:286a739f7c05
Corrected haveFullTime in sync time PPS

Who changed what in which revision?

UserRevisionLine numberNew contents of line
andrewboyson 46:d3d56cb47940 1 #include <stdint.h>
andrewboyson 47:fd2af868c10a 2
andrewboyson 46:d3d56cb47940 3 #include "clktime.h"
andrewboyson 71:f621d2127216 4 #include "tm.h"
andrewboyson 71:f621d2127216 5 #include "rtc.h"
andrewboyson 47:fd2af868c10a 6
andrewboyson 47:fd2af868c10a 7 #define GPREG1 (*((volatile unsigned *) 0x40024048))
andrewboyson 47:fd2af868c10a 8
andrewboyson 47:fd2af868c10a 9 /*
andrewboyson 47:fd2af868c10a 10 +----+----+----+----+----+----+----+----+
andrewboyson 47:fd2af868c10a 11 |Flgs| Leap months | Leap count |
andrewboyson 47:fd2af868c10a 12 |UUDE| 12 bits | 16 bits |
andrewboyson 47:fd2af868c10a 13 +----+----+----+----+----+----+----+----+
andrewboyson 47:fd2af868c10a 14
andrewboyson 47:fd2af868c10a 15 Leap months: 12 bits will hold 4096 months or 341 years
andrewboyson 47:fd2af868c10a 16
andrewboyson 47:fd2af868c10a 17 Leap count: 16 bits will hold enough leaps seconds for 60,000 years
andrewboyson 47:fd2af868c10a 18
andrewboyson 47:fd2af868c10a 19 Flgs
andrewboyson 47:fd2af868c10a 20 U = unused
andrewboyson 47:fd2af868c10a 21 D = direction: 1 to subtract; 0 to add
andrewboyson 47:fd2af868c10a 22 E = Enable: 1 if leap to take into account at the start of the leap month; 0 if already taken or to be ignored
andrewboyson 47:fd2af868c10a 23 */
andrewboyson 47:fd2af868c10a 24
andrewboyson 47:fd2af868c10a 25 //Leap seconds
andrewboyson 55:e18983651004 26 static int epochOffset = 0; //12 bits holds enough leap seconds for at least 300 years.
andrewboyson 57:4daf2e423b27 27 static clktime epochOffset64 = 0;
andrewboyson 51:826c58fbfaed 28 int ClkUtcGetEpochOffset() { return epochOffset; }
andrewboyson 73:286a739f7c05 29 void ClkUtcSetEpochOffsetWithUtcChange(int value)
andrewboyson 47:fd2af868c10a 30 {
andrewboyson 55:e18983651004 31 epochOffset = value;
andrewboyson 57:4daf2e423b27 32 epochOffset64 = (clktime)epochOffset << CLK_TIME_ONE_SECOND_SHIFT;
andrewboyson 67:c44c5c90e35a 33 GPREG1 = (GPREG1 & 0xFFFF0000) | (value & 0x0000FFFF);
andrewboyson 47:fd2af868c10a 34 }
andrewboyson 73:286a739f7c05 35 void ClkUtcAddEpochOffsetWithUtcChange(int value)
andrewboyson 47:fd2af868c10a 36 {
andrewboyson 73:286a739f7c05 37 ClkUtcSetEpochOffsetWithUtcChange(epochOffset + value);
andrewboyson 73:286a739f7c05 38 }
andrewboyson 73:286a739f7c05 39 void ClkUtcSetEpochOffsetWithoutUtcChange(int value)
andrewboyson 73:286a739f7c05 40 {
andrewboyson 73:286a739f7c05 41 ClkTimeAdjustSeconds(value - epochOffset); //Adjust the tai time by the difference
andrewboyson 73:286a739f7c05 42 ClkUtcSetEpochOffsetWithUtcChange(value);
andrewboyson 47:fd2af868c10a 43 }
andrewboyson 46:d3d56cb47940 44
andrewboyson 47:fd2af868c10a 45 //Next leap second
andrewboyson 51:826c58fbfaed 46 static int nextEpochMonth1970 = 0;
andrewboyson 57:4daf2e423b27 47 static clktime nextEpochUtc = 0;
andrewboyson 51:826c58fbfaed 48 int ClkUtcGetNextEpochMonth1970() { return nextEpochMonth1970; }
andrewboyson 57:4daf2e423b27 49 clktime ClkUtcGetNextEpoch () { return nextEpochUtc; }
andrewboyson 51:826c58fbfaed 50 static void makeNextEpochUtc()
andrewboyson 47:fd2af868c10a 51 {
andrewboyson 51:826c58fbfaed 52 int year = nextEpochMonth1970 / 12 + 1970;
andrewboyson 51:826c58fbfaed 53 int month = nextEpochMonth1970 % 12 + 1;
andrewboyson 47:fd2af868c10a 54 struct tm tm;
andrewboyson 47:fd2af868c10a 55 TmFromInteger(year, month, 1, 0, 0, 0, &tm);
andrewboyson 58:ad2bfd0345de 56 time64 t = TmUtcToTime64(&tm);
andrewboyson 57:4daf2e423b27 57 nextEpochUtc = (clktime)t << CLK_TIME_ONE_SECOND_SHIFT;
andrewboyson 47:fd2af868c10a 58 }
andrewboyson 51:826c58fbfaed 59 void ClkUtcSetNextEpochMonth1970(int value)
andrewboyson 47:fd2af868c10a 60 {
andrewboyson 51:826c58fbfaed 61 nextEpochMonth1970 = value;
andrewboyson 51:826c58fbfaed 62 makeNextEpochUtc();
andrewboyson 67:c44c5c90e35a 63 GPREG1 = (GPREG1 & 0xF000FFFF) | (((uint32_t)value << 16) & 0x0FFF0000); //Precedence order: shifts then ands then ors.
andrewboyson 47:fd2af868c10a 64 }
andrewboyson 47:fd2af868c10a 65
andrewboyson 51:826c58fbfaed 66 static bool nextLeapEnable = false;
andrewboyson 51:826c58fbfaed 67 static bool nextLeapForward = true;
andrewboyson 51:826c58fbfaed 68 bool ClkUtcGetNextLeapEnable () { return nextLeapEnable; }
andrewboyson 51:826c58fbfaed 69 bool ClkUtcGetNextLeapForward() { return nextLeapForward; }
andrewboyson 47:fd2af868c10a 70 void ClkUtcSetNextLeapEnable(bool value)
andrewboyson 47:fd2af868c10a 71 {
andrewboyson 51:826c58fbfaed 72 nextLeapEnable = value;
andrewboyson 47:fd2af868c10a 73 if (value) GPREG1 |= 0x10000000;
andrewboyson 47:fd2af868c10a 74 else GPREG1 &= 0xEFFFFFFF;
andrewboyson 47:fd2af868c10a 75 }
andrewboyson 51:826c58fbfaed 76 void ClkUtcSetNextLeapForward(bool value)
andrewboyson 47:fd2af868c10a 77 {
andrewboyson 51:826c58fbfaed 78 nextLeapForward = value;
andrewboyson 47:fd2af868c10a 79 if (value) GPREG1 |= 0x20000000;
andrewboyson 47:fd2af868c10a 80 else GPREG1 &= 0xDFFFFFFF;
andrewboyson 47:fd2af868c10a 81 }
andrewboyson 51:826c58fbfaed 82 void ClkUtcTglNextLeapEnable () { ClkUtcSetNextLeapEnable (!nextLeapEnable ); }
andrewboyson 51:826c58fbfaed 83 void ClkUtcTglNextLeapForward() { ClkUtcSetNextLeapForward(!nextLeapForward); }
andrewboyson 47:fd2af868c10a 84
andrewboyson 48:b0f38e523552 85 void ClkUtcInit(void)
andrewboyson 47:fd2af868c10a 86 {
andrewboyson 71:f621d2127216 87 if (RtcPowerLost()) GPREG1 = 0;
andrewboyson 71:f621d2127216 88
andrewboyson 55:e18983651004 89 epochOffset = GPREG1 & 0x0000FFFF;
andrewboyson 57:4daf2e423b27 90 epochOffset64 = (clktime)epochOffset << CLK_TIME_ONE_SECOND_SHIFT;
andrewboyson 55:e18983651004 91 nextEpochMonth1970 = (GPREG1 & 0x0FFF0000) >> 16;
andrewboyson 51:826c58fbfaed 92 makeNextEpochUtc();
andrewboyson 55:e18983651004 93 nextLeapEnable = GPREG1 & 0x10000000;
andrewboyson 55:e18983651004 94 nextLeapForward = GPREG1 & 0x20000000;
andrewboyson 47:fd2af868c10a 95 }
andrewboyson 47:fd2af868c10a 96
andrewboyson 47:fd2af868c10a 97
andrewboyson 57:4daf2e423b27 98 clktime ClkUtcFromTai(clktime tai) { return tai - epochOffset64; }
andrewboyson 57:4daf2e423b27 99 clktime ClkUtcToTai (clktime utc) { return utc + epochOffset64; }
andrewboyson 46:d3d56cb47940 100
andrewboyson 57:4daf2e423b27 101 void ClkUtcCheckAdjustLeapSecondCount(clktime tai)
andrewboyson 46:d3d56cb47940 102 {
andrewboyson 51:826c58fbfaed 103 if (!nextLeapEnable) return; //Do nothing if leaps are disabled
andrewboyson 49:e4424cc18bcb 104
andrewboyson 57:4daf2e423b27 105 clktime utc = ClkUtcFromTai(tai);
andrewboyson 57:4daf2e423b27 106 clktime epochEnd = ClkUtcGetNextEpoch() - (nextLeapForward ? 0 : 1);
andrewboyson 49:e4424cc18bcb 107
andrewboyson 51:826c58fbfaed 108 if (utc < epochEnd) return; //Do nothing until reached the end of the current epoch
andrewboyson 49:e4424cc18bcb 109
andrewboyson 73:286a739f7c05 110 if (nextLeapForward) ClkUtcAddEpochOffsetWithUtcChange(+1); //repeat 59
andrewboyson 73:286a739f7c05 111 else ClkUtcAddEpochOffsetWithUtcChange(-1); //skip 59
andrewboyson 49:e4424cc18bcb 112
andrewboyson 49:e4424cc18bcb 113 ClkUtcSetNextLeapEnable(false);
andrewboyson 49:e4424cc18bcb 114
andrewboyson 46:d3d56cb47940 115 }