Step 7 Bit aus Array of Bool (mit Variable als Index) lesen, setzen, rücksetzen in AWL

WTEFUE

Level-1
Beiträge
4
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen!

Bei folgendem Szenario habe ich Probleme mit der Umsetzung:
Ich habe ein Array of Bool mit 15 Einträgen. Nennen wir dieses Array mal TestArrayBool[0..14]. Ich möchte gerne einzelne Bits lesen, setzen und rücksetzen können. Der Zugriff auf die Einträge soll mittels einer Indexvariable (Index) bestimmt werden können.

Mein erster Ansatz wäre folgender gewesen:

Code:
// Variablen:
// TrayIndex als INT
// TestArrayBool als Array [0..14] of Bool


// Eintrag prüfen
U TestArrayBool[Index]

// Bit setzen
S TestArrayBool[Index]

// Bit rücksetzen
R TestArrayBool[Index]

Der Ansatz hat natürlich nicht funktioniert (Syntaxfehler) und ich habe in diesem Forum einige ähnliche Beispiele dazu gefunden. Wie in den Artikel beschrieben, ist diese Aufgabe in AWL nicht ganz so einfach zu lösen. Um den indirekten Zugriff auf ein Arrayelemt zu ermöglichen, muss mit Pointern gearbeitet werden.


Ich konnte mit Hilfe des Beitrages über Pointer Zeiger die Aufgabenstellung zumindest für ein Array of Int lösen.

Funktion fürs Laden
Code:
// Funktion: Laden eines Arrayeintrages
// Variablen:
// TrayIndex als INT
// TestArrayInt als Array [0..14] of Int

// TrayIndex testweise beschreiben
L         4
T         TrayIndex

