Real time automation systems require time keeping functions
weAutSys offers all those possibilities. (For the aspect of timers see timer handling.) Its basic timing is done in milliseconds (ms) resolution and by counting ticks from start or system reset in a 32 bit internal counter. The absolute timing is done in seconds (s) resolution in terms of seconds since March 1 2008 00:00 UTC.
Some timing functions, values and types are defined for weAutSys' internals. But all may be exploited by application software.
weAutSys internal basic timing is milliseconds based. The milliseconds since system start / reset are counted as an unsigned 32 bit number.
All periodic tasking for automation cycles and all timers with milliseconds resolution is based on that "milliseconds since start" time. It will run on until power down or next reset and never be manipulated (adjusted) by any system operation.
The 32 bit unsigned number of milliseconds since system start will wrap around after a bit more than 49 days. This will limit the maximum period or runtime of weAutSys's timers with millisecond resolution to half that period; that is 24,8 days or 2.147.483.647 ms. Hence, for longer periods than three weeks use the timers with seconds resolution — a bearable restriction in most use cases.
weAutSys's relative timing is based on having the seconds since system start continuously counted. This (32 bit unsigned) value will wrap around in about 136 years giving about 68 years (unproblematic) timer periods. (At present we don't care on longer timers and do not expect a device running that long without reset or power down — but that may be too pessimistic.)
Timers with seconds (s) resolution that are duration / period oriented are based on this absolute time.
weAutSys's absolute seconds timing is measured as seconds since March 1 2008 00:00 UTC (as a 32 bit unsigned value). The offset from seconds since start is, of course, basically the systems reset / restart time.
To get the local time and date two further offsets are handled:
The time zone offset is best be set by the answer of an appropriate DHCP server. Using one DHCP server + the NTP server provided by this DHCP has the advantage of automatically having the same settings on the whole automation subnet.
Switching DST on and off is based on the currently valid EU or Northern America's summer time rules.
The reset time offset will be known at the first incoming date / time information (best by weAutSys's built in NTP client and a NTP server made known by DHCP). Till this first setting it will be preset by the software build time, and hence be more or less in the past. Applications dependent on date and time should check if this (first) time setting has occurred.
The runtime's absolute "zero date" (March 1 2008 00:00 UTC ) is 1204329600s in UNIX (1.1.1970 based) and (approximately) 3413318400s in NTP (1.1.1900 based) time. So there is just a simple offset to usual absolute time informations. The problem of leap seconds is not yet handled (but may be in future).
The wrap around of that absolute seconds time (in the year 2144) is in no way dealt with but the NTP wrap around in 2136 is.
All date time related timers with seconds (s) resolution are based on this absolute time.
Files | |
file | timing.h |
weAutSys definition of timing services | |
Data Structures | |
struct | datdur_t |
"Time since" as a structure: a duration or a date/time More... | |
struct | date_t |
Date structure: a date in our time. More... | |
struct | dst_rule_year_t |
Rule structure: DST rules for a given (set of) year(s) More... | |
Defines | |
#define | DEFAULT_START_TIME |
Reset (or default) start time. | |
#define | MARCH_2008_NTP 3413318400 |
March 2008 as NTP seconds. | |
#define | MARCH_2008_UNIX 1204329600 |
March 2008 as UNIX seconds. | |
#define | OFFSET2NTPTIME |
same as MARCH_2008_NTP | |
#define | OFFSET2UNIXTIME |
same as MARCH_2008_UNIX | |
#define | PRESC_TRIM_FAC |
Prescaled time factor to millisecond. | |
#define | secTime308Loc(x) |
Seconds since March 1st 2008 local time. | |
#define | startTime308UTC |
The reset / start time in seconds since March 1st 2008 UTC. | |
#define | VCO_DEFAULT 7 |
Trimming of timing oscillator. | |
#define | zoneOffsetSec |
The time zone offset in seconds. | |
#define | zoneOffsetZone |
zoneOffsetSec's byte [1] | |
Date calculation defines | |
#define | DAYS_IN_YEAR |
Days in a year. | |
#define | DAYS_IN4YEARS |
Days in four years. | |
#define | APRIL_OFF |
Offset of April. | |
#define | MAY_OFF |
Offset of May. | |
#define | JUNE_OFF |
Offset of June. | |
#define | JULY_OFF |
Offset of July. | |
#define | AUGUST_OFF |
Offset of August. | |
#define | SEPTEMBER_OFF |
Offset of September. | |
#define | OCTOBER_OFF |
Offset of October. | |
#define | NOVEMBER_OFF |
Offset of November. | |
#define | DECEMBER_OFF |
Offset of December. | |
#define | JANUARY_OFF |
Offset of (next) January. | |
#define | FEBRUARY_OFF |
Offset of (next) February. | |
#define | MARCH_2012 |
March 2012. | |
#define | MARCH_2016 |
March 2016. | |
#define | MARCH_2020 |
March 2020. | |
#define | MARCH_2100 |
March 2100. | |
Date handling functions | |
uint8_t | setDatByDays (date_t *datStr, uint16_t ds) |
Set date structure by days since since March 2008. | |
uint16_t | getDaysByDat (date_t *datStr) |
Get days since since March 2008 by date structure. | |
uint8_t | getMarchYearByDays (uint16_t *daysInYear, uint16_t ds) |
Get year starting March (includes next January and February) | |
Functions | |
uint8_t | cnt12u8_8 (void) __attribute__((pure)) |
Get the 12.8 (16) µs tick value (8 bit) | |
uint32_t | datdur2sec (datdur_t *timStr) |
Calculate seconds from a datdur_t structure. | |
const dst_rule_year_t * | getDSTrule (uint8_t year) |
Get the EU, US &c DST rule data for a given year. | |
uint32_t | getFATtime (void) |
The local time as FAT time. | |
uint8_t | getVCOsetting (void) __attribute__((pure)) |
Trimming of timing oscillator. | |
uint8_t | isEUdstSwitchDay (date_t *datStr) |
Is date represented in date structure EU DST switching day. | |
void | sec2datdur (datdur_t *timStr, uint32_t timSec) |
Convert seconds to a datdur_t structure. | |
uint32_t | secClock (void) __attribute__((always_inline)) |
The system's run time in seconds. | |
uint32_t | secTime308UTC (void) __attribute__((always_inline)) |
Seconds since March 1st 2008 UTC. | |
uint32_t | secTimeNtpUTC (uint32_t secTime308UTC) __attribute__((always_inline)) |
Convert to seconds since January 1st 1990 UTC (NTP time) | |
uint32_t | secTimeUnixUTC (uint32_t secTime308UTC) __attribute__((always_inline)) |
Convert to seconds since January 1st 1970 UTC (Unix time) | |
uint8_t | setDST (uint8_t dlt) |
Set if current time is DST. | |
void | setVCOnormal (void) __attribute__((always_inline)) |
Trimming of timing oscillator. | |
uint8_t | setZoneOffsetSec (uint32_t newOffset) |
Adjust / set zone offset. | |
void | slowVCOdown (void) __attribute__((always_inline)) |
Trimming of timing oscillator. | |
void | speedVCOup (void) __attribute__((always_inline)) |
Trimming of timing oscillator. | |
Variables | |
uint8_t | adjustUTCcount |
Count of secTime308Loc adjustments. | |
uint8_t | cn12u8 |
The 12,8 µs counter summand. | |
uint32_t | combinedOffset |
The combined offset (local - UTC) | |
uint8_t | isDST |
Is current time DST. | |
uint8_t | msIntTick |
The ms tick counter. |
#define PRESC_TRIM_FAC |
Prescaled time factor to millisecond.
By this factor the processor clock period pre-scaled by 256 will be the best <= approach to one millisecond.
For a 20 MHz machine the pre-sclaled clock period will be 12.8 µs and this factor 78. That will yield 998.4 µs which cal easily be slowed down by VCO to 1ms.
On a 16 MHz machine that will be 16µs, 62 and 992µs
#define VCO_DEFAULT 7 |
Trimming of timing oscillator.
The higher the value the faster the oscillator for the system 1 ms ticks will be running (in the mean). The relation is non-linear.
Background: This value is used to slow down respectively lengthen every 1st .. 255th millisecond tick period a tiny bit. All others will be a wee bit too fast.
Range: 1 .. 255 (0 would act as 256)
The best value for a 16 MHz machine is 2. The best setting for weAut_01 board with a good 20 MHz Quartz for ATmega1284's processor clock is 7.
#define secTime308Loc | ( | x | ) |
Seconds since March 1st 2008 local time.
This gives the local time in seconds since (Saturday) March 1st 2008 in local time — i.e. with zone offsets and summertime (DST) shifts.
#define startTime308UTC |
The reset / start time in seconds since March 1st 2008 UTC.
This is the systems reset time in seconds in seconds since weAutSys's zero date March 1st 2008 UTC. It will be initialised by DEFAULT_START_TIME and be adjusted by the first setting of time and date (e.g. by NTP client function) after reset.
#define zoneOffsetSec |
The time zone offset in seconds.
This is usually in multiples of hours (3600) and in the range of -12..+12 (*3600) — positive being east of Greenwich.
The value should usually set by DHCP, the reset / default is 3600, i.e. CET.
#define DEFAULT_START_TIME |
Reset (or default) start time.
This absolute time in seconds since March 1st 2008 that will usually be set as last compile time (in the file maketime_utc.h by utc_maketime.bat).
This time will be used as reset value for the internal seconds clock.
Of course it will be quite wrong (i.e. more or less in past) until any adjustments by human command (CLI), time server answers or else are available.
DEFAULT_START_TIME
can be used as date and time of make but will usually differ from SYST_DAT, which is the "official" (sub) version date.
#define MARCH_2008_NTP 3413318400 |
March 2008 as NTP seconds.
This is the NTP date for March 1 2008 00:00 UTC.
In weAutSys's seconds based absolute time this is the zero date.
#define MARCH_2008_UNIX 1204329600 |
March 2008 as UNIX seconds.
This is the UNIX date for March 1 2008 00:00 UTC (weAutSys's zero date).
#define DAYS_IN_YEAR |
Days in a year.
It is of course 365; a leap year will be 1 day longer.
This leap day, the 29th of February (29.2.leapYear), would better have been put at the end of the year by the popes. That is in effect done so by clever calendar algorithms (not invented here but heavily used in weAutSys).
#define DAYS_IN4YEARS |
Days in four years.
It is of course 4 * 365 + 1.
#define APRIL_OFF |
Offset of April.
Days from March 1st to April 1st (same year).
#define MAY_OFF |
Offset of May.
Days from March 1st to May 1st (same year).
#define JUNE_OFF |
Offset of June.
Days from March 1st to June 1st (same year).
#define AUGUST_OFF |
Offset of August.
#define SEPTEMBER_OFF |
Offset of September.
#define OCTOBER_OFF |
Offset of October.
#define NOVEMBER_OFF |
Offset of November.
#define DECEMBER_OFF |
#define JANUARY_OFF |
#define FEBRUARY_OFF |
Offset of (next) February.
Days from March 1st to February 1st (next year).
#define MARCH_2012 |
March 2012.
This is the number of days at march 1st 2012 (1.3.2012 / 2012-03-01) counted since March 1st 2008 (1.3.2008 / 2008-03-01).
#define MARCH_2016 |
March 2016.
This is the number of days at march 1st 2016 (1.3.2016 / 2016-03-01) counted since March 1st 2008 (1.3.2008 / 2008-03-01).
#define MARCH_2020 |
March 2020.
This is the number of days at march 1st 2020 (1.3.2020 / 2020-03-01) counted since March 1st 2008 (1.3.2008 / 2008-03-01).
#define MARCH_2100 |
March 2100.
This is the number of days at march 1st 2100 (1.3.2100 / 2100-03-01) counted since March 1st 2008 (1.3.2008 / 2008-03-01).
void setVCOnormal | ( | void | ) |
Trimming of timing oscillator.
This function will set the "value controlled oscillator" for the system's 1 ms tick to normal (usually best) speed.
uint8_t getVCOsetting | ( | void | ) |
Trimming of timing oscillator.
This function returns the current speed of the "value controlled oscillator" for the system's 1 ms tick. VCO_DEFAULT is usually normal / best setting; higher values are faster.
void speedVCOup | ( | void | ) |
Trimming of timing oscillator.
This function will speed up the "value controlled oscillator" for the system's 1 ms tick a little bit. This is for catching up quite little differences to an externally given absolute (NTP e.g.) time.
Little differences in that sense are below -200 ms.
See the note at setVCOnormal.
void slowVCOdown | ( | void | ) |
Trimming of timing oscillator.
This function will slow down the "value controlled oscillator" for the system's 1 ms tick a little bit. This is for catching up quite little differences to an externally given absolute (NTP e.g.) time.
Little differences in that sense are below +100 ms.
See the note at setVCOnormal.
uint8_t cnt12u8_8 | ( | void | ) |
Get the 12.8 (16) µs tick value (8 bit)
This function returns the actual 12.8 µs respectively 16 µs tick count. This value will wrap about every 3.27 ms on a 20 MHz machine and every 4 ms at 16 MHz. One tick (increment) is 256 processor clocks.
It might be used for quite exact measures of software execution times the normal milliseconds resolution is of little use for.
Hint: 12.8 µs is correct for a 20 MHz machine. For slower machines the counted period is proportionally longer. For 16 MHz it's 16 µs.
uint32_t secClock | ( | void | ) |
The system's run time in seconds.
The value will be handled (incremented) by system software (i.e. the system's 1s thread). Monotony is guaranteed. There may be gaps, but that is very improbable — all miss-behaving (application) software leading to this should get a watchdog reset.
Do not modify the underlying variable! See also the warning.
uint32_t getFATtime | ( | void | ) |
The local time as FAT time.
The local time contains zone offsets and summertime (DST) shifts. This function returns this time in a 32 bit (uint32_t) packed structure used for the traditional FAT file systems. Note that this (old DOS) time format has a 2s resolution only. The format is:
bit 31..25: year from 1980 (0..127; 2012 is 32)
bit 24..21: month (1..12)
bit 20..16: day in month (1..31)
bit 15..11: hour (0..23)
bit 10...5: minute (0..59)
bit 4...0: second / 2 (0..29, 30 if leap second)
uint32_t secTime308UTC | ( | void | ) |
Seconds since March 1st 2008 UTC.
The value will be the time in seconds since weAutSys's zero date March 1st 2008 UTC.
UTC here means without any zone or DST offsets.
The returned value can easily be converted to UNIX or NTP time.
uint32_t secTimeUnixUTC | ( | uint32_t | secTime308UTC | ) |
Convert to seconds since January 1st 1970 UTC (Unix time)
The returned value will be time in seconds since the UNIX time start date.
secTime308UTC | the time in seconds since March 2008 UTC |
uint32_t secTimeNtpUTC | ( | uint32_t | secTime308UTC | ) |
Convert to seconds since January 1st 1990 UTC (NTP time)
The returned value is the time in seconds since the NTP time start date. Called with secClock() as parameter stamp
this value would be used as a NTP server respectively indirectly adjusted by the NTP client function.
secTime308UTC | the time in seconds since March 2008 UTC |
uint8_t setDST | ( | uint8_t | dlt | ) |
uint8_t setZoneOffsetSec | ( | uint32_t | newOffset | ) |
Adjust / set zone offset.
This function sets zoneOffsetSec and handles all side effects in the case of a change.
void sec2datdur | ( | datdur_t * | timStr, |
uint32_t | timSec | ||
) |
Convert seconds to a datdur_t structure.
This function converts a 32 bit unsigned seconds value to a time structure of type datdur_t. Its interpretation as duration or absolute date and time depends on the parameter's semantic.
Hint: This is a quite expensive operation for an 8 bit RISC machine without divide instruction. The result timStr
should be kept if usable more than once.
timStr | pointer to the time structure to be set |
timSec | the time in seconds |
uint32_t datdur2sec | ( | datdur_t * | timStr | ) |
Calculate seconds from a datdur_t structure.
This function converts the content of a structure of type datdur_t to the corresponding 32 bit unsigned seconds value. It is the inverse of sec2datdur().
Hint: This function is not quite cheap for an 8 bit RISC machine with just a 8bit * 8bit = 16bit multiply instruction. The returned result should be kept if needed more than once.
timStr | pointer to the time structure to be used |
uint8_t setDatByDays | ( | date_t * | datStr, |
uint16_t | ds | ||
) |
Set date structure by days since since March 2008.
This function sets the (16 bit unsigned) number of days since March 2008 into the structure datStr and all its other fields.
datStr | pointer to the date structure to be set |
ds | days since weAutSys's day 0 |
uint16_t getDaysByDat | ( | date_t * | datStr | ) |
Get days since since March 2008 by date structure.
This calculates the (16 bit unsigned) number of days since March 2008 from the structure and returns the result. All fields of datStr except day of week (ed) are used and must have correct and consistent values.
Before March 2008 0 is returned as is for everything after about ~2177.
datStr | pointer to the date structure to be used |
uint8_t getMarchYearByDays | ( | uint16_t * | daysInYear, |
uint16_t | ds | ||
) |
Get year starting March (includes next January and February)
ds | days since weAutSys's day 0 |
daysInYear | pointer to extra result: the the days in the respective year (0 = March 1st); if NULL this information is lost |
const dst_rule_year_t* getDSTrule | ( | uint8_t | year | ) |
Get the EU, US &c DST rule data for a given year.
This function returns a pointer to the DST rule structure for the year requested in flash memory (!). Use pgm_read_byte() respectively pgm_read_dword() accordingly to get single elements. Or — to have the whole struture in RAM use
dst_rule_year_t currentRuleValues; memcpy_P(¤tRuleValues, getDSTrule(year), sizeof(dst_rule_year_t));
year | 0..255 interpreted as 2000..2255 |
uint8_t isEUdstSwitchDay | ( | date_t * | datStr | ) |
Is date represented in date structure EU DST switching day.
This function checks if the date is either the last Sunday in March (returns 3) or the last Sunday in October (returns 10) or non of both (returns 0).
datStr | pointer to the date structure |
uint8_t msIntTick |
The ms tick counter.
This variable is written, i.e. incremented, in the tick interrupt handler ISR(TIMER0_COMPA_vect)
only.
This variable must never (!) be modified elsewhere.
Being just one byte reading it is atomic.
uint8_t cn12u8 |
The 12,8 µs counter summand.
Incremented in the 1ms interrupt by PRESC_TRIM_FAC (or PRESC_TRIM_FAC + 1 if VCO slow).
This variable is not to be used directly by user / application software. Use it idirectly by cnt12u8_8().
uint8_t adjustUTCcount |
Count of secTime308Loc adjustments.
Significant local time adjustments are counted here. 0 means no time date setting since reset (hence usually incorrect).
Counting wraps from 255 to 1.
Significant means hard setting of secTime308Loc or its ms (0.999) fraction part msAbsClockCount. Adjustments by variable controlled oscillator (VCO) are not counted.
uint32_t combinedOffset |
The combined offset (local - UTC)
This is the difference from local time to UTC.
In other words it is the sum zoneOffsetSec + DST offset
Do never ever modify this value directly. That may spoil timers and more.
uint8_t isDST |