Indirekte Adressierung bei Codesys/TwinCat

Majestic_1987

Level-1
Beiträge
270
Reaktionspunkte
22
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Leute. Im Zuge meiner Facharbeiterprüfung hab ich in der S7 einen Baustein mit indirekter Adressierung geschrieben. Habe dadurch natürlich den Sinn und Zweck dieser Technik entdeckt. Jetzt habe ich eine Frage zu der Thematik: Ich möchte Messwerte in ein Array schreiben. Bei der S7 geht das so: Über Multipanel kann man bis zu 4 Quell-DB's auswählen, in denen der Messwert steckt (z.b. der Hauptistwert eines FU) Dazu dann noch das Wort, in welchem der Messwert innerhalb des DB steckt. In einer frei wählbaren Abtastzeit wird dann der erste Wert genommen und vom Quell-DB in den Ziel-DB geschrieben. Dieser beinhaltet ein Array aus 4000 Integer-Werten. Die ersten 1000 sind für Messwert 1, die zweiten 1000 für Messwert 2 usw. Das Programm adressiert über das Adressregister den ersten Integer-Wert des Array, schreibt den zur Messzeit aktuellen Messwert dort hinein und die Ziel-Adresse wird um 1 Wort erhöht. Zum nächsten Messzeitpunkt wird der zu diesem Zeitpunkt aktive Messwert in das 2. Wort des Array geschrieben. Dies wiederholt sich, bis der Bereich für diesen Messwert (sprich die ersten 1000 Worte des Array) voll sind. Wie kann ich soetwas mit Codesys umsetzen frage ich mich nun, denn ich arbeite mich privat gerade in Codesys ein. Hoffe mir kann jemand helfen. Grüße Sven
 
Es gibt zwar auch Pointer etc. in Codesys, aber es geht viel einfacher. Du kannst Arrays erstellen (Wie auch in einem Step7-DB). Aber Codesys kann direkt über die Arrayindizes Daten in die Arrays schreiben. Der Indize darf also eine Variable sein. (Geht bei Step 7 nur mit SCL und wird dann intern von einem Compiler in Befehle mit ind. Adressierung umgesetzt). Das geht auch mit mehrdimensionalen Arrays, so daß du deine 3*1000 Meßwerte bei Bedarf in einem zweidimensionalen Array unterbringen kannst.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Gut, anderer Fall, der Fall, durch den ich auf ind. adressierung kam:

Kunde will einen Betriebsstundenzähler für erstmal 10 Betriebsmittel, der die Betriebsstunden ausfallsicher speichert.

Lösung S7:
1 FC
Lokale Parameter:
IN:
DB Nummer: INT
DB Adresse: WORD

plus 1 Datenbaustein mit folgender Struktur
Seconds - INT
Minutes - INT
Hours - DINT
--------------
Das Muster wiederholt sich 100 mal.

Der FC bekommt nen 1Hz-Taktmerker.
Mit jeder steigenden Flanke wird der Sekunden-Wert inkrementiert:

