Andrew Boyson / clock

Dependents:   oldheating gps motorhome heating

Files at this revision

API Documentation at this revision

Comitter:
andrewboyson
Date:
Thu Nov 29 16:51:19 2018 +0000
Parent:
30:212ca42b8779
Child:
32:f915ccb1ece3
Commit message:
Changed time.c to tm.c

Changed in this revision

clock.c Show annotated file Show diff for this revision Revisions of this file
clock.txt Show annotated file Show diff for this revision Revisions of this file
text.txt Show diff for this revision Revisions of this file
tick.c Show annotated file Show diff for this revision Revisions of this file
tick.h Show annotated file Show diff for this revision Revisions of this file
time-formats.txt Show annotated file Show diff for this revision Revisions of this file
time.c Show diff for this revision Revisions of this file
time.h Show diff for this revision Revisions of this file
timer.c Show annotated file Show diff for this revision Revisions of this file
timer.h Show annotated file Show diff for this revision Revisions of this file
tm.c Show annotated file Show diff for this revision Revisions of this file
tm.h Show annotated file Show diff for this revision Revisions of this file
--- a/clock.c	Fri Feb 16 17:30:46 2018 +0000
+++ b/clock.c	Thu Nov 29 16:51:19 2018 +0000
@@ -1,11 +1,11 @@
 #include <stdint.h>
 #include <stdio.h>
 
-#include  "log.h"
-#include "tick.h"
-#include "sync.h"
-#include "time.h"
-#include  "rtc.h"
+#include   "log.h"
+#include  "tick.h"
+#include  "sync.h"
+#include    "tm.h"
+#include   "rtc.h"
 #include "timer.h"
 
 #define ONE_BILLION 1000000000
@@ -24,7 +24,7 @@
 static time_t  nowT     = 0;
 static void calculateTimes()
 {
-    nowTicks = Ticks();
+    nowTicks = TicksNow();
     nowT = nowTicks >> TICK_ONE_SECOND_SHIFT;
 }
 
