【问题标题】:TwinCAT 3: Synchronizing "F_GetSystemTime" with local Windows time?TwinCAT 3:将“F_GetSystemTime”与本地 Windows 时间同步?
【发布时间】:2021-08-27 21:36:42
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: twincat


    【解决方案1】:

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

    另一种可能性是使用像 el1262 这样具有过采样的快速 IO。

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

    声明部分:

    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。

    【讨论】:

      【解决方案2】:

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

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

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

      //...
      
      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
      

      【讨论】:

        猜你喜欢
        • 2021-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-07
        • 2012-09-25
        • 2017-01-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多