AUF DB[#DB_Nummer]

L P##DB_Adresse
LAR1

U Taktmerker
FP Temp_Taktmerker
L DBW[AR1, P#0.0]
+ 1
T DBW[AR1, P#0.0]

Dann folgt der Vergleich des dort liegenden Wertes auf 60.
Ist der Wert gleich 60 wird das Adressregister um P#2.0 erhöht.

Dann dieser Wert (Minuten) um 1 inkrmentiert, Sekundenwert auf 0 gesetzt und Minutenwert auf 60 verglichen.

Adressregister wieder um 1 Wort erhöht und dann die Stunden inkrementiert, Minuten auf 0 gesetzt....usw.

Beim Aufruf des FC übergibt man die DB-Nummer sowie die Adresse, an welcher der Datenbereich des zu überwachenden Betriebsmittels beginnt, z.B. DBW20

Ist auch das in Codesys (einfacher) lösbar?

Zur Array-Struktur in Codesys (habe dort bisher nicht mit Arrays gearbeitet):

Gelten für das Array hier auch die selben Grenzen wie bei Step7? Also max 65535 Werte und maximal 6 Dimensionen?
Und wie lautet der Befehl (AWL, ST) mit dem ich auf ein Datenelement im Array zugreife? Speziell wenn dieses eine Variable ist?
 
Zuletzt bearbeitet:
Aus der Codesys Hilfe:

Code:
Pointer 
 pt:POINTER TO INT;
 var_int1:INT := 5;
 var_int2:INT;
 pt := ADR(var_int1);
 var_int2:= pt^; (* var_int2 ist nun 5 *)
Trotzdem würde ich das mit Arrays machen.
 
Wir lernen also, dass es in Codesys nur speicherindirekte Adressierung gibt.

Mensch, das is das blöde daran, bei Siemens zu lernen: Nur da gibts diese DB-Geschichte....und wenn man daran gewöhnt is fällts schwer, davon weg zu kommen xD
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich kenne mich mit Step7 leider nicht so gut aus und mit AWL komm ich auch nicht klar, daher werde ich dir mal kurz erläutern, wie man bei CoDeSys /TwinCAT mit Strukturiertem Text solche Dinge angeht:

DBs sind bei CoDeSys Strukturen.
Deklaration:
Code:
TYPE myStructType:
    STRUCT 
       bBool1 : BOOL;
       iInteger1: INT;
       sString1: STRING(20);
       arrInteger1: ARRAY [0..10] OF INT;
    END_STRUCT
END_TYPE
In deinem Programmteil, sei's ein Programm oder ein Funktionsbaustein, kannst du nun einzelne dieser Strukturen deklarieren oder auch ganze Arrays davon:
Code:
VAR
     stMyStruct: myStructType;
     arrstMyStruct: ARRAY [0..5] OF myStructType;
     iarrMesswerte: ARRAY [0..10] OF INT;
END_VAR
Im Array kann man dann je nach bedarf einzelne Elemente der Struktur behandeln oder z.B. mittels Schleifen ganze Abläufe automatisieren.
Code:
stMyStruct.bBool1  := TRUE;
stMyStruct.arrInteger1[2] := 32;                  (* Array in der Struktur beschrieben *)
arrstMyStruct[1].stMyStruct.arrInteger1[3] := 64;  (* Array-Element des Arrays beschrieben *)

....

(*Bearbeitung mittels Schleife*)
FOR i:=0 TO 10 DO
    stMyStruct.arrInteger1[i] := iarrMesswerte[i];
END_FOR

...

CASE y OF
1:  stMyStruct.arrInteger1[y] := iMesswert;
     stMyStruct.bBool1  := TRUE;
     stMyStruct.sString1 := 'Messwert im Index 1';
2:  stMyStruct.arrInteger1[y] := iMesswert;
     stMyStruct.bBool1  := FALSE;
     stMyStruct.sString1 := 'Messwert im Index 2';
END_CASE
(Du kannst die maximale Array-Größe auch mit einer VAR CONSTANT festlegen)
Code:
VAR
     arrstMyStruct: ARRAY [0..iMax ] OF myStructType;
END_VAR
VAR CONSTANT
     iMax : INT := 5;
END_VAR
So kannst du bei CoDeSys und TwinCAT alles komplett ohne direkte Adressierungen und ohne Pointer erledigen :cool:
... noch Fragen?
 
Zuletzt bearbeitet:
Mensch, das is das blöde daran, bei Siemens zu lernen: Nur da gibts diese DB-Geschichte....und wenn man daran gewöhnt is fällts schwer, davon weg zu kommen xD
Wenn du bei S symbolisch programmierst interessieren dich (außer evtl. in FUP/AWL) die DBs auch nicht mehr.

Dein Anwendungsfall würde ich in ST lösen.

Code:
TYPE runtime_t :
STRUCT
    seconds : INT;
    minutes : INT;
    hours    : DINT;
END_STRUCT
END_TYPE
Zählfunktion mit so einer Schnittstelle:

Code:
FUNCTION runtime_counter : BOOL
VAR_INPUT
    running : BOOL; (* Antrieb in Betrieb *)
END_VAR
VAR_IN_OUT
    runtime : runtime_t;
END_VAR
Dann kannst du dir ein globales Array in beliebiger Dimension vom Typ runtime_t anlegen, und damit jeden Funktionsaufruf mit z.B. antriebe[1, 9] beschalten.

Die Zähleraufrufe dann im 1 Sekunden Task aufrufen.
 
Erstmal dankeschön für die fixen Antworten.

Ist halt alles ne Sache der Gewöhnung :) Sieht so zwar auf den ersten Blick kompliziert aus (weil ich auch bisher wenig in ST gemacht hab) aber scheint doch recht easy und vor allem durchschaubar zu sein.

Hab in nem anderen Beitrag (wo jemand was ähnliches machen will) den Befehl

Code:
TVar[i] := 0
entdeckt. Was hats damit auf sich? Versuche mich grad nämlich an soner Schleife in ST.

Ist ST eig. für sämtliche Aufgaben der Steuerungstechnik geeignet oder ist in bestimmten Bereichen FUP oder CFC sinnvoller?

Und an Thomas zum Thema DB's interessieren nicht:

Projekte bei uns (ich denke da z.b. an Hochregallager oder auch an Stahlwerke) bestehen aus mindestens so vielen DB's wie FC's ;-) Also das ist in der Step7-Programmierung, auch wenn sie voll symbolisch erfolgt, standard.
Der unterschied zur IEC ist ja vor allem, dass man in Step7 komplett selber adressiert während man in der IEC komplett mit Variablen arbeitet und sich nicht um adressierung kümmern muss..oder verwendet hier jemand Merker in Codesys?? ;-)
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Hier mal eine Quick and Dirty Lösung für dein Betriebsstundenprob,
jetzt bist du mit Siemens wieder dran ...

