Mein erster FB

WL7001

Level-2
Beiträge
182
Reaktionspunkte
17
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen,

bis heute habe ich mich immer schön vor den FBs gedrückt, aber jetzt will es auch wissen.

Also habe ich mir einen FB geschrieben, der mich von den Timern erlösen soll, bei denen es eine ON und eine OFF zeit geben soll, einstellbar über ein TP. Da werden dann die ON und OFF zeiten als INT Werte eingegeben und im FB schön mit dem CPU-Takt runtergezählt.

Im FB habe ich folgendes hinterlegt :

IN var
1Hz Takt der CPU
Sollwert ON zeit (INT ) aus dem TP
Sollwert OFF zeit (INT ) aus dem TP
Freigabesignal

OUT var
Der Ausgang, der getaktet werden soll

INOUT var
Aktualwert der ON zeit (INT)
Aktualwert der OFF zeit (INT)

STATvar
BOOL ob die ON zeit abgelaufen ist
BOOL ob die OFF zeit abgelaufen ist

TEMPvar
BOOL Hilfsmerker für Flanke Takt 1s
BOOL Flankenmerker für Takt 1s
BOOL Hilfsmerker für Flanke Freigabe
BOOL Flankenmerker Freigabe


Programm läuft prima. Die Sollwerte sowie die Aktualwerte hole / schreibe ich in einen DB (hier199)

Aufgerufen wird der FB wie folgt :

CALL FB 200 , DB200
TAKT_5_CPU :=M100.5
SOLLWERT_T_ON :=DB199.DBW2
SOLLWERT_T_OFF :=DB199.DBW4
FREIGABE_TIMER :=DB199.DBX0.0
TAKTAUSGANG :=DB199.DBX0.1
AKTUALWERT_T_ON :=DB199.DBW6
AKTUALWERT_T_OFF:=DB199.DBW8

Soweit, so gut ( oder schlecht ? )

Jetzt muss ich den FB ja mit einem Instanzdatenbaustein (hier 200) aufrufen. Wenn ich den FB jetzt 10* aufrufen will, muss ich ja 10 verschieden DBs angeben. Das finde ich blöd und habe hier auch gelesen, dass es anders geht, Stichwort Multiinstanz ? Habe jetzt aber auch gar keine Idee, wie das gehen soll. Ich weiss nicht wo und an welcher Stelle ich diesen ominösen Pointer einsetzen soll, um dem FB zu sagen an welcher Stelle des Db er die Daten ablegen / aktualisieren soll.

Könnte mir mal bitte jemand auf die Sprünge helfen.

Habe den DB extra schon so strukturiert, dass man da einigermassen übersichtlich hinspringen könnte, wenn man weiß wie es geht.

Adr. 0.0: Freigabe_T01 BOOL
Adr. 0.1: Ausgang_T01 BOOL
Adr. 2: Sollwert_TON_T01 INT
Adr. 4: Sollwert_TOFF_T01 INT
Adr. 6: Aktualwert_TON_T01 INT
Adr. 8: Aktualwert_TOFF_T01 INT

Adr. 10.0: Freigabe_T02 BOOL
etc. für beliebig viele Timer


Danke für nen Tipp.

Gruß Wilhelm
 
Zuletzt bearbeitet:
Wieso? Da gibt's doch schon die IEC-Timer SFB3 - 5, die sind selber FBs (also SFBs und damit fest in der CPU verfügbar).
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Na zum Üben!

Pack deine FBs in einen übergeordneten FB und erstell dann in der Variablendeklaration einzelne Instanzen deines UnterFB's mit verschiedenen Namen, z.b. Takt_1, Takt_2 etc.. Als Typ gibst du den Namen deines UnterFBs an. Dann kümmert sich das Programm /der Compiler um die Adressen und nicht du. So wie man es von einer modernen Umgebung erwartet.
 
Da gibt es allerdings ein CPU-abhängiges Speicherproblem, falls deine Unter-FBs zu groß sind. Wenn das Probleme macht, dann na ja, es muss ja Siemens sein!?
 
Na zum Üben!

Pack deine FBs in einen übergeordneten FB und erstell dann in der Variablendeklaration einzelne Instanzen deines UnterFB's mit verschiedenen Namen, z.b. Takt_1, Takt_2 etc.. Als Typ gibst du den Namen deines UnterFBs an. Dann kümmert sich das Programm /der Compiler um die Adressen und nicht du. So wie man es von einer modernen Umgebung erwartet.

