Datenbaustein beschreiben

nutellahase

Level-2
Beiträge
180
Reaktionspunkte
28
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo.

Mal angenommen ich habe einen Datenbaustein (Test_DB) mit einem Array [1..100] of Bool (Test). Zu jedem Index ist ein fester Eingang zugeordnet. Allerdings liegen diese Eingänge nicht hintereinander, sondern immer nur jeder 2. Eingang.
Ich brauch alle 100 Werte immer jeden Zyklus, daher gibt es jetzt meiner Meinung nach zwei Möglichkeiten:

1. Möglichkeit - alles in AWL runtertippen

Code:
U E0.0
= "Test_DB".Test[1]

U E0.2
= "Test_DB".Test[2]

....

U E24.4
= "Test_DB".Test[99]

U E24.6
= "Test_DB".Test[100]

Vorteil: Ich finde alle Eingänge in meinen Referenzdaten.
Nachteil: Enorm viel Tipparbeit, bei einer Erweiterung der Array Dimension muss ich ebenfalls alles wieder kopieren und händisch ausbessern (fehleranfällig).

2. Möglichkeit - Anwendung einer Schleife (vorzugsweise in SCL) - ist natürlich eine von vielen Lösungen..

Code:
FUNCTION "Test":VOID

VAR_OUTPUT
    Liste: ARRAY [1..100] OF BOOL;  // hier wird dann von außen mein Test-Array beschalten
END_VAR

VAR_TEMP
    Index: INT;
    Byteadresse: INT;
    Bitadresse: INT;
END_VAR

BEGIN

FOR Index := 1 TO 100 DO
    Byteadresse := ((Index - 1) * 2) / 8;        
    Bitadresse  := ((Index - 1) * 2) MOD 8;     // es wird immer nur in 2er schritten gezählt 0,2,4,6, 0,2,4,6,...
    Liste[Index]:= E[Byteadresse,Bitadresse];
END_FOR;

END_FUNCTION

Vorteil: Bei einer Erweiterung der Array-Dimension muss man nur den Endwert der Schleife ändern (z.B. von 100 auf 200). Die Adresse wird abhängig vom Index immer richtig berechnet.
Nachteil: In den Referenzdaten finde ich keinen einzigen Hinweis auf die Verwendung der Eingänge E0.0, E0.2, E0.4, ... E24.4, E24.6 (auch nicht unter Überlappender Zugriff auf Speicherbereiche!!)

Jetzt auch mal eine Frage zur Zykluszeit.. Würde es sichtliche Auswirkungen haben wenn ich Möglichkeit 1 durch Möglichkeit 2 ersetze??? Ob mein Programm jetzt Zeile für Zeile abgearbeitet wird oder innerhalb der Schleife hin- und herspringt müsste dann schon egal sein oder ??

Welche Methode würdet ihr verwenden???
 
Hallo,
die Vor- und Nachteile hast du schon erwähnt.
Ich würde keine der beiden Möglichkeiten wählen sondern immer die Eingänge selbst im Programm verwenden.

Deine Möglichkeit 1 ist viel Tipparbeit - die SCL-Variante schön überschaubar und schnell getippt. Da ich aber weiß (oder zu wissen meine) wie SCL solche Zugriffe zusammen bastelt würde ich behaupten, dass das SCL-Script sehr wahrscheinlich mehr Zeit in Anspruch nehmen wird, da aus Allem, was da vorkommt hinterher Pointer werden - und die sind in der Operationszeit nicht unbedingt schnell ...

Gruß
Larry
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich sehe das wie Larry, ich würde auch keine der beiden Varianten nehmen.

Und wenn es unbedingt Not tut, würde auf keine Fall die SCL-Variante nehmen, da die Zugriffe auf die Eingänge überhaupt nicht in den Referenzdaten erscheinen.

Wahrscheinlich würde ich mir mit Excel deine erste Variante zusammenbasteln und als AWL reinkopieren.

Was spricht gegen die Verwendung der Eingänge?
 
Hallo ihr beiden.