@@ -44,17 +44,17 @@
 
 void ClockTmLocal(struct tm* ptm)
 {
-    TimeToTmLocal(nowT, ptm);
+    TmLocalFromTimeT(nowT, ptm);
 }
 void ClockTmUtc(struct tm* ptm)
 {
-    TimeToTmUtc(nowT, ptm);
+    TmUtcFromTimeT(nowT, ptm);
 }
 
 void ClockAscii(char* p)
 {
     struct tm tm;
-    TimeToTmUtc(nowT, &tm);
+    TmUtcFromTimeT(nowT, &tm);
     sprintf(p, "%d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
 }
 void ClockInit()
@@ -74,13 +74,13 @@
 {
     //Establish this scan time
     static uint32_t lastCount = 0;
-    
+
     bool firstScan = !lastCount;
-    
+
     uint32_t elapsed = TimerIntervalCount(&lastCount);
-    
+
     if (firstScan) return;
-    
+
     //Average the scan time
     if (elapsed > ClockScanAverage) ClockScanAverage++;
     if (elapsed < ClockScanAverage) ClockScanAverage--;
@@ -93,32 +93,32 @@
 {
     TimerMain();
     TickMain();
-    
+
     //Establish the scan times
     establishScanTimes();
-    
+
     //Calculate ns and time_t
     calculateTimes();
-    
+
     static int lastSyncedRate = 0;
     static int lastSyncedTime = 0;
-    
+
     if ( SyncedRate && !lastSyncedRate) LogTimeF("Rate sync acquired\r\n");
     if (!SyncedRate &&  lastSyncedRate) LogTimeF("Rate sync lost\r\n");
     if ( SyncedTime && !lastSyncedTime) LogTimeF("Time sync acquired\r\n");
     if (!SyncedTime &&  lastSyncedTime) LogTimeF("Time sync lost\r\n");
-    
+
     lastSyncedRate = SyncedRate;
     lastSyncedTime = SyncedTime;
-         
+
     //Record the time the clock started
     if (SyncedTime && SyncedRate) refTicks = nowTicks;
-        
+
     //Set a one shot memory for having had a tick
     static time_t lastT = 0;
     ClockTicked = lastT > 0 && lastT != nowT;
     lastT = nowT;
-    
+
     if (TickIsSet())
     {
         //Save the time to the RTC on the second
@@ -139,7 +139,7 @@
             RtcGetTm(&tm);
             if (lastRtcSecond > 0 && tm.tm_sec != lastRtcSecond)
             {
-                time_t t = TimeFromTmUtc(&tm);
+                time_t t = TmUtcToTimeT(&tm);
                 TickSet((int64_t)t << TICK_ONE_SECOND_SHIFT);
                 calculateTimes();
                 LogTimeF("Clock set from RTC\r\n");
--- a/clock.txt	Fri Feb 16 17:30:46 2018 +0000
+++ b/clock.txt	Thu Nov 29 16:51:19 2018 +0000
@@ -1,17 +1,39 @@
-Clock era is 1970
-Ntp era is 1900
+Clock
+=====
+clock.c contains
+* settings
+* functions to save and restore the time to the RTC.
 
-Clock time is a 64 bit signed int counting ns since 1970                               ==> 1970 +/- 292 years ==> 1678 --> 2262
-Ntp time is a 32 bit unsigned int (with a 32 bit fraction) counting seconds since 1900 ==> 1900 +   136 years ==> 1900 --> 2036
-
-To eliminate the need to do multiplication and division by a billion we need to use a binary fraction with bit shifts
+High resolution timer
+=======================
+timer.c uses TIM0 as a 32bit timer which counts at the cpu frequency 96MHz and rolls over after 44s.
+It is used for three purposes:
+* A general purpose high resolution timer with a (1/96MHz) 10nS resolution and a maximum interval of 44 seconds
+* A one second pulse for use by governed time (see time.c) 
+* A general purpose low resolution elapsed time of about 20mS 
 
-36bits for seconds 28bits for fraction
-ie SSSS SSSS S.FFF FFFF
-would give +/- 1089 years with a resolution of 3ns
+Governed time
+=============
+tick.c receives a tick each second from timer.c. It increments the governed time using the ppb and slew (see by sync.c).
+When the governed 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 governed time format.
+PPB is stored in GPREG0 whenever it is set and retrieved during initialisation.
+
+time_t and struct tm utilities
+==============================
+time.c contains a number of functions for manipulating time_t and struct tm times. It contains nothing for governed time or the high resolution count.
 
-34bits for seconds 30bits for fraction
-would give +/- 272 years with a resolution of 1ns
+Synchronisation to an external source
+=====================================
+sync.c takes external time from either:
+* a long term source such as NTP
+* a pulse per second (PPS)
+It adjusts the governed time ppb and slew to synchronise the external source and the governed time. 
 
-44 bits for seconds 20 bits for fraction
-would give +/- 278,731 years with a resolution of 1us
\ No newline at end of file
+Backup over power down
+======================
+rtc.c contains routines to save and restore the time in the battery backed real time clock.
+
+NTP utilities
+=============
+Converts governed time to NTP time and vice versa.
\ No newline at end of file
--- a/text.txt	Fri Feb 16 17:30:46 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-Current system counts ns from 1970. It needs much arithmetic o convert between NTP and time_t.
-
-To eliminate the need to replace multiplication and division by a billion with bit shifts we need to use a binary fraction
-
-1 bit sign, 43 bits for seconds, 20 bits for fraction
-would give +/- 278,731 years at 1us or 1 ppm per second or 3 ppb per hour
-Advantages:   a wide coverage
-              simple transformation to NTP and time_t
-Disadvantage: not able to reflect the freq adjustments for pps.
-
-1 bit sign, 35bits for seconds, 28bits for fraction
-would give +/- 1089 years at 3ns or 3ppb per second
-ie SSSS SSSS S.FFF FFFF
-Advantages:   easily represented in hex
-              a wide coverage
-              simple transformation to NTP and time_t
-Disadvantage: borderline to reflect the freq adjustments for pps.
-
-1 bit sign, 33 bits for seconds, 30 bits for fraction
-would give +/- 272 years at 1ns or 1 ppb per second
-Advantages:   adequately representing the freq adjustments for pps
-              simple transformation to NTP and time_t
-              can use a bitwise shift to give an approximation to ns, us or ms
-              adequately covers the next two centuries
-Disadvantage: none
-
-Use 96MHz int64 count
-would give +/- 3044 years with a resolution of 10ns or 10ppm per second
-Advantages:   a wide coverage
-Disadvantage: not able to reflect the freq adjustments for pps.
-
-Use a count of ns
-would give +/- 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: 2^32 / 96MHz = 44s so 1 tick will be 44s
--- a/tick.c	Fri Feb 16 17:30:46 2018 +0000
+++ b/tick.c	Thu Nov 29 16:51:19 2018 +0000
@@ -2,7 +2,7 @@
 #include <stdbool.h>
 
 #include "rtc.h"
-#include "time.h"
+#include "tm.h"
 #include "tick.h"
 #include "timer.h"
 #include "led.h"
@@ -20,7 +20,7 @@
 
 bool    TickIsSet() { return countIsSet; }
 bool    Ticked()    { return ticked; }
-int64_t Ticks()     { return ticksThisScan; } //30th bit is one second
+int64_t TicksNow()  { return ticksThisScan; } //30th bit is one second
 
 int32_t TickGetSlew() { return slew; }
 void    TickSetSlew(int32_t value) { slew = value; }
@@ -32,16 +32,16 @@
 void    TicksToTmUtc  (int64_t ticks, struct tm* ptm)
 {
     time_t t = ticks >> TICK_ONE_SECOND_SHIFT;
-    TimeToTmUtc(t, ptm);
+    TmUtcFromTimeT(t, ptm);
 }
 int64_t TicksFromTmUtc(struct tm* ptm)
 {
-    time_t t = TimeFromTmUtc(ptm);
+    time_t t = TmUtcToTimeT(ptm);
     return t << TICK_ONE_SECOND_SHIFT;
 }
 
 
-/*
+/* Timer counts like this:
 +---------+---------------+
 | Seconds |   Base count  |
 +---------+---------------+
@@ -53,6 +53,9 @@
 |   45    |    25,032,704 |
 |   ...   |       ...     |
 +---------+---------------+
+Tick counts as a signed 64 bit integer
+1 bit sign, 33 bits for seconds, 30 bits for fraction
+giving +/- 272 years with a resolution of around approximately 1ns or 1 ppb per second (2^30 == 1024x1024x1024)
 */
 
 void TickSet(int64_t extClock)
@@ -73,7 +76,7 @@
 void TickMain()
 {    
     //Update the times whenever there has been a system second
-    if (TimerTicked)
+    if (TimerHadSecond)
     { 
         __disable_irq();
                tickCount += TICK_ONE_SECOND + ppb;
--- a/tick.h	Fri Feb 16 17:30:46 2018 +0000
+++ b/tick.h	Thu Nov 29 16:51:19 2018 +0000
@@ -14,7 +14,7 @@
 extern int32_t TickGetPpb (void); extern void TickSetPpb (int32_t value); extern void TickAddPpb(int32_t value);
 extern bool    TickIsSet(void);
 extern bool    Ticked(void);
-extern int64_t Ticks(void);
+extern int64_t TicksNow(void);
 
 extern void    TickInit(void);
 extern void    TickMain(void);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/time-formats.txt	Thu Nov 29 16:51:19 2018 +0000
@@ -0,0 +1,49 @@
+Current system counts ns from 1970. It needs much arithmetic o convert between NTP and time_t.
+
+To eliminate the need to replace multiplication and division by a billion with bit shifts we need to use a binary fraction
+
+NTP time
+========
+Ntp era is 1900
+Ntp time is a 32 bit unsigned int (with a 32 bit fraction) counting seconds since 1900 ==> 1900 +   136 years ==> 1900 --> 2036
+
+Governed time - the version chosen
+==================================
+Clock era is 1970
+1 bit sign, 33 bits for seconds, 30 bits for fraction
+
+Governed time - alternatives considered
+=======================================
+1 bit sign, 43 bits for seconds, 20 bits for fraction
+would give +/- 278,731 years at 1us or 1 ppm per second or 3 ppb per hour
+Advantages:   a wide coverage
+              simple transformation to NTP and time_t
+Disadvantage: not able to reflect the freq adjustments for pps.
+
+1 bit sign, 35bits for seconds, 28bits for fraction
+would give +/- 1089 years at 3ns or 3ppb per second
+ie SSSS SSSS S.FFF FFFF
+Advantages:   easily represented in hex
+              a wide coverage
+              simple transformation to NTP and time_t
+Disadvantage: borderline to reflect the freq adjustments for pps.
+
+1 bit sign, 33 bits for seconds, 30 bits for fraction
+would give +/- 272 years at 1ns or 1 ppb per second
+Advantages:   adequately representing the freq adjustments for pps
+              simple transformation to NTP and time_t
+              can use a bitwise shift to give an approximation to ns, us or ms
+              adequately covers the next two centuries
+Disadvantage: none
+
+Use 96MHz int64 count
+would give +/- 3044 years with a resolution of 10ns or 10ppm per second
+Advantages:   a wide coverage
+Disadvantage: not able to reflect the freq adjustments for pps.
+
+Use a count of ns
+would give +/- 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: 2^32 / 96MHz = 44s so 1 tick will be 44s
--- a/time.c	Fri Feb 16 17:30:46 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,309 +0,0 @@
-#include <stdlib.h>
-#include <time.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-#define STD_OFFSET 0
-#define DST_OFFSET 1
-
-static bool isLeapYear(int year)
-{
-    year += 1900;
-    bool leapYear = !(year & 0x3);
-    if (year >= 2100)
-    {
-        if (year % 100 == 0) leapYear = false;
-        if (year % 400 == 0) leapYear =  true;
-    }
-    return leapYear;
-
-}
-static int monthLength(int year, int month)
-{
-    static char monthlengths[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-    int daysInMonth = monthlengths[month];
-    if (month == 1 && isLeapYear(year)) daysInMonth++; //February is month 1 of months 0 to 11
-    return daysInMonth;
-}
-static bool isDst(int year, int month, int dayOfMonth, int dayOfWeek, int hours)
-{
-    //Find the last Sunday in the month
-    int lastDayOfMonth = monthLength(year, month);
-    int daysToEndOfMonth = lastDayOfMonth - dayOfMonth;
-    int dayOfWeekOfLastDayOfMonth = (dayOfWeek + daysToEndOfMonth) % 7;
-    int lastSundayDayOfMonth = lastDayOfMonth - dayOfWeekOfLastDayOfMonth;
-
-    //Check each month
-    if (month <= 1) return false;                  //Jan, Feb
-    if (month == 2)                                //Mar - DST true after 1am UTC on the last Sunday in March
-    {
-        if (dayOfMonth <  lastSundayDayOfMonth) return false;
-        if (dayOfMonth == lastSundayDayOfMonth) return hours >= 1;
-        if (dayOfMonth >  lastSundayDayOfMonth) return true;
-    }
-    if (month >= 3 && month <= 8)     return true; //Apr, May, Jun, Jul, Aug, Sep
-    if (month == 9)                                //Oct - DST false after 1am UTC on the last Sunday in October
-    {
-        if (dayOfMonth <  lastSundayDayOfMonth) return true;
-        if (dayOfMonth == lastSundayDayOfMonth) return hours < 1;
-        if (dayOfMonth >  lastSundayDayOfMonth) return false;
-    }
-    if (month >= 10) return false;                  //Nov, Dec
-    return false;
-}
-static void calculateDayOfYearAndWeek(int thisYear, int thisMonth, int thisMonthDay, int* pDayOfYear, int* pDayOfWeek)
-{
-    int dayOfYear = 0;    //1 Jan is day 0
-    int dayOfWeek = 4;    //1 Jan 1970 is a Thursday
-    
-    //Add days of each whole year
-    for (int y = 70; y < thisYear; y++)
-    {
-        int lengthOfYear = isLeapYear(y) ? 366 : 365;
-        dayOfWeek += lengthOfYear;
-    }
-    
-    //Add days of each whole month
-    for (int m = 0; m < thisMonth; m++)
-    {
-        int lengthOfMonth = monthLength(thisYear, m);
-        dayOfYear += lengthOfMonth;
-        dayOfWeek += lengthOfMonth;
-    }
-    
-    //Add days of part month
-    thisMonthDay--; //thisMonthDay is 01 to 31 where we need 00 to 30
-    dayOfYear += thisMonthDay;
-    dayOfWeek += thisMonthDay;
-    
-    //Update the day of year and day of week parts of the struct tm
-    *pDayOfYear = dayOfYear;     // 0 --> 365
-    *pDayOfWeek = dayOfWeek % 7; // 0 --> 6
-}
-void TimeIncrement(struct tm* ptm)
-{
-    ptm->tm_sec++;
-    if (ptm->tm_sec > 59)
-    {
-        ptm->tm_sec = 0;
-        ptm->tm_min++;
-    }
-    if (ptm->tm_min > 59)
-    {
-        ptm->tm_min = 0;
-        ptm->tm_hour++;
-    }
-    if (ptm->tm_hour > 23)
-    {
-        ptm->tm_hour = 0;
-        ptm->tm_wday++;
-        if (ptm->tm_wday > 6) ptm->tm_wday = 0;
-        ptm->tm_yday++;
-        ptm->tm_mday++;
-        if (ptm->tm_mday > monthLength(ptm->tm_year, ptm->tm_mon))
-        {
-            ptm->tm_mon++;
-            if (ptm->tm_mon > 11)
-            {
-                ptm->tm_year++;
-                ptm->tm_yday = 0;
-                ptm->tm_mon = 0;
-            }
-            ptm->tm_mday = 1;
-        }
-    }
-}
-static void normalise(int* pHours, int* pDayOfWeek, int* pDayOfMonth, int* pMonth, int * pDayOfYear, int* pYear)
-{
-    if (*pHours > 23)
-    {
-        *pHours -= 24;
-        ++*pDayOfWeek;
-        if (*pDayOfWeek > 6) *pDayOfWeek = 0;
-        ++*pDayOfYear;
-        ++*pDayOfMonth;
-        if (*pDayOfMonth > monthLength(*pYear, *pMonth))
-        {
-            ++*pMonth;
-            if (*pMonth > 11)
-            {
-                ++*pYear;
-                *pDayOfYear = 0;
-                *pMonth = 0;
-            }
-            *pDayOfMonth = 1;
-        }
-    }
-    
-    if (*pHours < 0)
-    {
-        *pHours += 24;
-        --*pDayOfWeek;
-        if (*pDayOfWeek < 0) *pDayOfWeek = 6;
-        --*pDayOfYear;
-        --*pDayOfMonth;
-        if (*pDayOfMonth < 1)
-        {
-            --*pMonth;
-            if (*pMonth < 0)
-            {
-                --*pYear;
-                *pDayOfYear = isLeapYear(*pYear) ? 365 : 364;
-                *pMonth = 11;
-            }
-            *pDayOfMonth = monthLength(*pYear, *pMonth);
-        }
-    }
-}
-static void addYears(int* pYear, int* pDayOfWeek, int* pDaysLeft)
-{
-    while(1)
-    {
-        //See if it is a leap year
-        int leapYear = isLeapYear(*pYear);
-        
-        //Find the number of days in this year
-        int daysInYear = leapYear ? 366 : 365;
-        
-        //Stop if this is the final year
-        if (*pDaysLeft < daysInYear) break;
-        
-        //Calculate the current day of the week at the start of the year
-        *pDayOfWeek += leapYear ? 2 : 1;
-        if (*pDayOfWeek >= 7) *pDayOfWeek -= 7;
-        
-        //Move on to the next year
-        *pDaysLeft -= daysInYear;
-        ++*pYear;
-    }
-}
-static void addMonths(int year, int* pMonth, int* pDaysLeft)
-{
-    while(1)
-    {
-        int daysInMonth = monthLength(year, *pMonth);
-        
-        //Stop if this is the last month
-        if (*pDaysLeft < daysInMonth) break;
-        
-        //Move onto next month
-        *pDaysLeft -= daysInMonth;
-        ++*pMonth;
-    }
-}
-static void timeToTm(time_t t, struct tm* ptm, bool local)
-{
-    //Extract the seconds, minutes, hours and days from the time_t t
-    div_t divres;
-    divres = div(          t, 60);    int seconds  = divres.rem;
-    divres = div(divres.quot, 60);    int minutes  = divres.rem;
-    divres = div(divres.quot, 24);    int hours    = divres.rem;
-                                      int daysLeft = divres.quot;
-    
-    //Add a year at a time while there is more than a year of days left
-    int year      = 70; //Unix epoch is 1970
-    int dayOfWeek = 4;  //1 Jan 1970 is a Thursday
-    addYears(&year, &dayOfWeek, &daysLeft);
-    
-    //Days left contains the days left from the start (1 Jan) of the current year
-    int dayOfYear = daysLeft;
-    dayOfWeek += daysLeft;
-    dayOfWeek %= 7;
-
-    //Add a month at a time while there is more than a month of days left
-    int month = 0;
-    addMonths(year, &month, &daysLeft);
-    
-    //Days left contains the days left from the start (1st) of the current month
-    int dayOfMonth = daysLeft + 1;
-          
-    //Deal with local time offsets
-    int dst;
-    if (local)
-    {
-        //Work out if Daylight Saving Time applies
-        dst = isDst(year, month, dayOfMonth, dayOfWeek, hours);
-        
-        //Adjust for the timezone
-        hours += dst ? DST_OFFSET : STD_OFFSET;
-        normalise(&hours, &dayOfWeek, &dayOfMonth, &month, &dayOfYear, &year);
-    }
-    else
-    {
-        dst = -1;
-    }
-    
-    //Set up the broken time TM structure
-    ptm->tm_sec   = seconds;       // 00 --> 59
-    ptm->tm_min   = minutes;       // 00 --> 59
-    ptm->tm_hour  = hours;         // 00 --> 23
-    ptm->tm_mday  = dayOfMonth;    // 01 --> 31
-    ptm->tm_mon   = month;         // 00 --> 11
-    ptm->tm_year  = year;          // Years since 1900
-    ptm->tm_wday  = dayOfWeek;     // 0 --> 6 where 0 == Sunday
-    ptm->tm_yday  = dayOfYear;     // 0 --> 365
-    ptm->tm_isdst = dst;           // +ve if DST, 0 if not DSTime, -ve if the information is not available. Note that 'true' evaluates to +1.
-}
-void TimeToTmUtc(time_t time, struct tm* ptm)
-{
-    timeToTm(time, ptm, false);
-}
-void TimeToTmLocal(time_t time, struct tm* ptm)
-{
-    timeToTm(time, ptm, true);
-}
-time_t TimeFromTmUtc(struct tm* ptm)
-{
-    int days = 0;
-    
-    for (int y = 70; y < ptm->tm_year; y++) days += isLeapYear(y) ? 366 : 365;
-    
-    days += ptm->tm_yday;
-    
-    return days         * 86400 + 
-           ptm->tm_hour *  3600 + 
-           ptm->tm_min  *    60 + 
-           ptm->tm_sec;
-}
-int TimePeriodBetween(struct tm* ptmLater, struct tm* ptmEarlier)
-{
-    int days = 0;
-    
-    if (ptmLater->tm_year > ptmEarlier->tm_year) for (int y = ptmEarlier->tm_year; y < ptmLater->tm_year; y++) days += isLeapYear(y) ? 366 : 365;
-    else                                         for (int y = ptmEarlier->tm_year; y > ptmLater->tm_year; y--) days -= isLeapYear(y) ? 366 : 365;
-    
-    days += ptmLater->tm_yday - ptmEarlier->tm_yday;
-    
-    return  days                                     * 86400 + 
-           (ptmLater->tm_hour - ptmEarlier->tm_hour) *  3600 + 
-           (ptmLater->tm_min  - ptmEarlier->tm_min ) *    60 + 
-           (ptmLater->tm_sec  - ptmEarlier->tm_sec );
-}
-
-void TimeTmUtcToLocal(struct tm* ptm)
-{    
-    //Establish DST
-    ptm->tm_isdst = isDst(ptm->tm_year, ptm->tm_mon, ptm->tm_mday, ptm->tm_wday, ptm->tm_hour);
-        
-    //Adjust for the timezone
-    ptm->tm_hour += ptm->tm_isdst ? DST_OFFSET : STD_OFFSET;
-    normalise(&ptm->tm_hour, &ptm->tm_wday, &ptm->tm_mday, &ptm->tm_mon, &ptm->tm_yday, &ptm->tm_year);
-}
-
-void TimeAsciiDateTimeToTm(const char* pDate, const char* pTime, struct tm* ptm) // Convert compile time to system time 
-{
-    //__DATE__ The string constant contains eleven characters and looks like "Feb 12 1996". If the day of the month is less than 10, it is padded with a space on the left. 
-    char month[5];
-    sscanf(pDate, "%s %d %d", month, &ptm->tm_mday, &ptm->tm_year); ptm->tm_year -= 1900;
-    
-    // Find where month is in month_names. Deduce month value. 
-    static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
-    ptm->tm_mon = (strstr(month_names, month) - month_names) / 3;
-        
-    //__TIME__ The string constant contains eight characters and looks like "23:59:01".     
-    sscanf(pTime, "%2d %*c %2d %*c %2d", &ptm->tm_hour, &ptm->tm_min, &ptm->tm_sec);
-
-    //Fill the day of week and the day of year part of the tm structure
-    calculateDayOfYearAndWeek(ptm->tm_year, ptm->tm_mon, ptm->tm_mday, &ptm->tm_yday, &ptm->tm_wday);
-}
--- a/time.h	Fri Feb 16 17:30:46 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-#include <time.h>
-#include <stdint.h>
-
-extern void   TimeTmUtcToLocal(struct tm* ptm);
-
-extern void   TimeAsciiDateTimeToTm(const char* pDate, const char* pTime, struct tm* ptm);
-extern void   TimeToTmLocal(uint32_t time, struct tm* ptm);
-extern void   TimeToTmUtc(uint32_t time, struct tm* ptm);
-
-extern time_t TimeFromTmUtc(struct tm* ptm);
-extern int    TimePeriodBetween(struct tm* ptmLater, struct tm* ptmEarlier);
\ No newline at end of file
--- a/timer.c	Fri Feb 16 17:30:46 2018 +0000
+++ b/timer.c	Thu Nov 29 16:51:19 2018 +0000
@@ -47,13 +47,25 @@
     return (value * fraction) >> 32;
 }
 
-bool TimerTicked = false;
+bool TimerHadSecond = false;
+
+//Counts from zero to 2^32 and wraps around after:
+// 13.7 years if  10 per second - scan time must be less than 100mS
+// 1.37 years if 100 per second - scan time must be less than  10mS
+uint32_t TimerTicks = 0;
 
 void TimerMain()
 {
-    TimerTicked = TimerCountSinceLastSecond() > TIMER_COUNT_PER_SECOND;
+    TimerHadSecond = TimerCountSinceLastSecond() > TIMER_COUNT_PER_SECOND;
+    if (TimerHadSecond) secondsBaseCount        += TIMER_COUNT_PER_SECOND;
+    
+    static uint32_t tickBaseCount = 0;
     
-    if (TimerTicked) secondsBaseCount += TIMER_COUNT_PER_SECOND;
+    if (TC - tickBaseCount > TIMER_COUNT_PER_SECOND / TIMER_TICKS_PER_SECOND)
+    {
+        TimerTicks++;
+        tickBaseCount += TIMER_COUNT_PER_SECOND / TIMER_TICKS_PER_SECOND;
+    }
 }
 void TimerInit()
 {    
--- a/timer.h	Fri Feb 16 17:30:46 2018 +0000
+++ b/timer.h	Thu Nov 29 16:51:19 2018 +0000
@@ -8,10 +8,11 @@
 
 extern uint32_t TimerCountSinceLastSecond(void);
 extern int32_t  TimerMultiplyFractionalPart(int32_t value, uint32_t timerCountSinceLastSecond);
-extern bool     TimerTicked;
+extern bool     TimerHadSecond;
 extern void     TimerMain(void);
 extern void     TimerInit(void);
 
 #define TIMER_COUNT_PER_SECOND 96000000UL
 #define TIMER_COUNT_PER_MS     (TIMER_COUNT_PER_SECOND / 1000);
 #define TIMER_COUNT_PER_US     (TIMER_COUNT_PER_MS     / 1000);
+#define TIMER_TICKS_PER_SECOND 50
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tm.c	Thu Nov 29 16:51:19 2018 +0000
@@ -0,0 +1,309 @@
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define STD_OFFSET 0
+#define DST_OFFSET 1
+
+static bool isLeapYear(int year)
+{
+    year += 1900;
+    bool leapYear = !(year & 0x3);
+    if (year >= 2100)
+    {
+        if (year % 100 == 0) leapYear = false;
+        if (year % 400 == 0) leapYear =  true;
+    }
+    return leapYear;
+
+}
+static int monthLength(int year, int month)
+{
+    static char monthlengths[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+    int daysInMonth = monthlengths[month];
+    if (month == 1 && isLeapYear(year)) daysInMonth++; //February is month 1 of months 0 to 11
+    return daysInMonth;
+}
+static bool isDst(int year, int month, int dayOfMonth, int dayOfWeek, int hours)
+{
+    //Find the last Sunday in the month
+    int lastDayOfMonth = monthLength(year, month);
+    int daysToEndOfMonth = lastDayOfMonth - dayOfMonth;
+    int dayOfWeekOfLastDayOfMonth = (dayOfWeek + daysToEndOfMonth) % 7;
+    int lastSundayDayOfMonth = lastDayOfMonth - dayOfWeekOfLastDayOfMonth;
+
+    //Check each month
+    if (month <= 1) return false;                  //Jan, Feb
+    if (month == 2)                                //Mar - DST true after 1am UTC on the last Sunday in March
+    {
+        if (dayOfMonth <  lastSundayDayOfMonth) return false;
+        if (dayOfMonth == lastSundayDayOfMonth) return hours >= 1;
+        if (dayOfMonth >  lastSundayDayOfMonth) return true;
+    }
+    if (month >= 3 && month <= 8)     return true; //Apr, May, Jun, Jul, Aug, Sep
+    if (month == 9)                                //Oct - DST false after 1am UTC on the last Sunday in October
+    {
+        if (dayOfMonth <  lastSundayDayOfMonth) return true;
+        if (dayOfMonth == lastSundayDayOfMonth) return hours < 1;
+        if (dayOfMonth >  lastSundayDayOfMonth) return false;
+    }
+    if (month >= 10) return false;                  //Nov, Dec
+    return false;
+}
+static void calculateDayOfYearAndWeek(int thisYear, int thisMonth, int thisMonthDay, int* pDayOfYear, int* pDayOfWeek)
+{
+    int dayOfYear = 0;    //1 Jan is day 0
+    int dayOfWeek = 4;    //1 Jan 1970 is a Thursday
+    
+    //Add days of each whole year
+    for (int y = 70; y < thisYear; y++)
+    {
+        int lengthOfYear = isLeapYear(y) ? 366 : 365;
+        dayOfWeek += lengthOfYear;
+    }
+    
+    //Add days of each whole month
+    for (int m = 0; m < thisMonth; m++)
+    {
+        int lengthOfMonth = monthLength(thisYear, m);
+        dayOfYear += lengthOfMonth;
+        dayOfWeek += lengthOfMonth;
+    }
+    
+    //Add days of part month
+    thisMonthDay--; //thisMonthDay is 01 to 31 where we need 00 to 30
+    dayOfYear += thisMonthDay;
+    dayOfWeek += thisMonthDay;
+    
+    //Update the day of year and day of week parts of the struct tm
+    *pDayOfYear = dayOfYear;     // 0 --> 365
+    *pDayOfWeek = dayOfWeek % 7; // 0 --> 6
+}
+void TimeIncrement(struct tm* ptm)
+{
+    ptm->tm_sec++;
+    if (ptm->tm_sec > 59)
+    {
+        ptm->tm_sec = 0;
+        ptm->tm_min++;
+    }
+    if (ptm->tm_min > 59)
+    {
+        ptm->tm_min = 0;
+        ptm->tm_hour++;
+    }
+    if (ptm->tm_hour > 23)
+    {
+        ptm->tm_hour = 0;
+        ptm->tm_wday++;
+        if (ptm->tm_wday > 6) ptm->tm_wday = 0;
+        ptm->tm_yday++;
+        ptm->tm_mday++;
+        if (ptm->tm_mday > monthLength(ptm->tm_year, ptm->tm_mon))
+        {
+            ptm->tm_mon++;
+            if (ptm->tm_mon > 11)
+            {
+                ptm->tm_year++;
+                ptm->tm_yday = 0;
+                ptm->tm_mon = 0;
+            }
+            ptm->tm_mday = 1;
+        }
+    }
+}
+static void normalise(int* pHours, int* pDayOfWeek, int* pDayOfMonth, int* pMonth, int * pDayOfYear, int* pYear)
+{
+    if (*pHours > 23)
+    {
+        *pHours -= 24;
+        ++*pDayOfWeek;
+        if (*pDayOfWeek > 6) *pDayOfWeek = 0;
+        ++*pDayOfYear;
+        ++*pDayOfMonth;
+        if (*pDayOfMonth > monthLength(*pYear, *pMonth))
+        {
+            ++*pMonth;
+            if (*pMonth > 11)
+            {
+                ++*pYear;
+                *pDayOfYear = 0;
+                *pMonth = 0;
+            }
+            *pDayOfMonth = 1;
+        }
+    }
+    
+    if (*pHours < 0)
+    {
+        *pHours += 24;
+        --*pDayOfWeek;
+        if (*pDayOfWeek < 0) *pDayOfWeek = 6;
+        --*pDayOfYear;
+        --*pDayOfMonth;
+        if (*pDayOfMonth < 1)
+        {
+            --*pMonth;
+            if (*pMonth < 0)
+            {
+                --*pYear;
+                *pDayOfYear = isLeapYear(*pYear) ? 365 : 364;
+                *pMonth = 11;
+            }
+            *pDayOfMonth = monthLength(*pYear, *pMonth);
+        }
+    }
+}
+static void addYears(int* pYear, int* pDayOfWeek, int* pDaysLeft)
+{
+    while(1)
+    {
+        //See if it is a leap year
+        int leapYear = isLeapYear(*pYear);
+        
+        //Find the number of days in this year
+        int daysInYear = leapYear ? 366 : 365;
+        
+        //Stop if this is the final year
+        if (*pDaysLeft < daysInYear) break;
+        
+        //Calculate the current day of the week at the start of the year
+        *pDayOfWeek += leapYear ? 2 : 1;
+        if (*pDayOfWeek >= 7) *pDayOfWeek -= 7;
+        
+        //Move on to the next year
+        *pDaysLeft -= daysInYear;
+        ++*pYear;
+    }
+}
+static void addMonths(int year, int* pMonth, int* pDaysLeft)
+{
+    while(1)
+    {
+        int daysInMonth = monthLength(year, *pMonth);
+        
+        //Stop if this is the last month
+        if (*pDaysLeft < daysInMonth) break;
+        
+        //Move onto next month
+        *pDaysLeft -= daysInMonth;
+        ++*pMonth;
+    }
+}
+static void timeToTm(time_t t, struct tm* ptm, bool local)
+{
+    //Extract the seconds, minutes, hours and days from the time_t t
+    div_t divres;
+    divres = div(          t, 60);    int seconds  = divres.rem;
+    divres = div(divres.quot, 60);    int minutes  = divres.rem;
+    divres = div(divres.quot, 24);    int hours    = divres.rem;
+                                      int daysLeft = divres.quot;
+    
+    //Add a year at a time while there is more than a year of days left
+    int year      = 70; //Unix epoch is 1970
+    int dayOfWeek = 4;  //1 Jan 1970 is a Thursday
+    addYears(&year, &dayOfWeek, &daysLeft);
+    
+    //Days left contains the days left from the start (1 Jan) of the current year
+    int dayOfYear = daysLeft;
+    dayOfWeek += daysLeft;
+    dayOfWeek %= 7;
+
+    //Add a month at a time while there is more than a month of days left
+    int month = 0;
+    addMonths(year, &month, &daysLeft);
+    
+    //Days left contains the days left from the start (1st) of the current month
+    int dayOfMonth = daysLeft + 1;
+          
+    //Deal with local time offsets
+    int dst;
+    if (local)
+    {
+        //Work out if Daylight Saving Time applies
+        dst = isDst(year, month, dayOfMonth, dayOfWeek, hours);
+        
+        //Adjust for the timezone
+        hours += dst ? DST_OFFSET : STD_OFFSET;
+        normalise(&hours, &dayOfWeek, &dayOfMonth, &month, &dayOfYear, &year);
+    }
+    else
+    {
+        dst = -1;
+    }
+    
+    //Set up the broken time TM structure
+    ptm->tm_sec   = seconds;       // 00 --> 59
+    ptm->tm_min   = minutes;       // 00 --> 59
+    ptm->tm_hour  = hours;         // 00 --> 23
+    ptm->tm_mday  = dayOfMonth;    // 01 --> 31
+    ptm->tm_mon   = month;         // 00 --> 11
+    ptm->tm_year  = year;          // Years since 1900
+    ptm->tm_wday  = dayOfWeek;     // 0 --> 6 where 0 == Sunday
+    ptm->tm_yday  = dayOfYear;     // 0 --> 365
+    ptm->tm_isdst = dst;           // +ve if DST, 0 if not DSTime, -ve if the information is not available. Note that 'true' evaluates to +1.
+}
+void TmUtcFromTimeT(time_t time, struct tm* ptm)
+{
+    timeToTm(time, ptm, false);
+}
+void TmLocalFromTimeT(time_t time, struct tm* ptm)
+{
+    timeToTm(time, ptm, true);
+}
+time_t TmUtcToTimeT(struct tm* ptm)
+{
+    int days = 0;
+    
+    for (int y = 70; y < ptm->tm_year; y++) days += isLeapYear(y) ? 366 : 365;
+    
+    days += ptm->tm_yday;
+    
+    return days         * 86400 + 
+           ptm->tm_hour *  3600 + 
+           ptm->tm_min  *    60 + 
+           ptm->tm_sec;
+}
+int TmSecondsBetween(struct tm* ptmLater, struct tm* ptmEarlier)
+{
+    int days = 0;
+    
+    if (ptmLater->tm_year > ptmEarlier->tm_year) for (int y = ptmEarlier->tm_year; y < ptmLater->tm_year; y++) days += isLeapYear(y) ? 366 : 365;
+    else                                         for (int y = ptmEarlier->tm_year; y > ptmLater->tm_year; y--) days -= isLeapYear(y) ? 366 : 365;
+    
+    days += ptmLater->tm_yday - ptmEarlier->tm_yday;
+    
+    return  days                                     * 86400 + 
+           (ptmLater->tm_hour - ptmEarlier->tm_hour) *  3600 + 
+           (ptmLater->tm_min  - ptmEarlier->tm_min ) *    60 + 
+           (ptmLater->tm_sec  - ptmEarlier->tm_sec );
+}
+
+void TmUtcToLocal(struct tm* ptm)
+{    
+    //Establish DST
+    ptm->tm_isdst = isDst(ptm->tm_year, ptm->tm_mon, ptm->tm_mday, ptm->tm_wday, ptm->tm_hour);
+        
+    //Adjust for the timezone
+    ptm->tm_hour += ptm->tm_isdst ? DST_OFFSET : STD_OFFSET;
+    normalise(&ptm->tm_hour, &ptm->tm_wday, &ptm->tm_mday, &ptm->tm_mon, &ptm->tm_yday, &ptm->tm_year);
+}
+
+void TmFromAsciiDateTime(const char* pDate, const char* pTime, struct tm* ptm) // Convert compile time to system time 
+{
+    //__DATE__ The string constant contains eleven characters and looks like "Feb 12 1996". If the day of the month is less than 10, it is padded with a space on the left. 
+    char month[5];
+    sscanf(pDate, "%s %d %d", month, &ptm->tm_mday, &ptm->tm_year); ptm->tm_year -= 1900;
+    
+    // Find where month is in month_names. Deduce month value. 
+    static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+    ptm->tm_mon = (strstr(month_names, month) - month_names) / 3;
+        
+    //__TIME__ The string constant contains eight characters and looks like "23:59:01".     
+    sscanf(pTime, "%2d %*c %2d %*c %2d", &ptm->tm_hour, &ptm->tm_min, &ptm->tm_sec);
+
+    //Fill the day of week and the day of year part of the tm structure
+    calculateDayOfYearAndWeek(ptm->tm_year, ptm->tm_mon, ptm->tm_mday, &ptm->tm_yday, &ptm->tm_wday);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tm.h	Thu Nov 29 16:51:19 2018 +0000
@@ -0,0 +1,11 @@
+#include <time.h>
+#include <stdint.h>
+
+extern void   TmUtcToLocal(struct tm* ptm);
+
+extern void   TmFromAsciiDateTime(const char* pDate, const char* pTime, struct tm* ptm);
+extern void   TmLocalFromTimeT(uint32_t time, struct tm* ptm);
+extern void   TmUtcFromTimeT(uint32_t time, struct tm* ptm);
+
+extern time_t TmUtcToTimeT(struct tm* ptm);
+extern int    TmSecondsBetween(struct tm* ptmLater, struct tm* ptmEarlier);
\ No newline at end of file