Einfach das Txt von der Datei hinten löschen, und in Codesys öffnen.

Mfg
Manuel
 

Anhänge

  • Codesys_Betriebsstunden.pro.txt
    19,8 KB · Aufrufe: 227
Danke schonmal, aber ich versuchs erstmal selber

In Step7 hab ichs übrigens schon lauffähig, wie gesagt möchte ich aber (stichwort Rundumblick) auch Codesys lernen, und da is dann ohne DB's der Anfang schwer xD
 
Zu deinem Siemens-Code Schnipsel,
eine Flankenauswertung in einem Mehrfach aurfrufbaren FC kannst du eh vergessen,
das aber nur ganz am Rande ...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hab in nem anderen Beitrag (wo jemand was ähnliches machen will) den Befehl

Code:
TVar[i] := 0
entdeckt. Was hats damit auf sich? Versuche mich grad nämlich an soner Schleife in ST.
"i" ist eine gewöhnliche Variable. Kannst auch nen anderen Namen nehmen. Hier steht i für den Index eines Array. So kannst du in Schleifen durch manipulation des Index sehr unkompliziert zB ein ganzes Array in einem Rutsch beschreiben.
Code:
VAR
  iArray: ARRAY [0..iMax] OF INT;
  i : INT;
END_VAR
VAR CONSTANT
  iMax: INT := 999;
END_VAR

----------------------
FOR i:= 0 TO iMax DO
  iArray[i] := (* irgendwas *);