Ich muss zugeben, bei diesem Beispiel würde das nicht viel Sinn machen. In erster Linie ging es mir da mehr um die Zykluszeit (ob es hier um ms oder nur ein paar µs geht wenn man Möglichkeit 1 durch 2 ersetzt).
In der Realität ist es so, dass sich hinter dem Array of Bool eigentlich ein Array [1..100] of UDT befindet. Daher erfolgt in einem Baustein das umrangieren in den DB, damit man in weiterer Folge in SCL immer auf den UDT und den Index zugreifen kann! z.B. "Test_Db".Test[index].Initiator.

Momentan ist bei uns Möglichkeit 1 ausgeführt. Da aber bald eben eine Erweiterung der Array-Dimension ansteht, haben wir uns eine weitere Variante überlegt (nämlich die Möglichkeit 2). Und da ist eben die Frage aufgetaucht, ob es nicht egal ist, wenn nun alles in einer Wurst runtergeschrieben wird oder eben in einer Schleife abgearbeitet wird.

Bei Möglichkeit 1 ist mir noch eingefallen/aufgefallen, dass für den seltenen Fall eines defekten Einganges die SCL Variante nicht mehr klappen würde:
Code:
U E30.0 // E0.0 ist defekt
= "Test_DB".Test[1]

U E0.1
="Test_DB".Test[2]

...

Zugegeben die Wahrscheinlichkeit ist sehr gering.. aber das würde dann eher wieder für Variante 1 sprechen.
 
Ich würde es schon in einer Schleife bearbeiten, in so einem Fall allerdings in einer FC in AWL und über AR1/AR2. Alles andere wäre mir zu viel Schreibarbeit, von der Fehlerträchtigkeit mal ganz abgesehen.

Etwa so:

Code:
//*** AR1 sichern
      TAR1  #TEMP_AR1
      SET   
      SAVE  

//*** AR1 auf Eingänge
      L     P##E_POINTER
      LAR1  