Danke erstmal, dass du dir um die Zeit für mein ÜBUNGSproblem Zeit nimmst.
Deine Antwort versteh ich jedoch nicht. Wieso FBs ? Ich habe doch nur einen. Und wenn in einem übergeordneten FB viele Unter-FBs ( also Kopien meines einen FBs ) aufgerufen werden sollen, dann müsste ich ja schon vorher wissen, wie oft ich den FB brauche bzw in der Variablendeklaration bis Takt xy schon vordefinieren.

Ich wollte ja auch eigentlich darauf hinaus, dass ich beim Aufruf des FBs im FC dem Callbefehl irgendwie mit auf den Weg gebe, welchen Bereich des InstanzDBs und/oder des DB199 er verarzten soll.

Ist das überhaupt in der Form möglich.

Gruß Wilhelm
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich wollte ja auch eigentlich darauf hinaus, dass ich beim Aufruf des FBs im FC dem Callbefehl irgendwie mit auf den Weg gebe, welchen Bereich des InstanzDBs und/oder des DB199 er verarzten soll.

Ist das überhaupt in der Form möglich.

Gruß Wilhelm

Dann kannst du auch gleich einen FC nehmen und diesem z.Bsp. eine udt oder struct als INOUT mitgeben, die alle im FB statischen Variablen enthält. Mit denen kannst du dann im FC normal symbolisch arbeiten und hast gar keinen Instanz-DB.

PS: Die udt bzw. struct definierst du in einem DB (so viele, wie reinpassen) und gibtst jedem Timer einen anderen Bereich mit.
 
Hallo!

Leider hast du nicht deinen ganzen Code hereingestellt aber was ich so bei der Variablendekleration gesehen habe wird dein Programm nicht funktionieren wenn du es Öfters aufrufst bzw in einem anderen Baustein auch Lokaldaten verwendest.
Bei Flanken muss der Hilfsmerker eine Statische Variable sein sonst wird der Lokaldatenbereich in einem anderen Baustein überschrieben und deine Flanke funktioniert nicht mehr.

Zu Multiinstanzen:
In der Suche findet man da schon sehr viel dazu aber kurz:
Du legst dir einen Übergeordneten FB an. in diesem kannst du dann in den Statischen Variablen eine Variable mit zb myTime anlegen und bei datentype wählst du FB aus und schreibst deine FB nummer von dem timer dazu.
Dann brauchst du nur mehr im programm call #myTimer schreiben und du hast eine Multiinstanz kreiert.
Die Daten von deinem Zeit FB werden danach in deinem Instanzdatenbaustein vom Übergeordneten FB abgelegt. Also du brauchst da nicht selber einen DB basteln und mit Pointern darauf zugreifen.

godi
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Danke erst mal an alle für die vielen Hinweise. Werde das alles morgen mal versuchen und dem Link folgen und lesen. Muss morgen früh raus. Wenn ich mehr Hilfe brauche, frage ich noch mal nach.

Gruß Wilhelm
 
Hi,

das ist gar nicht so schwer, auch wenns für den Timer natürlich keinen Sinn macht, da es ihn ja schon gibt.

Wenn du z.B. in deinem FB nur mit Symbolik arbeitest, auf die du direkt zugreifst, so wie du sie auch deklariert hast, dann brauchst du dich um die Adressen nicht kümmern. Beispiel:

In deinem Kopf stehen zwei Stat-Variablen, Typ word.

L #word_1
T #word_2


-> kein Problem.

BTW: Es verbietet sich schon von selbst von aussen irgendwas in die Instanzdaten deines FB zu schreiben, weil du die Adresse ja gar nicht kennst, und sie sich auch ändert, sobald du in deinem übergeordnetem FB noch was neues VOR der Deklaration deines (Multiinstanz-DBs) änderst. Alle Adressen verschieben sich, und der Editor passt es auch nicht an!
Möchtest du also z.B. den Zeitwert für deinen Timer übergeben, dann machst du das über eine als IN deklarierte Variable in deinem FB.

Manchmal ist es erforderlich oder wünschenswert auf einzelne Bits eines als IN-Parameter übergebenen Wortes zuzugreifen.

