0

I'm working on an application where I am time-stamping high precision data using the "F_GetSystemTime". This function returns the number of 100ns intervals since January 1st 1601.

I also have my local Windows time synchronized to a global network clock via an NTP server. This ensures that the calendar system time does not drift long-term.

However, I'm running into a problem where "F_GetSystemTime" does not seem to be synchronized with the Windows time. There is noticeable drift in my "F_GetSystemTime" over the course of weeks.

Is there a way to keep "F_GetSystemTime" synchronized with my Windows time?

Izzo
  • 4,461
  • 13
  • 45
  • 82

2 Answers2

0

You could build an internal counter with the precision of your task cycle. Let's say you have a plc cycle of 10ms, you can log your data with a 10ms timestamp. If you want to be more precise, you can either go down with cycle time of your main plc task or create a separate task with a faster cycle.

Another possibility is to use fast IOs with oversampling like the el1262.

Here as a raw example of how you could define your internal counter:

Declaration part:

PROGRAM MAIN
VAR
    bInit           : BOOL;
    nTime           : UINT;
    tBufferTime     : TIME;
    dtBufferDT      : DT;
    nCalcBuffer     : UDINT;
    sMs             : STRING;
    sLogTime        : STRING;
    sLogTimeWithMs  : STRING;
    stSystemTime    : TIMESTRUCT;
    fbLocalTime     : FB_LocalSystemTime;
END_VAR

Implementation part:

fbLocalTime(bEnable := TRUE);
IF NOT bInit
THEN
    dtBufferDT := SYSTEMTIME_TO_DT(fbLocalTime.systemTime);
    IF fbLocalTime.bValid 
    THEN
        bInit := TRUE;
    END_IF
ELSE
    nTime := nTime + 1;
    tBufferTime := UINT_TO_TIME(nTime*10);
    IF tBufferTime = T#1S
    THEN
        //Add a second to your system time
        ntime := 0;
        nCalcBuffer := DT_TO_UDINT(dtBufferDT)+1;
        dtBufferDT := UDINT_TO_DT(nCalcBuffer);
        sLogTime := DT_TO_STRING(dtBufferDT);
        sLogTimeWithMs := sLogTime;
    ELSE
        //Add ms string time-stamp
        sMs := TIME_TO_STRING(tBufferTime);
        sLogTimeWithMs := CONCAT(sLogTime,sMs);
    END_IF
END_IF

sLogTimeWithMs will show something like this:

'DT#2019-09-21-14:30:28T#530ms'

giving you the correct time with a 10ms precision without any need of further synchronization.

You can then polish the string in order to clear it from unwanted chars like DT#,T# or ms.

Filippo Boido
  • 1,136
  • 7
  • 11
0

Use the FB_LocalSystemTime function block as in Filippos answer. It gives you the current time with milliseconds and syncronises to Windows clock every dwCycle seconds. In that way the block always gives you the current time when called and keeps itself syncronized. I don't think you need any buffer calculations etc. Just run this code every PLC cycle.

If your cycle time is 10ms, the output will change by 10 ms every call. Of course this is not probably 99% accurate, but should do the trick. When the time is synced to Windows time, the delta between previous and current value will of course change a bit. If that's not OK, increase the sync interval to smaller or higher value.

The output is TIMESTRUCT, it contains date and time as well as milliseconds. Just use SYSTEMTIME_TO_DT to convert it. Remember that milliseconds are rounded in that conversion so it might change the seconds. You can set the milliseconds to 0 before SYSTEMTIME_TO_DT to keep the seconds as they are.

//...

VAR
    LocalSystemTime     : FB_LocalSystemTime;
    FirstTimeUpdateOK   : BOOL; //True after the time was synced OK at least once
    EmptyDateAndTime    : TIMESTRUCT := (wYear := 1970, wMonth := 1, wDay := 1);
END_VAR
VAR_OUTPUT
    SyncedTime          : TIMESTRUCT;
END_VAR




//First, set current time to 1970 if not updated yet.
IF NOT FirstTimeUpdateOK THEN
    SyncedTime := EmptyDateAndTime;
END_IF


//Call the time sync block
LocalSystemTime(
    bEnable := TRUE,    //Keep as TRUE always
    dwCycle := 5        //Sync with windows clock every 5 seconds (change to smaller/higher value to prevent small changes every 5 seconds)
);


IF LocalSystemTime.bValid THEN
    //Everything OK, we have valid time
    FirstTimeUpdateOK:= TRUE;
    SyncedTime := LocalSystemTime.systemTime;


ELSIF FirstTimeUpdateOK THEN
    //We have once had the valid time, but not now. Show error?
    //...
END_IF
Quirzo
  • 1,183
  • 8
  • 10