END_FOR
Ist ST eig. für sämtliche Aufgaben der Steuerungstechnik geeignet oder ist in bestimmten Bereichen FUP oder CFC sinnvoller?
Wir nutzen bei uns nur ST. Haben bisher auch noch nie irgend eine "Grenze" erkannt.
Vorteil, sofern man sich an das Hochsprachen-ähnliche Programmieren gewöhnt hat, ist für mich, dass du insbesondere solche Schleifen oder Verriegelungen sehr einfach realisieren kannst. Und dann ist der Schritt zur kompletten Datenverwaltung innerhalb der SPS nicht mehr schwer. Insbeondere TwinCAT bietet dir ja bei PC-Systemen zig Möglichkeiten um mit PC-Daten oder Datenbanken zu arbeiten.
Im Gegensatz zu "grafischen" Sprachen ist der Bildschirm nicht so voll. Probier mal größere mathematische Rechungen mit FUP :ROFLMAO:,
und im Vergleich zu AWL finde ich ist ST-Code leichter lesbar, wenn's ums Debuggen geht.
 
Naja das ST für Mathe top ist war mir von vorn herein klar, hatte nur immer meine Zweifel in sachen Bit-Logic, wofür ja AWL und FUP prädestiniert sind.

Andere Sache:

Angenommen ich hab 4 Arrays.
Array1
Array2
Array3
Array4

Ich möchte gerne das Array, auf das mein Baustein zugreifen soll, als Lokalparameter diesem Baustein übergeben...


Wie mach ich das? Welchen Typ muss die IN-Variable haben?

Ziel wäre z.b. dass ich in der Visualisierung für ne Kurve oder sowas mittels Drop-Down das Array auswählen kann, in das mein Baustein Daten schreibt.

@Trinitaucher:

Das i war mir klar, das TVar nicht. Mir is dann grad auch mal aufgefallen, dass es sich bei TVar um ein Array handelt, auf dessen i-tes Element zugegriffen wird.
 
Wir lernen also, dass es in Codesys nur speicherindirekte Adressierung gibt.

Mensch, das is das blöde daran, bei Siemens zu lernen: Nur da gibts diese DB-Geschichte....und wenn man daran gewöhnt is fällts schwer, davon weg zu kommen xD

DBs sind IMHO Rotz. Die Pointerei auf der S7 ist 1. auch Rotz und 2. auch "speicherindirekt" (wie Du es getauft hast). Man bildet einen Pointer von einer Variable und manipuliert diesen dann mit Byteoffsets. Das kann man in CoDeSys zwar auch tun aber oft ist dies nicht notwendig da man auch von einem Pointer aus Symbolisch weiteradressieren kann.
Dies bringt mich zu Deiner Frage mit den Arrays:
...
Angenommen ich hab 4 Arrays.
Array1
Array2
Array3
Array4

Ich möchte gerne das Array, auf das mein Baustein zugreifen soll, als Lokalparameter diesem Baustein übergeben...


Wie mach ich das? Welchen Typ muss die IN-Variable haben?

Ziel wäre z.b. dass ich in der Visualisierung für ne Kurve oder sowas mittels Drop-Down das Array auswählen kann, in das mein Baustein Daten schreibt.
...

Obligatorische Gegenfrage wie würdest Du das bei der S7 machen?

Ich gehe davon aus das Du nun keine Kopie des Arrays anlegen willst da kann man dann zu einem Pointer greifen.

Ich geh mal davon auf das Du die Arrays irgend wo global definiert hast:
Code:
VAR_GLOBAL
  Array1 : ARRAY[0..MAXCHART] OF INT;
  Array2 : ARRAY[0..MAXCHART] OF INT;
  Array3 : ARRAY[0..MAXCHART] OF INT;
  Array4 : ARRAY[0..MAXCHART] OF INT;
  (* MAXCHART ist eine globale Konstante damit man das nicht immer von Hand anpassen muss *)
END_VAR
Nun zum Eingang der Funktion:
Code:
VAR_INPUT
     Chart : POINTER TO ARRAY[0..MAXCHART] OF INT;
END_VAR
In der Funktion kann man dann auf das Array so Zugreifen:
Code:
Chart^[2] := 99; (* fester INDEX*)