Um das ganze dann aufzuschlüsseln kannst du mit dem AR2 arbeiten. Im AR2 steht nach dem Aufruf deines FB nähmlich der Offset deiner Adressen.

Wenn du z.B. in deinem übergeordnetem FB 5 Multiinstanzen vom vom Typ "Mein_FB" hast, und dein "Mein_FB" z.B. selbst 5 Wörter enthält, dann hat der erste Aufruf von "Mein_FB" die Adressen 0-8 (Byteweise eben)

Der zweite Aufruf belegt die Adressen 10-18.
Der dritte Aufruf (eigentlich ist die deklarationsreihenfolge wichtig...) belegt die Adressen 20-28...

Angenommen dein erstes Wort besteht aus diesen Bits, die du gerne einzeln und vor allem schön mit symbolik versehen möchtest. Der IN-Parameter heisst "Statuswort"

Du erstellst nun in "Mein_FB" ein Array aus 16 Bit, dass nennst du z.B. Statusbit[0..16]. Du kannst natürlich auch einfach 16 Bits einzeln deklarieren, so hast du alle Freiheiten bei der Auswahl der Namen für die einzelnen Bits. Für die Übersichtlichkeit ist es noch schöner wenn man diese Bits wiederum in eine Struktur verpackt. So kannst du dann später z.B. schreiben:

U #Statusbit.AchseBereit
O #Statusbit.FehlerAntrieb
...

Diese Struktur hat nun die Adresse 2.0

Nun kannst du schreiben

L #Statuswort
T DIW[ar2,p#2.0]

In "Statuswort" steht der Wert, den du beim Aufruf übergeben hast. Ich sag jetzt mal 256.

Der wird jetzt in dein Array transferiert, damit du den Zugriff auf die einzelnen Bits hast.

Zu lesen ist das obige in etwa so:

Lade den Wert der in #Statuswort steht.
Transferiere in das DIW (Wort im Instanzdatenbaustein) mit der Adresse: Pointerwert im AR2 + 2 Bytes.

Im AR2 steht nun z.B. beim zweiten Aufruf des "Mein_FB" die Adresse 10.0, weil es ja die Anfangsadresse der Daten von "Mein_FB" ist.
Durch das p#2.0 hinter dem AR2, gibst du nochmals einen Offset zum AR2 dazu, da es ja nicht dein erstes Wort ist, in das du transferieren möchtest, sondern das zweite. Im ersten steht ja die In-Variable Statusword...

Bei der Sache musst du aber bedenken, das nun innerhalb von Mein_FB nichts mehr im Kopf verschoben werden sollte, da du sonst die Offsets auf AR2 immer anpassen musst. Diese Art der Programmierung sollte dann auch einen "abgeschlossenen" Baustein ergeben, der eine bestimmte Funktion hat, und denn man nach dem testen nur noch benutzt, und nicht alle Nase lang ändert.

Besser kann ichs leider nicht erklären ohne Bildchen...

Torsten
 
Zuviel Werbung?
-> Hier kostenlos registrieren
ich hab gerade eines meiner Programme von globalen Timern befreit und diese gegen IEC-Timer ausgetauscht. Problem bei meiner Anlage: Zykluszeit! Ich hab noch nicht ins AG übertragen, aber ich hab Bammel, dass die IEC-Timer volll auf die Zykluszeit schlagen.. Ich hab die SFB's als Stat-Variable aufgeschlagen usw. aber ich hab das Prog noch nicht übertragen. Wenn es auf der CPU313C zu langsam wird, dann muss ich doch wieder globalvariablen nehmen....

;-)
Approx
 
Hat jemand von Euch ne Ahnung davon, ob ieC-Timer soviel Recourcen verbraten, oder ob man als lieber Programmierer doch lieber globale
Adressen nimmt?
meinem Freundin schläft schon!
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo!

Leider hast du nicht deinen ganzen Code hereingestellt aber was ich so bei der Variablendekleration gesehen habe wird dein Programm nicht funktionieren wenn du es Öfters aufrufst bzw in einem anderen Baustein auch Lokaldaten verwendest.
Bei Flanken muss der Hilfsmerker eine Statische Variable sein sonst wird der Lokaldatenbereich in einem anderen Baustein überschrieben und deine Flanke funktioniert nicht mehr.