L         P##TestArrayInt         //Pointer auf Array laden
LAR1                                    //ins Adressregister AR1 speichern
L         #TrayIndex               // TrayIndex laden
L         2
*I                                       // mit 2 multiplizieren da Typ: int
SLD     3                             // Pointer bauen
+AR1                                 // zum Adressregister hinzuaddieren
L         W [AR1,P#0.0]        // Syntax läd den Wert von #TestArrayInt[4]

Funktion fürs Schreiben:
Code:
// Funktion: Schreiben eines Arrayeintrages
// Variablen:
// TrayIndex als INT
// TestArrayInt als Array [0..14] of Int

// TrayIndex testweise beschreiben
L         4
T         TrayIndex

L         P##TestArrayInt         //Pointer auf Array laden
LAR1                                    //ins Adressregister AR1 speichern
L         #TrayIndex               // TrayIndex laden
L         2
*I                                       // mit 2 multiplizieren da Typ: int
SLD     3                             // Pointer bauen
+AR1                                 // zum Adressregister hinzuaddieren
L         50                          // Intwert von 50 laden
T        W [AR1,P#0.0]        // Syntax schreibt den Wert 50 in den Arrayeintrag #TestArrayInt[4]

Anschließend habe ich versucht die Funktion auf ein Array of Bool anzupassen:

Code:
// Funktion: Lesen eine Arrayeintrages
// Variablen:
// TrayIndex als INT
// TestArrayBool als Array [0..14] of Bool

// TrayIndex testweise beschreiben
L         4
T         TrayIndex

L         P##TestArrayBool         //Pointer auf Array laden
LAR1                                    //ins Adressregister AR1 speichern
L         #TrayIndex               // TrayIndex laden
L         8
/I                                       // durch 8 multiplizieren da Typ: Bool
SLD     3                             // Pointer bauen
+AR1                                 // zum Adressregister hinzuaddieren

// Bit lesen
U  ??????? // Welcher Syntax muss hier stehen, damit ich das Bit TestArrayBool[TrayIndex] abfragen kann bzw. geht das überhaupt so einfach, wie ich mir das vorstelle?

Wie würde der Syntax aussehen, wenn ich beschreiben (S) bzw. rücksetzen (R) will?

Danke im Voraus!
 
Hi,

ich hab mich kürzlich auch erst damit auseinandersetzten müssen, nicht in dem gleichen Zusammenhang wie du, aber
vllt finden wir gemeinsam eine Lösung :)

Ich geh mal davon aus, dass du dein Array in einem DB stehen hast und die Start Adresse DB1.DBX0.0 ist

Ganz wichtig natürlich die Datentypen der verschiedenen Variablen richtig angeben, sonst klappt es nicht.

Ich wäre jetzt so vorgegangen (schreiben):

L #Index //Zahlenwert zwischen 0..14
SLD3
T #Addr

// Sprungbedingung ob L1 oder L0 geladen werden soll

L 1 // Lade 1 oder 0 in Abhängigkeit was du machen möchtest
AUF DB1 //Aufruf DB1
T DBW[#Addr] //Wert 1 auf die Adresse die im Addr steht transferieren


lesen:



L #Index //Zahlenwert zwischen 0..14
SLD3
T #Addr

L DBW[#Addr] //Wert 1 auf die Adresse die im Addr steht transferieren
T HM // HM Hilfsmerker

U HM
=M0.2 //Was du dann auch immer damit machen möchtest
...
..
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Solches Zeug ist in SCL eben schon sehr übersichtlich und einfach zu machen.

kleines Beispiel.

Code:
VAR_IN_OUT
    testarray : ARRAY[0..15] OF BOOL;
    merker : BOOL;
END_VAR


VAR_TEMP
index : INT;
END_VAR


FOR index := 0 TO 15 DO
    IF testarray[index] THEN // alle elemente des array abfragen wenn eines wahr ist dann 
         merker := true; // merker setzen
    END_IF;
        
END_FOR;


END_FUNCTION_BLOCK
 
Hi WTEFUE,

so arg falsch ist dein Weg gar nicht! Ausgehend von deinen Zeilen oben und der Annahme, dass TrayIndex als INT an (Lokal)Adresse 0.0 und TestArrayBool an (Lokal)Adresse 2.0 steht:
Code:
// Variablen:
// TrayIndex als INT
// TestArrayBool als Array [0..14] of Bool

// TrayIndex testweise beschreiben
L         10
T         TrayIndex

L         P##TestArrayBool         //Pointer auf Array laden
// AKKU1 enthält jetzt (hex)8600 0010 und zeigt
// somit auf Byte 2 der Lokaldaten. Entspricht TrayIndex = 0.
// Daran denken, dass ein Pointer in den untersten drei
// Bit die 8 einzelnen Bits eines Bytes addressiert!
// Es würde jetzt also auf TestArrayBool[0] gezeigt.

// Soll bspw. auf TestArrayBool[10] (TrayIndex = 10) gezeigt
// werden, muss einfach TrayIndex (also 10) zum Wert im
// AKKU1 addiert werden. AKKU1 enthält dann
// (hex)8600 001A. Oder anders ausgedrückt: die Adresse
// auf das Bit L3.2.
L        TrayIndex
+D
LAR1

// Zum Vergleich ein Laden der Adresse auf L3.2 direkt.
L P#L 3.2

// Um den Zustand des Bits 10 im Array (auf das zeigt AR1
// zu lesen/zu schreiben, die gewohnte Semantik verwenden.
// Also bspw. zum Lesen:
SET
U        L [AR1, P#0.0]
=        M4711.0

// Oder zum Übernehmen eines anderen Bits
SET
U        M4711.1
=        L [AR1, P#0.0]

// Das Setzen wie üblich mit
SET
S        L [AR1, P#0.0]

// Oder das Rücksetzen mit
SET
R        L [AR1, P#0.0]

Grüße
Max
 
Zuletzt bearbeitet:
Solches Zeug ist in SCL eben schon sehr übersichtlich und einfach zu machen.

Ansich würde ich das sicherlich auch in SCL machen anstatt in AWL. Mein Problem ist nur, dass ich zwar eine Professionallizenz (mit SCL) auf dem Rechner habe, jedoch mein Kunde auf AWL besteht, da er nur eine Basic Lizenz hat.

Trotzdem danke ich dir!

EDIT: Ist es möglich ein bereits übersetztes SPS-Projekt (mit SCL Bausteinen) mit einer Basiclizenz auf die SPS zu übertragen. Wird nur für das Übersetzen eine Professionallizenz benötigt?

Lg
WTEFUE
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Löwensenft!

Ich habe deine Lösung ausprobiert und es hat funktioniert.

Ist es auch möglich das TestArrayBool nicht in dem TEMP-Bereich zu haben. Ich würde mir gerne einen Baustein basteln, den ich mit INPUTS füttere. Dann wird allerdings die Syntax L [Ar1,P#0.0] nicht funktionieren, da diese ja auf den TEMP-Bereich zugreift.

Im Prinzip würde ich gerne den Baustein aufrufen können und die Eingangs und Ausgangsparameter definieren.

Beispiel:
Ich baue mir einen FB, nennen wir ihn TestArray. Dieser Baustein hat als INPUT den #TrayIndex und ein #AktionsInt. Als IN_OUt ist das #TestArrayBool definiert. Den zugehörigen Instanzbaustein nennen wir INST_DB_TestArray. Die gefütterten Daten kommen von Datenbausteinen. Nennen wir diese DB_Parameter1 und DB_Parameter2.


Code:
// Aufruf von FB TestArray gefüttert durch DB_Parameter1
Call "TestArray";"INST_DB_TestArray"
TrayIndex     := "DB_Parameter1".TrayIndex                 //INPUT
AktionsInt     := "DB_Parameter1".AktionsInt                 //INPUT: AktionsInt=0 --> lesen; AktionsInt=1 --> setzen, AktionsInt=2 -->rücksetzen
TestArray     := "DB_Parameter1".TestArray                  //IN_OUT


//später im Programm möchte ich denselben Baustein verwenden, aber zum Beispiel den TrayIndex und den AktionsInt direkt bestimmen und das TestArray aus dem DB_Parameter2 verwenden.
Call "TestArray";"INST_DB_TestArray"
TrayIndex     := 2                                                      //INPUT
AktionsInt     := 0                                                      //INPUT: AktionsInt=0 --> lesen; AktionsInt=1 --> setzen, AktionsInt=2 -->rücksetzen
TestArray     := "DB_Parameter2".TestArray                  //IN_OUT

Geht so viel Flexibilität ohne SCL oder muss ich mich an Junge's Beitrag orientieren und meine Datenbausteine direkt ansprechen.

Liebe Grüße
WTEFUE
 
Hi,

ich vermute es hat einen Grund, dass du einen FB brauchst?

Zurück zum Thema: In den Baustein wird nur ein Pointer (6 Byte) übergeben. Das heißt ein
Code:
L P#TestArray
zeigt dann auf die Stelle an der die eigentliche Adresse der Daten steht.

Das heißt es gilt nun zunächst die eigentliche Adresse für dich "verfügbar" zu machen. Schau nach dem POINTER Typ, wenn du mehr über dessen Aufbau wissen willst.
Code:
L P#TestArray
LAR1

L W[AR1, P#0.0]
T #tmpDataDBNum   // #tmpDataDBNum muss vom Typ WORD (oder INT) sein
L D[AR1, P#2.0]
T #tmpDataAddress // #tmpDataAdress muss vom Typ DWORD (oder DINT) sein

Nun kann wie bisher auf die Daten zugegriffen werden:
Code:
// Datenbaustein öffnen, falls Daten in einem DB stehen
AUF DB[#tmpDataDBNum]
// "Start-"Adresse der Daten laden, Index aufrechnen und in Adressregister laden
L #tmpDataAddress
L #TrayIndex
+D
LAR1

// Als Beispiel: Bit prüfen. Der Zugriff mittels des "allgemeinen" "L[AR1...]" sollte hier funktionieren.
SET
U L[AR1, P#0.0]

Sollte so funktionieren. Habs jetzt aber nicht nochmal getestet.

Gruß
Max
 
Ich hab gerade nochmal mein Quellcode ins PG eingegeben und gesehen das da noch ein Fehler ist :) ..deswegen hier noch eine kleine Korrektur:

Array[0..14] of Bool


Schreiben:
Code:
      L     #Index                      //Datentyp Byte
      SLD   3
      LAR1  
      T     #Addr                      //Datentyp Dword
                                            // Sprungbedingung ob L1 oder L0 geladen werden soll
      L     1                             // Lade 1 oder 0 in Abhängigkeit was du machen möchtest
      AUF   DB     2                   //Aufruf DB2
      T     [COLOR=#FF0000]DBW[/COLOR] [#Addr]             //Datentyp Byte DBB,Word  DBW, DWord DBD?!

Mit dem Datentyp bei dem Transfer bin ich auch überfragt, wäre schön wenn mir das auch noch einer beantworten könnte :confused:
Da nur die Werte 1 oder 0 übertragen werden, müsste es doch egal sein welchen Datentyp wir angeben, der Rest würde einfach wegfallen?!
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo nochmal Löwensenft!


Ich habe folgenden Code nachgebaut und ausgetestet:

Code:
L P#TestArray                      // Da #TestArray als IN_OUT definiert ist und im Bausteinaufruf TestArray:= "DB_Parameter1".TestArray definiert worden ist, sollte hier auch das TestArray aus dem DB_Parameter1 verwendet werden?!


LAR1                               // und ins Adressregister geschrieben werden


L W[AR1, P#0.0]                
T #tmpDataDBNum             // #tmpDataDBNum muss vom Typ WORD (oder INT) sein
L D[AR1, P#2.0]              
T #tmpDataAddress         // #tmpDataAdress muss vom Typ DWORD (oder DINT) sein

// Datenbaustein öffnen, falls Daten in einem DB stehen
AUF DB[#tmpDataDBNum]           // habe mit und ohne dieser Zeile getestet. In meinem Test ist nämlich TrayIndex direkt als Zahl angegeben. Lediglich das Array soll aus dem DB entnommen werden und nachher falls geändert wieder in                                                    //den DB geschoben werden. Deshalb sollte ich diese Zeile nicht benötigen.


// "Start-"Adresse der Daten laden, Index aufrechnen und in Adressregister laden
L #tmpDataAddress               // in tmpDataAddress sollte jetzt die Start-Addesse vom DB_Parameter1.TestArray stehen oder?
L #TrayIndex                       // den TrayIndex laden (4)
+D                                     // zum Adressregister wird der TrayIndex hinzuaddiert, damit auf TestArray[4] zugegriffen werden kann.
LAR1


// Als Beispiel: Bit prüfen. Der Zugriff mittels des "allgemeinen" "L[AR1...]" sollte hier funktionieren.
SET
U L[AR1, P#0.0]             // für Testzwecke setze ich M10.0 auf 1.
= M10.0


#TestArray ist dabei als IN_OUT definiert (Array[0..14] of Bool)
#TrayIndex ist als IN definiert (INT)
#tmpDataDBNum ist als TEMP definiert (WORD)
#tmpDataAddress ist als TEMP definiert (DWORD)

Ich rufe den Baustein so auf:
CALL "TestArray","INST_DB_TestArray"
TrayIndex := 4 // auf 4 gesetzt zum Testen
TestArray := "DB_Parameter1".Testarray // Array[0..14] of Bool --> zum Testen habe ich alle Einträge auf True gesetzt


Die Abfrage U L[AR1,P#0.0] hat als VKE 0, obwohl ich im DB_Parameter1 alle Einträge im TestArray auf True gesetzt habe.
M10.0 sollte 1 werden, wird es aber leider nicht...



Ich werde mir, wie von dir vorgeschlagen, den Pointer Typ genauer anschauen.

Aus dem Kontext heraus kann ich zwar annehmen, dass der Syntax L W[AR1, P#0.0] die DBNummer extrahiert und der Syntax L D[AR1, P#2.0] die Adresse extrahiert. Wieso und warum zum Beispiel der Versatz P#2.0 benötigt wird verstehe ich leider noch nicht.


Vielen Dank für die Bemühungen.
Ich freue mich über jeglichen weiteren Tipps und melde mich hier mit meiner Lösung, falls ich sie gefunden habe.

Lg
WTEFUE
 
Hi,

sorry, mein Fehler. Ich dachte dass der Zugriff (ob DB oder Lokal) automatisch korrekt gemacht wird. *grübel*

Wenn du deine Bits tatsächlich in einem DB abspeichert, musst du mit "U DBX[AR1, P#0.0]" zugreifen... Allerdings funktioniert DBX nicht, wenn dein AR1 auf Lokaldaten zeigt...

Da muss ich nochmal in mich gehen.

Gruß
Max
 
Hi nochmal,

hat sich schon erledigt. Einen kurzen Blick in die Hilfe unter "Bereichsübergreifende, registerindirekte Adressierung" brachte die Lösung: Einfach jegliche Bereichskennung vor der eckigen Klammer weglassen. Sieht dann so aus:
Code:
U [AR1, P#0.0]

Sieht komisch aus, geht aber. ;)

Gruß
Max
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wenn du deine Bits tatsächlich in einem DB abspeichert, musst du mit "U DBX[AR1, P#0.0]" zugreifen... Allerdings funktioniert DBX nicht, wenn dein AR1 auf Lokaldaten zeigt...
Das funktioniert doch. Wenn in der Operation eine Bereichsangabe enthalten ist (z.B. das DBX) , dann handelt es sich um bereichsinterne registerindirekte Adressierung. Eine eventuell in AR1 enthaltene Bereichskennung wird dann ignoriert.

@WTEFUE
Wichtig ist noch, vor dem Zugriff den entsprechenden DB zu öffnen.
Und auf Bits wird nicht mit L/T zugegriffen, sondern mit U/O/X/S/R/=

Harald
 
Hi,

ja, das ist schon klar @ Ignorierung von AR1. Allerdings bringt das nichts, wenn tatsächlich bspw. auf die vorherigen Lokaldaten (wegen Parameterübergabe) zugegriffen werden muss. :)

Grüße
Max
 
Zurück
Oben