//    L     W [AR1,P#0.0]
//    T     #TEMP_INT
//    AUF   DB [#TEMP_INT]
      L     D [AR1,P#2.0]
      LAR1  

//*** AR2 auf Datenfeld
      L     P##DB_POINTER
      LAR2  
      L     W [AR2,P#0.0]
      T     #TEMP_INT
      AUF   DI [#TEMP_INT]
      L     D [AR2,P#2.0]
      LAR2  

//*** ANZAHL Bit kopieren
      L     #ANZAHL
LOO1: T     #TEMP_INT
      U     E [AR1,P#0.0]
      =     DIX [AR2,P#0.0]
      +AR1  P#0.2
      +AR2  P#0.1
      L     #TEMP_INT
      LOOP  LOO1

//*** AR1-Register wiederherstellen
      LAR1  #TEMP_AR1

Schöner wäre es noch mit einem ANY-Pointer.


Falls ein Eingang mal defekt ist, kannst du ihn im Abbild überschreiben. Am besten am Anfang des OB1 und mit einem eindeutigen Kommentar.
Code:
      U     E     30.0
      =     E      0.0                  // E0.0 ist defekt !!!


Gruß, Onkel
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Bin nicht so fit in AWL aber ich versuche es trotzdem mal, bitte um Korrektur wenn ich falsch liege!

Ich würde es schon in einer Schleife bearbeiten, in so einem Fall allerdings in einer FC in AWL und über AR1/AR2. Alles andere wäre mir zu viel Schreibarbeit, von der Fehlerträchtigkeit mal ganz abgesehen.

Etwa so:

Code:
//*** AR1 sichern
      TAR1  #TEMP_AR1
      SET   
      SAVE  

//*** AR1 auf Eingänge
      L     P##E_POINTER[COLOR=#ff0000] // ist also vom Datentyp Pointer oder??[/COLOR]
      LAR1               [COLOR=#ff0000]// im Pointerformat ins AR1 laden[/COLOR]
//    L     W [AR1,P#0.0][COLOR=#ff0000] // zwar auskommentiert aber: Hier würdest du die DB-Nummer (siehe Aufbau des Parametertyps Pointer) in den Akku 1 laden[/COLOR]
//    T     #TEMP_INT     [COLOR=#ff0000]// zwar auskommentiert aber: Nummer des DBs zwischenspeichern[/COLOR]
//    AUF   DB [#TEMP_INT] [COLOR=#ff0000]// zwar auskommentiert aber: DB öffnen im DB-Register müsste nun die DB-Nummer stehen die ich übergeben habe.[/COLOR]
      L     D [AR1,P#2.0] [COLOR=#ff0000]// lade Bereichskennung und Startadresse in den Akku1[/COLOR]
      LAR1              [COLOR=#ff0000]// AR1 mit Bereichsübergreifenden Pointer (Speicherbereich und Startadresse) laden[/COLOR]

//*** AR2 auf Datenfeld
      L     P##DB_POINTER  [COLOR=#ff0000]// das gleiche für den zweiten Pointer, nur hier werden das AR2 und DI-Register verwendet![/COLOR]
      LAR2  
      L     W [AR2,P#0.0]
      T     #TEMP_INT
      AUF   DI [#TEMP_INT]
      L     D [AR2,P#2.0]
      LAR2  

//*** ANZAHL Bit kopieren
      L     #ANZAHL
LOO1: T     #TEMP_INT
      U     [COLOR=#ff0000][B]E[/B][/COLOR] [AR1,P#0.0]   [COLOR=#ff0000]  // Brauch ich hier den Speicherbereich ??? Wurde nicht ein Bereichsübergreifender Pointer ins AR geladen???[/COLOR]
      =     [COLOR=#ff0000][B]DIX[/B][/COLOR] [AR2,P#0.0] 
      +AR1  P#0.2
      +AR2  P#0.1
      L     #TEMP_INT
      LOOP  LOO1

//*** AR1-Register wiederherstellen
      LAR1  #TEMP_AR1

Schöner wäre es noch mit einem ANY-Pointer.


Falls ein Eingang mal defekt ist, kannst du ihn im Abbild überschreiben. Am besten am Anfang des OB1 und mit einem eindeutigen Kommentar.
Code:
      U     E     30.0
      =     E      0.0                  // E0.0 ist defekt !!!


Gruß, Onkel

Parameterübergabe wäre dann: an E_Pointer := E0.0 und an DB_Pointer := "Test_DB".Test ???

mit freundlichen Grüßen
 
Hallo Hase,

alles richtig. Auch mit dem Bereichsübergreifenden Pointer hast du recht. Da nur ein DB geöffnet werden muß, kannst du diesen einen auch als "DB" (und nicht als "DI") öffnen. Dann kannst du beide Bereichskennungen weg lassen. Falls man jedoch zwei DBs benötigt, dann muß man zumindest bei dem "DI" die Bereichskennung explizit angeben. Das Problem ist dass in beiden Fällen im Pointer-Parameter immer ein "DB" steht. Das DI-Register wird so zu sagen zum öffnen eines zweiten DBs missbraucht. In deinem Fall ist das eigentlich nicht notwendig.


Gruß, Onkel
 
Zuletzt bearbeitet:
Ich würde keine der beiden Möglichkeiten wählen sondern immer die Eingänge selbst im Programm verwenden.

Deine Möglichkeit 1 ist viel Tipparbeit

Die Eingänge selbst zu verwenden, wäre demnach extrem viel "Tipparbeit" - der kompletten Code je Eingang muss 100x erzeugt werden. Wenn ich generell so programmieren würde, hätte ich auch viel bessere Zykluszeiten - größtenteils durch die größeren CPUs, die ich dann brauche.;)

Ich würde mich wohl knapp für die 1. Möglichkeit entscheiden. Wenn ich kein Perl, C oder ähnliches installieren dürfte, (für den Codegenerator) entfällt sie. Dann bleibt die oben "Test" genannte SCL-Funktion.

Der AWL-Code scheint ein modifiziertes BLKMOV zu sein. Da sollte auch ein einfaches BLKMOV möglich sein, gefolgt von einem Schritt in SCL, in dem die Bits nebeneinander geschrieben werden, in der Art FOR... Liste := Liste[i*2]... - langsamer, aber schöner.

Auf die Zykluszeit, denke ich, haben wenige BLKMOVs oder ähnliches meist keine schwerwiegenden Auswirkungen. Das hängt aber von den Ansprüchen ab. Notfalls hilft dann nur Ausprobieren.

Um Eingänge umzuverlegen, finde ich auch wieder 1. besser. Dazu wird einfach das Symbol von E0.0 auf den neuen Eingang gelegt und der generierte Code neu übersetzt. Die Symbole kann man auch generieren und die Reparatur-Eingang-Symbole manuell dranhängen.
 
Zurück
Oben