FOR i := 0 TO MAXCHART DO
  Chart^[i] := 0; (* variabler INDEX*)
END_FOR
Nun ist bei dem Aufruf der Funktion noch zu beachten das man einen Pointer versorgen muss:
Code:
RetVal := MyFunction(Chart:=ADR(Chart1));
---

Diese Anwendung eigent sich auch für Strukturen (Benutzerdefinierte Datentypen). Da man halt im Baustein dann weiterhin Symbolisch zugreifen kann.

PS: Man könnte es auch ganz schnöde mit einem FB und einem IN_OUT lösen den gibt es aber im FC nun mal nicht.
PPS: Es gibt immer für und wider
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
[/code]Nun ist bei dem Aufruf der Funktion noch zu beachten das man einen Pointer versorgen muss:
Code:
RetVal := MyFunction(Chart:=ADR(Chart1));
---

Diese Anwendung eigent sich auch für Strukturen (Benutzerdefinierte Datentypen). Da man halt im Baustein dann weiterhin Symbolisch zugreifen kann.

PS: Man könnte es auch ganz schnöde mit einem FB und einem IN_OUT lösen den gibt es aber im FC nun mal nicht.
PPS: Es gibt immer für und wider

1. Frage:

Wo trägst du denn jetzt den Namen des Array (Array1) ein?
RevVal ist doch der Rückgabewert der Funktion welche du weiter oben geschrieben hast. Aber irgendwo muss ich dem Pointer ja sagen "Adressiere Array1" So dass dessen interne Adresse dann im FC zur Verfügung steht.

2. Frage:

Wie hätte man das per IN_OUT gelöst?
IN_OUT vom Typ Array of Int oder wie?
 
1. Frage:

Wo trägst du denn jetzt den Namen des Array (Array1) ein?
Ähm ja Schreibfehler ich wollte die Variablen Array1 bis 4 eigentlich Chart1 bis 4 nennen und habe das in der globalen Deklaration dann doch verschnarcht.

RevVal ist doch der Rückgabewert der Funktion...

Der RetVal hat nichts mit dem Thema zu tun das habe ich einfach reflexartig beim Funktionsaufruf verwendet.

2. Frage:

Wie hätte man das per IN_OUT gelöst?
IN_OUT vom Typ Array of Int oder wie?

IN_OUT nur bei PRGs und FBs erlaubt und dann einfach:

Code:
VAR_IN_OUT
    Chart : ARRAY[0..MAXCHART] OF INT;
END_VAR
 
Hallo Leute....

wo ist hier der Fehler?? Das schreiben der Werte in das Array soll stoppen, sobald Start = False ist...aber egal welchen Zustand Start hat, das Array wird weiter beschrieben

Code:
VAR_INPUT
    Start: BOOL;
    Array_Nummer: INT :=0; (*Werte schreiben ab diesem Element*)
    Array_Elements: INT; (*Gesamtheit der Elemente im Array*)
END_VAR
VAR_OUTPUT
    Error: BOOL;
END_VAR
VAR
    timer1:TP;
    timer2:TP;
    ft1: R_TRIG;
    Zaehl: INT;

----------------------------------------------------------------------