Zu Multiinstanzen:
In der Suche findet man da schon sehr viel dazu aber kurz:
Du legst dir einen Übergeordneten FB an. in diesem kannst du dann in den Statischen Variablen eine Variable mit zb myTime anlegen und bei datentype wählst du FB aus und schreibst deine FB nummer von dem timer dazu.
Dann brauchst du nur mehr im programm call #myTimer schreiben und du hast eine Multiinstanz kreiert.
Die Daten von deinem Zeit FB werden danach in deinem Instanzdatenbaustein vom Übergeordneten FB abgelegt. Also du brauchst da nicht selber einen DB basteln und mit Pointern darauf zugreifen.

godi

Hi Godi,

ich habe
1. deinen Rat befolgt und die Flankenmerker in den statischen Bereich verschoben und
2. einen übergeordneten FB angelegt, der den "Arbeits-FB" mehrmals aufruft.

Ich habe das mal als Programm angehängt in der Hoffnung, dass sich das mal jemand ansehen könnte und mir sagt, ob das so laufen wird, denn leider habe ich keinen Simulator und an die Hardware zum üben komme ich erst wieder nächste Woche. Aber wissen möchzte ich es jetzt schon gerne.

Danke erstmal an alle, die mir auf die Sprünge geholfen haben.

Gruß Wilhelm

Anhang anzeigen Mein_1_Multi_FB.zip
 
Hallo!

Habe jetzt mal kurz in dein Programm hineingeschaut.
Da mit der Multiinstanz hast du mich falsch verstanden.
Den FB 1 darfst du nicht mit UC aufrufen da du mit dem Aufruf keine Parameter übergibst und somit auch keinen Instanzdatenbaustein für deinen FB 1 erzeugst. Du musst das mit CALL machen.

Die Multiinstanzdeklaration stimmt in deinem FB1 aber du darfst dann den FB2 nicht direkt aufrufen sondern über die Variable. Also in deinem Fall CALL #Takt_1
Dann werden die Variablen die du in deinem FB 2 verwendest im Instanzdatenbaustein von deinem FB1 gespeichert.

Dein Taktprogramm habe ich nur kurz überflogen. Sollte soweit funktionieren nur kann man da sicher noch einiges optimieren bzw anders machen. ;)
Aber du kannst ja wenn du an der Hardware bist ja selber herumprobieren. ;)

godi
 
Hallo!

Habe jetzt mal kurz in dein Programm hineingeschaut.
Da mit der Multiinstanz hast du mich falsch verstanden.
Den FB 1 darfst du nicht mit UC aufrufen da du mit dem Aufruf keine Parameter übergibst und somit auch keinen Instanzdatenbaustein für deinen FB 1 erzeugst. Du musst das mit CALL machen.

Die Multiinstanzdeklaration stimmt in deinem FB1 aber du darfst dann den FB2 nicht direkt aufrufen sondern über die Variable. Also in deinem Fall CALL #Takt_1
Dann werden die Variablen die du in deinem FB 2 verwendest im Instanzdatenbaustein von deinem FB1 gespeichert.

Dein Taktprogramm habe ich nur kurz überflogen. Sollte soweit funktionieren nur kann man da sicher noch einiges optimieren bzw anders machen. ;)
Aber du kannst ja wenn du an der Hardware bist ja selber herumprobieren. ;)

godi



Tausend Dank, habe die Änderungen wie von dir beschrieben gemacht.

Den FB1 rufe ich jetzt so auf : Call FB1, DB2
Der DB2 wird auch vom Programm so angelegt, dass alle Deklarationen der 4 Instanzen im FB1 vorhanden sind.

Im FB1 rufe ich die einzelnen Instanzen des FB2 jetzt so auf : Call #Takt_1, Call #Takt_2 etc.

Der Vollständigkeit halber habe ich das Ganze nochmal als ZIP hochgeladen.

Das Taktprogramm als solches läuft, hatte ich als FC vorher schon mal gehabt. Ob das jetzt der Weisheit letzter Schluss ist, weiß ich auch nicht, aber es funzt prima.

Ob das alles in der SPS auch so läuft, gucke ich mir nächste Woche an. Wichtig war mir, dass ich verstehe, wie sowas prinzipiell funktioniert. Das habe ich ja dank eurer Hilfe geschafft.

Danke an alle dafür.

Gruß Wilhelm

Anhang anzeigen FB_Versuch2.zip
 
Zurück
Oben