Beispielprogramm 7 (Allgemeine Dezimierung in der PLC)

Die EL3751/ ELM3xxx Klemmen können eine Dezimierung ihrer Basisabtastrate fmax nur durch ganzzahlige Vielfache durchführen, siehe dazu das Kapitel „Dezimierung“. Um auch beliebige andere Abtastraten fZiel < fmax für einen Kanal zu realisieren, kann beispielsweise wie folgt vorgegangen werden:

Im Folgenden dient das Beispielprogramm als Orientierungshilfe, das auch die XY-Darstellung im TwinCAT Scope enthält. Wegen der o.a. Problematik der nicht konstant vorhandenen Anzahl gültiger Abtastwerte liefert das Programm für das Scope das Array-Paar aVarDecResult_TS und aVarDecResult mit der gleichen Anzahl von Elementen wie für den Eingangswert aSamples_1(Wert = nOVS ). Kommt es in einem Taskdurchlauf zu weniger Werten, wird der letzte Wert einfach wiederholt eingetragen (entspricht etwa „sample & hold“). Für die Aufzeichnung wurde das ScopeView wie folgt konfiguriert:

Eigenschaft

Wert

ScopeNodeProperties

ViewDetauilLevel

ExtendedXYOnly

Record time

00:00:00:05

ChartXYNodeProperties

Default Display Width

0,00:00:00,050:000

Max Data Points

200000

XYChannelNodeProperties

Marks

On

Mark Size

5

Mark Color

(andere Farbe als „Line Color“)

Für eine veranschaulichende Darstellung wurde zunächst die Aufzeichnung des ScopeView gestartet und dann das Programm, das auf eine Sekunde begrenzt die Dezimierten Werte lieferte:

   IF nOVS_CycleCount = 1000000000 THEN
      ;
      bEnable := FALSE;// Stop after 1s just for recording
   ELSE
...

Diese Zeile kann selbstverständlich für weitere Anpassungen auskommentiert werden:

      //bEnable := FALSE;// Stop after 1s just for recording

 

Hinweise:

Beispiel: Stellt eine Klemme wie ELM3602‑0002 (2-Kanal-IEPE-Auswertung) einen Datenstrom mit Oversampling von 50 kSps bei 100µs Zykluszeit zur Verfügung, so kann dieser Beispielcode eine Dezimierung auf 44,1 kSps vornehmen. Im Beispielprogramm wären dazu die Zyklusticks in der Taskkonfiguration von 5 auf 1 sowie die entsprechende Programmvariable nTaskCycle_ns von 500000 auf 100000 zu ändern. Siehe folgenden Bildausschnitt des ScopeView XY:

Beispielprogramm 7 (Allgemeine Dezimierung in der PLC) 4:
Dezimierung von 20 µs (links) auf 22,675.. µs (rechts) mit ELM3602

Der Dezimierungsfaktor ist durch Eintrag des Wertes „50/44.1“ für nDecimationValue im Beispiel vorgegeben. Wird dieses Beispiel für die EL3751 mit 500 µs Zykluszeit und 5x Oversampling verwendet, wird das Abtastintervall von 100 µs, das von der EL3751 kommt, zu ca. 113,378.. µs umgerechnet. Dementsprechend ist dieses Beispiel ausgelegt.
Die Dezimierung im Programm ist frei wählbar und muss mit Oversamplingfaktor und Taskzykluszeit abgestimmt konfiguriert sein. Die Variable nOVS muss den gleichen Oversamplingfaktor enthalten, wie dieser über die Prozessdatenkonfiguration eingestellt ist.

Download Beispielprogramm 7:

Hinweis: Bei Verwendung einer EtherCAT-Box wie EPP35xx entfällt der EtherCAT-Koppler EK1100.

Allgemeiner Hinweis

Der Zeitpunkt der Passage der EtherCAT Frames an der Klemme unterliegt Schwankungen, dem EtherCAT‑Frame-Jitter. Falls diese Schwankungen groß im Verhältnis zur Zykluszeit sind, werden u.U. Daten verspätet von der Klemme abgeholt, es kommt in der Scope-Darstellung zu Aussetzern/ Dopplungen. Mit der TwinCAT EtherCAT Diagnose können solche Effekte diagnostiziert werden. In dem Beispielprogramm zur ELM3602 steht zu dieser Kontrolle die Variable nEqualTimeStampsCnt zur Verfügung, die inkrementiert wird, falls ein solcher Ausfall auftritt. Abhilfe schafft die Veränderung der DC ShiftTime der Klemme, siehe dazu die EtherCAT Systemdokumentation.

Deklarationsteil

// THIS CODE IS ONLY AN EXAMPLE - YOU HAVE TO CHECK APTITUDE FOR YOUR APPLICATION
PROGRAM MAIN
VAR CONSTANT
    // User decimation factor e.g. 50 to 44.1 kSps:
    nDecimationValue       :LREAL := 50/44.1; // 50/20;
    nOVS                   :BYTE := 5;        // Oversampling factor
    nTaskCycle_ns          :UDINT := 500000;  // PlcTask configured cycle time in ns
   
    nOVSTimeInterval_ns    :UDINT := LREAL_TO_UDINT(nTaskCycle_ns/nOVS); // OVS interval
    nDecTimeInterval_ns    :LREAL := nDecimationValue * nOVSTimeInterval_ns; // Decimation interval