Timer1 (IN:=Start AND NOT Timer2.Q,PT:=T#100ms);
Timer2 (IN:=Start AND NOT Timer1.Q,PT:=T#100ms);
ft1 (CLK:=Timer1.Q);

IF Array_Elements > 1000 THEN
    Error := TRUE;
         RETURN;
END_IF

IF Start AND ft1.Q THEN
        Messwerte[Zaehl] := Analogwert;
        Zaehl := Zaehl + 1;

            IF Zaehl > Array_Elements THEN
            Zaehl := 0;
            END_IF

END_IF
 
Defensiv

Hallo Leute....

wo ist hier der Fehler?? Das schreiben der Werte in das Array soll stoppen, sobald Start = False ist...aber egal welchen Zustand Start hat, das Array wird weiter beschrieben

Code:
VAR_INPUT
    Start: BOOL;
    Array_Nummer: INT :=0; (*Werte schreiben ab diesem Element*)
    Array_Elements: INT; (*Gesamtheit der Elemente im Array*)
END_VAR
...

Ich sehe den Fehler nicht direkt und es ist auch unvollständig.

Ich würde es etwas defensiver angehen und die Eingangsparameter alle oder gar keinen vorbelegen.

Code:
IF Start AND ft1.Q THEN
        Messwerte[Zaehl] := Analogwert;

Dann würde ich hier so ansetzen:

IF ((Start <> FALSE) AND (ft1.Q <> FALSE)) THEN

So bin ich zumindest sicher, dass keine Auswerte Prioritäten durcheiander geraten.

Wenn es immer noch nicht hilft:

BOOL bTest := (Start <> FALSE) AND (ft1.Q <> FALSE);

und im Debugger anschauen, was daraus wird. Ansonsten erst mal Wochenende ausschlafen.
 
Oha, ich bin überzeugt.
Habe jetzt ne richtig schicke Messwertspeicher-Funktion. In Kombination mit dem "CASE" Befehl wirklich praktisch :) Also ich mag Codesys :ROFLMAO:

Hier mal mein geistiger Erguss, ich bitte um Kommentierung:

Variablen:
Code:
FUNCTION_BLOCK Test_Adress
VAR_INPUT
    Start: BOOL; (*Start-Bit für Messwertspeicherung*)         
    Array_Nummer: INT :=0; (Array-Element ab welchem Speicherung erfolgen soll*)
    Array_Elements: INT; (*Gesamtmenge der Elemente im Array, die beschrieben werden sollen*)
    Delete_Array: BOOL; (*Aktives Array löschen*)
    Array_Name: POINTER TO ARRAY [0..MaxChart] OF INT; (*Parameter Pointer auf nächstes Array*)
END_VAR

VAR_IN_OUT
END_VAR

VAR_OUTPUT
    Error: BOOL;
    Delete_Run: BOOL;
    Array_Full: BOOL;
END_VAR
VAR
    Active_Array: POINTER TO ARRAY [0..MaxChart] OF INT; (*aktives Array*)
    timer1:TP;
    timer2:TP;
    Zaehl: INT;
    ft1: R_TRIG;
    ft2: R_TRIG;
END_VAR
Programmcode:
Code:
Timer1 (IN:=Start AND NOT Timer2.Q,PT:=T#125ms);
Timer2 (IN:=NOT Timer1.Q,PT:=T#125ms);
ft1 (CLK:=Timer1.Q);

(* Neues Array nur akzeptieren, wenn altes voll oder leer*)
IF Array_Full OR Zaehl = 0 THEN
    Active_Array := Array_Name;
END_IF

(*Wenn Array-Elementzahl größer als Maximalwert -> Fehler*)
IF Array_Elements > 1000 THEN
    Error := TRUE;
    RETURN;
ELSE
    Error := FALSE;
END_IF

(*Wenn Start und Abtastzeit starte Werttransfer*)
IF Start AND ft1.Q THEN
        Active_Array^[Zaehl] := Analogwert;
        Zaehl := Zaehl + 1;
        Array_Full := FALSE;

            IF Zaehl > Array_Elements THEN
                Array_Full := TRUE;
                Zaehl := 0;
            END_IF

END_IF

(*Bei Flanke am Lösch-Bit aktives Array mit 0 beschreiben*)

ft2 (CLK:=Delete_Array);

IF ft2.Q THEN
    FOR Zaehl := 0 TO Array_Elements DO
    Active_Array^[Zaehl] := 0;
    Delete_Run := TRUE;
    END_FOR
    Zaehl := 0;
ELSE
    Delete_Run := FALSE;
END_IF
 
Zuletzt bearbeitet:
Zurück
Oben