0

我正在开发一个应用程序,在该应用程序中我使用“F_GetSystemTime”对高精度数据进行时间戳记。此函数返回自 1601 年 1 月 1 日以来的 100ns 间隔数。

我还通过 NTP 服务器将本地 Windows 时间同步到全球网络时钟。这可确保日历系统时间不会长期漂移。

但是,我遇到了一个问题,“F_GetSystemTime”似乎与 Windows 时间不同步。在几周的时间里,我的“F_GetSystemTime”出现了明显的偏差。

有没有办法让“F_GetSystemTime”与我的 Windows 时间同步?

4

2 回答 2

0

您可以构建一个具有任务周期精度的内部计数器。假设您有一个 10 毫秒的 PLC 周期,您可以使用 10 毫秒的时间戳记录您的数据。如果您想更精确,您可以减少主要 plc 任务的周期时间,或者创建一个具有更快周期的单独任务。

另一种可能性是使用带有过采样的快速 IO,例如 el1262。

这是一个如何定义内部计数器的原始示例:

声明部分:

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

实现部分:

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 将显示如下内容:

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

为您提供 10 毫秒精度的正确时间,无需进一步同步。

然后,您可以对字符串进行润色,以便将其从不需要的字符(如 DT#、T# 或 ms)中清除。

于 2019-09-21T12:32:56.787 回答
0

使用FB_LocalSystemTimeFilippos 答案中的功能块。它以毫秒为单位为您提供当前时间,并每秒与 Windows 时钟同步dwCycle。这样,该块总是在调用时为您提供当前时间并保持自身同步。我认为您不需要任何缓冲区计算等。只需在每个 PLC 周期运行此代码即可。

如果您的循环时间为 10 毫秒,则每次调用输出将更改 10 毫秒。当然,这可能不是 99% 准确,但应该可以解决问题。当时间与 Windows 时间同步时,先前值和当前值之间的增量当然会有所变化。如果这不行,请将同步间隔增加到更小或更高的值。

输出是TIMESTRUCT,它包含日期和时间以及毫秒。只是SYSTEMTIME_TO_DT用来转换它。请记住,在该转换中毫秒是四舍五入的,因此它可能会更改秒数。您可以在之前将毫秒设置为 0SYSTEMTIME_TO_DT以保持秒数不变。

//...

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
于 2019-09-26T07:47:28.447 回答