END_VAR
VAR
   aSamples_1 AT%I*        :ARRAY[0..nOVS-1] OF DINT;     // Link to the terminal PDO
   aOVS_SampleSets         :ARRAY[0..(2*nOVS)-1] OF DINT; // 2 OVS sample sets
   
   nVarDecResult           :DINT;  // The calculated interpolated value
   tVarDecResult           :LREAL; // Decimation timestamp
   aVarDecResult           :ARRAY[0..nOVS-1] OF DINT; // Decimation result values
   aVarDecResult_TS        :ARRAY[0..nOVS-1] OF LREAL; // Decimation result timestamps
   
   nResultNoOfSamples      :BYTE; // This is for the user for further processing
   
   nDivVar                 :INT;  // Value for selection of the target input element
   tDecVar_InTaskCycle     :LREAL:=0; // Time span for all decimation timestamps within a task cycle
   i                       :BYTE:=0; // Common loop counter
   nDX                     :LREAL; // X-Difference: target input element to decimation element
   nDY                     :DINT;  // Y-Difference: two values for interpolation
   sVal                    :LREAL; // Slope for calculation of new value
   bEnable                 :BOOL:=FALSE; // Start/Stop conversion to decimation values
   nOVS_CycleCount         :ULINT := 0;  // Time value for every OVS sample
   // Values for testing
   bTEST_VALUES_ENABLED    :BOOL := FALSE; // No input value needed, if TRUE
   nPhi                    :LREAL := 1.4; // Start angle for sinus simulation
   // For visualization only:
   aOVS_Samples            :ARRAY[0..nOVS-1] OF DINT;  // 2 OVS sample sets (value)
   aOVS_Samples_TS         :ARRAY[0..nOVS-1] OF ULINT; // 2 OVS sample sets (timestamp)
END_VAR

Ausführungsteil

// 500 µs Task
FOR i:= 0 TO nOVS-1 DO
   // Shift OVS set to left and update on right:
   aOVS_SampleSets[i] := aOVS_SampleSets[i+nOVS];   // Transfer "samples set" to the left side
   IF bTEST_VALUES_ENABLED THEN
      // Simulate values:
      aOVS_SampleSets[i+nOVS] := LREAL_TO_DINT(1000000 * SIN(nPhi));
      nPhi := nPhi + 0.01;//0.003141592653;
   ELSE
      // Fill current new samples set on right:
      aOVS_SampleSets[i+nOVS] := aSamples_1[i];
   END_IF
END_FOR
IF bEnable THEN
   nResultNoOfSamples := 0; // Use for further processing
   
   FOR i := 0 TO nOVS-1 DO
      nDivVar := TRUNC_INT(tDecVar_InTaskCycle/nOVSTimeInterval_ns);
      // Check, if new value is in grid
      IF (nDivVar = i) THEN
         nResultNoOfSamples := nResultNoOfSamples + 1;
         
         // Calc slope by the left and right element values (dy/dx):   
         nDY := aOVS_SampleSets[i+1] - aOVS_SampleSets[i];
         sVal := DINT_TO_LREAL(nDY)/nOVSTimeInterval_ns;
         // Get the time (difference) from the left side element start to the desired time point:
         nDX := tDecVar_InTaskCycle
                 - TRUNC_INT(tDecVar_InTaskCycle/nOVSTimeInterval_ns)
                 * UDINT_TO_LREAL(nOVSTimeInterval_ns);
         // Calc timestamp
         tVarDecResult := nDX + ULINT_TO_LREAL(nOVS_CycleCount);
         // Calc new value:
         nVarDecResult :=
                  LREAL_TO_DINT(DINT_TO_LREAL(aOVS_SampleSets[i]) + sVal * nDX);
 
         // next decimation time step
         tDecVar_InTaskCycle := tDecVar_InTaskCycle + nDecTimeInterval_ns;
         tDecVar_InTaskCycle := tDecVar_InTaskCycle
                 - INT_TO_UDINT(TRUNC_INT(tDecVar_InTaskCycle/nTaskCycle_ns))
                 * nTaskCycle_ns;
      END_IF
      // Fill timestamp and new value allocated to the field element of its timestamp
      aVarDecResult_TS[i] := tVarDecResult;
      aVarDecResult[i] := nVarDecResult;
      // For visualization of the original input:
      aOVS_Samples[i] := aOVS_SampleSets[i];
      aOVS_Samples_TS[i] := nOVS_CycleCount;
      // Count the task cycle timestamp
      nOVS_CycleCount := nOVS_CycleCount + nOVSTimeInterval_ns;
   END_FOR
END_IF
IF nOVS_CycleCount = 1000000000 THEN
   bEnable := FALSE;// Stop after 1s just for recording
   IF NOT bEnable THEN
      bEnable := TRUE; // OVS‑Samples transferred complete into both array sets
   END_IF
END_IF