# CoDeSys Zeitsteuerung mittels Taskkonfiguration



## Marco_ET12 (19 Mai 2020)

Hallo Liebes Forum,

kurz zu meiner Person. Ich studiere Elektrotechnik und belege dieses Semester das Modul Steuerungstechnik. Da auch die Uni fest im Corona-Griff ist, laufen alle Module dieses Semester als "Homeoffice". Das ist doch perfekt für Programmiermodule.:-D

So jetzt aber zu meinem Problem:

Im Sinne eines Labortestats soll eine Kreuzschaltung für ein Treppenhaus mittels 3 Schaltern in der Sprache FUP realisiert werden.
Als Entwicklungsumgebung nutzen wir hierzu CoDeSys, da dies etwas entspannter in der Simulation ist als TIA. 

Die Kreuzschaltung selbst habe ich schon programmiert und per Visualisierung getestet. Das funktioniert alles und war auch recht einfach. Das grundlegende Wissen ist also vorhanden. 

*Im nächsten Schritt soll ich nun einen zeitgesteuerten Task konfigurieren, der das Programm alle 50ms ausführt. Und genau da ist mein Problem. Ich habe schon alles versucht, komme jedoch nicht hinter die Funktionsweise/Einstelung der Task´s. Auch im Handbuch von Beckhoff komme ich nicht weiter. Leider bin ich aufgrund von Corona deshalb komplett auf mich selbst gestellt. Ich verstehe einfach nicht, wie ich diese Task einstellen muss, damit das funktioniert. *

Vielleicht hat jemand von euch ja schon mit Taskkonfigurationen gearbeitet und kann mir dahingehend vielleicht etwas helfen. Wäre schön, wenn sich Leute finden würden, die das mit mir zusammen angehen.

Ich möchte auch keine fertigen Lösungen bzw. Codes, denn mir ist es sehr wichtig, das selbst zu lernen und auch zu verstehen. Dieses Forum wurde mir wärmstens von Professoren empfohlen. 

Liebe Grüße und eine schöne, sonnige Woche.

Marco


----------



## Mavorkit (19 Mai 2020)

Moin Marco,

CoDeSys 2.3 oder 3.5?

Gruß

Mavorkit

Gesendet von meinem SM-G950F mit Tapatalk


----------



## Marco_ET12 (19 Mai 2020)

Moin Mavorkit,

ich wusste, dass ich etwas vergessen habe. 
Die Version ist v3.5.


----------



## oliver.tonn (19 Mai 2020)

Na ja, wenn Du in TC3 nicht gerade einen leeren SPS Knoten hinzugefügt hattest legt er neben einem Programm auch eine Task mit 10ms an, deren Eigenschaften musst Du nun einfach anpassen.

Von irgendwas mit Internetzugang gesendet.


----------



## Marco_ET12 (19 Mai 2020)

Und genau da kommt mein Problem. Ich habe eine Mail Task die zyklisch abgeabeit wird. Ich habe dieZykluszeit dort auf 50 ms angepasst. Als POU habe ich dann mein PRG angehängt. Dem Task habe ich die PRIO 1 gegeben. Sobald ich aber auf Visualisierung klicke, läuft gar nichts von alleine. Irgendwie muss ich doch etwas machen, damit das Programm duchläuft. 

Ich habe in der VISU 3 Schalter und eine Lampe eingesetzt. Wie muss ich den Task aber einstellen, dass bei Start die Lampe leuchtet und alle Schalter durchgeschaltet werden. Oder habe ich den Sinn der Task nicht verstanden? Ist das villt so gar nicht realisierbar? 

Falls gefordert, kann ich gerne meinen FUP mal hier anhängen.


----------



## oliver.tonn (19 Mai 2020)

Also der "normale" Vorgang wäre, dass Du unter TC3 ein neues Projekt anlegst und dann unter dem SPS-Knoten eine SPS. Soweit Du als Vorlage nicht die leere SPS gewählt hast erzeugt TwinCAT einen Knoten mit einer Task und einem Programm das dieser Task zugeordnet ist und somit zyklisch ausgeführt wird.
Wie bist Du denn bei der Anlage des Projektes vorgegangen?

Das die Lampe beim Start der SPS leuchtet legst Du über die mit dieser Lampe verknüpften Variable fest. Wenn Du bei der Deklaration angibst, dass diese TRUE sein soll leuchtet beim Start auch die Lampe.

Was meinst Du mit alle Schalter durchgeschaltet werden?


----------



## Marco_ET12 (19 Mai 2020)

Hi Oliver,

ich glaube ich verstehe nicht so ganz was du meinst. Ich benutze leider kein TwinCat. Wir programmieren generellnur mit Codesys 3.5. Eine SPS habe ichauch nicht. Sinn ist es, einfach über die CoDeSys Visualisierung das Programm zu testen. Deshalb verstehe ich nicht ganz, was du mit TwinCat meinst. 

Mit durchschalten meinte ich, dass das Programm durchläuft und alle Zustände der Schalter ausführt. Ich verstehe nicht ganz die Aufgabenstellung. Das Programm soll alle 50ms durchlaufen. 

Bei einer Kreuzschaltung denke ich, sollen alle Schalter nacheinander auf TRUE und FALSE gesetzt werden, damit man sieht, dass das Programm funktioniert. Oder verstehe ich den Sinn einer Task falsch? Ich verstehe einfach nicht wie man dies umsetzt. Wie sehe ich, ob die Task läuft. Ich habe die Task auf 50ms gesetzt. Aber starte ich das Programm läfd es auch nicht anders als ohne. Ich hoffe, du verstehst was ich meine.


----------



## Mavorkit (19 Mai 2020)

Hi Marco,

Oliver arbeitet mit TwinCat 3, was auf CoDeSys 3.5 basiert. Es kann nur sein, dass die Oberfläche und manche Funktionen andert aufgebaut sind. 

Ich denke am besten stellst du mal dein Programm hier rein, dann kann ich mir mal deine Taskkonfiguration anschauen. Aus der Ferne blind ist immer etwas schwierig.

Gruß

Mavorkit


----------



## oliver.tonn (19 Mai 2020)

Sorry, ich hatte bei Deinem ersten Post etwas von Beckhoff gelesen und bin daher von TwinCAT ausgegangen.
Eine SPS hast Du eigentlich immer, denn auch Codesys installiert auf dem Entwicklungsrechner eine Runtime.
Ich kann gerade kein Codesys 3.5 installieren, aber auch da sollte nach Anlage eines Projektes schon alles (Task, Programm, usw.) angelegt worden sein. In dem Programm musst Du jetzt Variablen deklarieren und diese dann mit den Objekten Deiner Visu verknüpfen.
Die Aufgabe ist eine Kreuzschaltung zu realisieren mit der eine Lampe angesteuert wird. Du musst ein Programm schreiben das zwei Variablen vergleicht und je nach Ergebnis eine Variable setzt. In der Visu platzierst Du dann zwei Schalter die mit den zu vergleichenden Schaltern verknüpft werden und eine Lampe die mit der dritten Variable verknüpft wird. Das Programm muss, soweit nicht automatisch geschehen einer Task zugeiwesen werden, damit es ausgeführt wird und die Task so konfiguriert werden, dass sie zyklisch alle 50ms startet.


----------



## Marco_ET12 (19 Mai 2020)

Soooooo, 

@ Mavorkit: jetzt habe ich mal ein paar Shots aus CoDeSys gemacht. Realisiert habe ich das Ganze mit einem XOR-Gatter. Das geht ja am einfachsten.

@Oliver: Das macht nichts. Ich wusste auch nicht, dass TwinCat ähnlich CoDeSys ist. 


So, der Maintask war auch schon automatisch auf zyklisch eingestellt. Das ergibt ja auch Sinn. Ich habe lediglich die Zykluszeit von 20ms auf meine 50ms angepasst. Als POU steht mein PRG drin.

So aber jetzt verstehe ich nicht wie ich weitermachen soll. 

Das Programm soll ja mittels Taskkonfiguration alle 50ms selbstständig ausgeführt werden. Wo kann ich das live verfolgen? 
Weil, wenn ich nun auf "einloggen" klicke und die Simulation starte, passiert gar nichts. Ich kann halt ganz normal die Schalter TRUE and FALSE setzen und dann geht eben die Lampe an oder nicht. Allerdings merke ich nichts von einer Automatik. 

Ich kann mir 100%-ig vorstellen, dass mein Kopf was anderes denkt, als er soll:-D


----------



## oliver.tonn (19 Mai 2020)

Marco_ET12 schrieb:


> So, der Maintask war auch schon automatisch auf zyklisch eingestellt. Das ergibt ja auch Sinn. Ich habe lediglich die Zykluszeit von 20ms auf meine 50ms angepasst. Als POU steht mein PRG drin.
> 
> So aber jetzt verstehe ich nicht wie ich weitermachen soll.
> 
> ...


Jetzt musst Du Dich mit Deiner SPS verbinden, das Ganze herunterladen und starten, Deine Visu öffnen, die Schalter betätigen, fertig. Deine SPS ist dabei Deine lokale Runtime.
Mach mal bitte einen kompletten Screenshot wenn Du Online bist.


----------



## Marco_ET12 (19 Mai 2020)

Ja das habe ich gemacht, aber leider sehe ich nicht, wie die Task jetzt arbeitet. 
Lege ich Schalter S1 um, leuchtet die Lampe. Ok. Aber das ging ohne Task ja auch. Wo genau kommt jetzt der Task ins Spiel. Ich habe schon die Werte verstellt, oder den Betrieb von Zyklisch auf freilaufend. Das Ergebnis ist immer gleich. Irgendwas stimmt doch da nicht:-D


----------



## plcSniffer (19 Mai 2020)

Hier besteht ein grundsätzliches Verständnisproblem. Deine Task wird alle 50ms abgearbeitet. Möchtest du, dass sich Schalterstände abwechselnd pro Zyklus automatisch ändern, so musst du dies auch programmieren.


----------



## Marco_ET12 (19 Mai 2020)

AUAAAAAAA,

Ok ich glaube, ich habe meinen Kopf mal wieder zurückgeholt. Ich glaube ich hatte einen falschen Ansatz.

Ich dachte, dass der Task dazu da ist, das Programm selbstständig ablaufen zu lassen. Dass ich quasi einen Taster drücke, und dann schaltet der Task alle Schalter automatisch durch, und ich sehe ob die Lampe blinkt oder nicht. also quasi so, als ob der Task für mich das Drücken der Tster übernimmt. 




Hier kann ich nun sehen, wie mein Programm durchschnittlich mit 5Müsec abgearbeitet wird. . Ich glaube jetzt habe ich es verstanden. Und mit der Prio kann ich eben festlegen, wann ein Task bearbeiutet wird.

Stimmt das so?


----------



## oliver.tonn (19 Mai 2020)

Marco_ET12 schrieb:


> Ja das habe ich gemacht, aber leider sehe ich nicht, wie die Task jetzt arbeitet.
> Lege ich Schalter S1 um, leuchtet die Lampe. Ok. Aber das ging ohne Task ja auch. Wo genau kommt jetzt der Task ins Spiel. Ich habe schon die Werte verstellt, oder den Betrieb von Zyklisch auf freilaufend. Das Ergebnis ist immer gleich. Irgendwas stimmt doch da nicht:-D


Du solltest Dir erstmal eine Doku zur Funktionsweise einer SPS durchlesen.
Task heißt übersetzt Aufgabe und stellt vereinfacht ausgedrückt einen Container dar in den auszuführende Programme (Die Aufgabe/n) gelegt werden. Über die Konfiguration der Task wird nun festgelegt, wie die Programme ausgeführt werden sollen, einmalig oder wie in Deinem Fall Zyklisch, also immer wieder. Das die Task arbeitet siehst Du daran, dass Dein Programm etwas tut. Du betätigst einen Schalter und in Deinem Programm wird die Ausgangsvariable entsprechend gesetzt. Übrigens soweit Du nicht einen Schalter und die Lampe auf die selbe Variable gelegt hattest wäre ohne Task nichts passiert. Ach ja, bei drei Schaltern kannst Du kein XOR nehmen, allerdings ist das dann auch (meine ich) keine Wechselschaltung mehr.
Es stimmt übrigens alles. Nur weil Du keinen Unterschied siehst heißt das nicht, dass es nicht läuft. Eine zyklische Task wir regelmäßig ausgeführt und zwar immer nach der eingestellten Zeit. Eine freilaufende Task wird erneut ausgeführt sobald sie durchgelaufen ist ohne eine eventuelle Pause.


----------



## oliver.tonn (19 Mai 2020)

Marco_ET12 schrieb:


> Und mit der Prio kann ich eben festlegen, wann ein Task bearbeiutet wird.
> 
> Stimmt das so?


Nein mit der Prio wird festgelegt welche Task als erstes ausgeführt wird, wenn mehrere Gleichzeitig starten würden.


----------



## Mavorkit (19 Mai 2020)

Der Unterschied ist theoretisch wenn du vorher 21ms Impulse erzeugt hast, wurden die vom Programm erfasst, das wäre jetzt nicht mehr der Fall.

Freilaufend heißt dein Programm wird nach dem Ausführen Theoretisch wieder direkt gestartet. Praktisch werden da noch andere Tätigkeiten wie die Lokalbusabfrage dazwischengeschoben. wenn das Programm z.B. 3ms läuft kann es sein, das der zweite Programmaufruf schon nach 5ms wieder angestoßen wird.

Zyklisch heißt, das Programm wird alle 50ms aufgerufen und abgearbeitet. Also bei 50ms, dann wieder bei 100ms .....

Bei deinem Programm jetzt macht das noch wenig Sinn, aber bei großen Programmen kannst du damit die Performance so einstellen, dass z.B. ein Regler sehr schnell ausgeführt wird, bei einer Lampenansteuerung wirst du den Unterschied von 20ms auf sagen wir mal 500ms gar nicht merken.

Wo eine Schnelle Zykluszeit noch Sinn macht, ist z.B. bei der Abfrage von Tastern. Wenn du hier den Zyklus auf 500ms stellst, wird dein Taster nicht mehr immer funktionieren.

Ich hoffe das hilft dir weiter.

Gruß

Mavorkit


----------



## PN/DP (19 Mai 2020)

Programmiere einen "Zykluszähler": z.B. eine UINT- oder INT-Variable in jedem Zyklus inkrementieren (ADD 1) und beobachte den Wert der Variable. Der müsste sich dann je Sekunde um 20 erhöhen - so siehst Du daß die Task alle 50ms ausgeführt wird.

Zykluszähler Code in ST:

```
MyCounter := MyCounter + 1;
```

Harald


----------



## Marco_ET12 (19 Mai 2020)

Danke euch, jetzt habe ich es verstanden. Ich stand wirklich auf dem Schlauch. 

Eine Frage habe ich jedoch noch. Ich haabe ja in meinem Task eingegeben, dass er eine Zykluszeit von 50ms haben soll. 

In dem letzten Bild was ich euch geschickt habe, sieht man ja die Überwachung. Mich wundert es, dass dort eine durchschnittliche Zykluszeit von 5microsekunden steht. Oder hat dieser Wert ncihts mit meiner ein gestellten Zeit zu tun? 



> Wo eine Schnelle Zykluszeit noch Sinn macht, ist z.B. bei der Abfrage von Tastern. Wenn du hier den Zyklus auf 500ms stellst, wird dein Taster nicht mehr immer funktionieren.


 Hat das mit dem Prellen zu tun?


----------



## plcSniffer (19 Mai 2020)

Deine Task wird alle 50ms abgearbeitet. Die Bearbeitung der Task dauert aber nur 5us. Umso umfangreicher dein Programm ist, umso länger wird die Bearbeitung der Task benötigen.


----------



## oliver.tonn (19 Mai 2020)

Marco_ET12 schrieb:


> Eine Frage habe ich jedoch noch. Ich haabe ja in meinem Task eingegeben, dass er eine Zykluszeit von 50ms haben soll.
> 
> In dem letzten Bild was ich euch geschickt habe, sieht man ja die Überwachung. Mich wundert es, dass dort eine durchschnittliche Zykluszeit von 5microsekunden steht. Oder hat dieser Wert ncihts mit meiner ein gestellten Zeit zu tun?​


​Das ist zugegebenermaßen etwas missverständlich. Damit ist gemeint, wie lange die Task gebraucht hat um komplett durchzulaufen. Anhand dieses Wertes kannst Du sich anbahnende Probleme erkennen, sprich eine mögliche Zykluszeitüberschreitung. Nähert sich dieser Wert der Zykluszeit müsstest Du diese erhöhen oder, soweit mehrere Programme aufgerufen werden diese auf mehrere Tasks verteilen.
​


Marco_ET12 schrieb:


> Hat das mit dem Prellen zu tun?


Nein, dafür gibt es entweder I/Os mit entsprechenden Filtern oder das löst Du im Programm. Je kürzer eine Zykluszeit ist umso stärker steigt die Auslastung der CPU durch verschiedene Zykluszeiten kannst Du dies steuern. Eine Kommunikation soll ja meist relativ flott erfolgen, daher nimmt man dafür eine Task mit einer kurzen Zykluszeit, vom Benutzer zu bedienende Schalter müssen dagegen nicht so schnell ausgewertet werden, da kann dann eine längere Zykluszeit benutzt werden.


----------



## PN/DP (19 Mai 2020)

Marco_ET12 schrieb:


> > Wo eine Schnelle Zykluszeit noch Sinn macht, ist z.B. bei der Abfrage von Tastern. Wenn du hier den Zyklus auf 500ms stellst, wird dein Taster nicht mehr immer funktionieren.
> 
> 
> Hat das mit dem Prellen zu tun?


Nein. 
Je länger die Zykluszeit eingestellt ist, desto länger muß ein Signal (z.B. Tastendruck) anstehen, damit es sicher erkannt und verarbeitet wird. Wenn die Task nur alle 500ms auf den Eingang vom Taster schaut, dann wird der nur dann sicher erkannt, wenn mindestens 500ms lang gedrückt wird. Kürzere Tastendrücke werden nur erkannt, wenn die Task da zufällig gerade in dem Moment den Eingang liest.

Harald


----------



## Marco_ET12 (19 Mai 2020)

Danke euch. Jetzt habe ich es verstanden.

Ich habe auch noch eine weitere Aufgabe, bei der ich etwas Hilfe benötigen könnte. 

Ich soll ein Lauflicht entwickeln. Ebenfalls wieder komplett über FUP. Schade, in ST wäre das irgendwie schöner. Naja.....

Am Ende sollen das 32 Lampen sein die von links nach rechts als Lauflicht einmal durchlaufen. Also ein DWORD.
Ich habe mir folgendes überlegt: Ich mache erste Versuche mit 8 Lampen, also einem BYTE, da dies als Visualisierung etwas entspannter ist, als 32 Lampen einzufügen.

Als Lösung wollte ich dies mit einem ROR-Glied realisieren, wobei ein Execute-Baustein ( enthält eine IF-ELSE Bedingung) das Shiften hochzählt. 

Hier habe ich auch mal ein kleines Bild:



Meine Frage ist nun, ob ihr eine elegantere Lösung kennt, wie man dieses Lauflicht in FUP realisieren kann, sodaß es auch schön aussieht. Mir fällt nur der Rotation-Operator ein. Aber ich lerne auch gerne dazu.


----------



## oliver.tonn (19 Mai 2020)

Soweit er nicht noch irgendwelche Kniffe haben  will hast Du die Lösung doch schon geschrieben, Rotation.
Die Frage wäre jetzt, ob das Licht bei jedem Zyklus um 1 weiterlaufen soll, falls nein bräuchtest Du noch einen Timer.
Was soll übrigens der Execute bewirken?


----------



## Heinileini (19 Mai 2020)

oliver.tonn schrieb:


> Ach ja, bei drei Schaltern kannst Du kein XOR nehmen, allerdings ist das dann auch (meine ich) keine Wechselschaltung mehr.


 
HardwareLösung:
- Bei 2 Schaltern: 2 WechselSchalter, Schaltung wird WechselSchaltung genannt.
- Bei mehr als 2 Schaltern: zwischen den 2 WechselSchaltern nur noch "KreuzSchalter" einfügen, Schaltung heisst jetzt KreuzSchaltung.
(Ein KreuzSchalter besteht aus zwei mechanisch gekoppelten WechselSchaltern (= "UmSchaltern"), die elektrisch so verdrahtet sind, dass die beiden "Eingänge" je nach SchalterStellung entweder vertauscht oder unvertauscht mit den beiden "Ausgängen" verbunden sind) 

SoftwareLösung:
- Bei 2 .. n Schaltern: n-1 XOR Verknüpfungen.

PS:
Ein KreuzSchalter kann selbstverständlich auch verwendet werden, wenn "nur" ein WeschselSchalter gebraucht wird. Ist aber "überqualifiziert" und kostet mehr


----------



## Marco_ET12 (19 Mai 2020)

Hi Oliver,

der Execute Baustein übernimmt genau diese Timer-Funktion. 

An den Baustein ist eine Variable Rotieren geknüpft, die pro Zyklus um 1 erhöhr wird. Der Wert dieser variablen ist dann ist Anzahl der Rotationen im ROR-Operator. 

Bei einem BYTE müsste ich 8 Mal Rotieren lassen, damit ich meine Bitanordnung wieder im Ausgangszustand habe.  Als Beispiel 1000000. Lasse ich einmal rotieren habe ich als Ergebnis 01000000, zweimal 00100000 usw. Somit habe ich bei 8 Mal rotieren einmal alle Lampen als Lauflicht erfasst. Die Lampe leuchtet als Lauflicht von links nach rechts. Wenn ich nun die Zykluszeit auf 125ms setze dann dauert das Lauflicht genau 1s. wenn ich nun im Execute einfach statt 8, 80 eingebe, dann läuft das Lauflicht 10 Mal von Links nach Rechts.

Ich hoffe das ist verständlich.


----------



## PN/DP (19 Mai 2020)

Für Zeiten und Zeittakte die wesentlich länger als die Zykluszeit sind, nimmt man Timer-Bausteine, z.B. TON, TOF. Damit kannst Du auch spielend einfach einen Weiterrück-Takt von 500ms oder 1s oder ... erzeugen.

Harald


----------



## Marco_ET12 (19 Mai 2020)

@Harald. 

Da gebe ich dir vollkommen Recht. Ich hatte nur vergessen zu erwähnen, dass wir keine TIMER benutzen sollen. Ansonsten wäre dein Vorschlag natürlich der definitiv einfachere und effizientere.


----------



## Marco_ET12 (19 Mai 2020)

Vielleicht kann mir noch jemand helfen für ein anderes Thema.

Ich soll mittels FUP ( und auch nur FUP) einen Converter programmieren, der eine vierstellige Dezimalzahl in einen 8-4-2-1 BCD Code umwandelt. 

Also wie die Umwandlung mathematisch funktioniert ist ja klar. Das ist auch ganz einfach. Aber wie zum Teufel mache ich das bitte in FUP?????????

Ich glaube der Prof denkt, man hat durch Corona zu viel Zeit.


----------



## Heinileini (19 Mai 2020)

Marco_ET12 schrieb:


> ... einen Converter programmieren, der eine vierstellige Dezimalzahl in einen 8-4-2-1 BCD Code umwandelt.


Eine vierstellige DezimalZahl oder eine positive vierstellige DezimalZahl?


----------



## Marco_ET12 (19 Mai 2020)

Auf jeden Fall eine positive vierstellige Dezimalzahl. Als Beispiel soll man dies für 1234 zeigen. In BCD gibt das ja 0001 0010 0011 0100.
Soweit so klar. Nur wie programmiere ich das jetzt in FUP. Vor allem, wenn man auch noch andere Zahlen eingeben kann. Aber nur positive.


----------



## Marco_ET12 (19 Mai 2020)

Was ich biher gemacht habe, ist eine Wahrheitstabelle aufzustellen. Dabei habe ich gesehen, dass ich prinzipiell 9 Eingangsvariablen (1-9) habe. Es sollen ja nur vierstellige Zahlen dargestellt werden. Somit fällt die 0 raus.
Weiterhin habe ich insgesamt 4 Ausgangsvariablen. Jede Ziffer von 0 bis 9 kann ja durch 4 Bit dargestellt werden. BitA, BitB, BitC, BitD. 

Möchte ich also die 1 als BCD darstellen, wäre die DNF dazu : nichtA ^nichtB ^nichtC ^ D.  Das ^ bedeutet logisch AND. 
Und so kann ich das für alle 9 Ziffern aufstellen. 
Die Frage ist nur, wie ich das programmiere. Ich muss ja am Anfang irgendwie eine Zahl eingeben. Ich kann ja auch nicht einen Eingang mit 4 Ausgängen verknüpfen. Verfolge ich den falschen Ansatz?


----------



## Heinileini (19 Mai 2020)

Falscher Ansatz.
Jede DezimalStelle isolieren (Division und Mod) mit 10er Potenzen und mit entsprechender 16er Potenz multiplizieren (mal 16^3 entspricht nach links schieben um 12 Positionen; mal 16^2 entspricht nach links schieben um 8 Positionen; mal 16^1 entspricht nach links schieben um 4 Positionen). ZwischenErgebnisse zusammenODERn.
Sorry, ausführliche Antwort kann ich im Moment nicht so schnell geben. In FUP dauert's noch viel länger ...


----------



## PN/DP (19 Mai 2020)

Marco_ET12 schrieb:


> Ich soll mittels FUP ( und auch nur FUP) einen Converter programmieren, der eine vierstellige Dezimalzahl in einen 8-4-2-1 BCD Code umwandelt.


Nur in 4 Ziffern zerlegen oder auch wieder zu 16 Bit Word im BCD-Code zusammenführen?
Das entspricht einer Umwandlung von 1234 dezimal ---> 16#1234

```
myInt muß im Bereich 0..9999 sein!

z1 := myInt / 1000;
z2 := (myInt / 100) MOD 10;
z3 := (myInt / 10) MOD 10;
z4 := myInt MOD 10;

OutBCD := z1 * 4096 + z2 * 256 + z3 * 16 + Z4;
```
Das kann man auch in FUP mit DIV MOD MUL ADD realisieren.
Die Prüfung/Beschränkung auf 0..9999 kann man mit Vergleichsoperatoren und LIMIT MIN MAX machen.

Harald


----------



## Marco_ET12 (19 Mai 2020)

@PN/DP
Also die Aufgabe ist so gedacht, dass die Zahl 1234 dann als Binätzahl dargestellt wird. Also am Ende soll dann 0001 0010 0011 0100 dastehen. Wie würde man dies am Ende dann wieder als eine BCD Zahl darstellen?

Erstmal vielen Dank euch für die Antworten. Ich versuche es mal später in FUP zu realisieren. Ggffls. melde ich mich nochmal.


----------



## PN/DP (19 Mai 2020)

Wo sollen die Binärziffern "zu sehen" sein?
Du könntest das Word OutBCD als Binärzahl 2#... beobachten (geht das in Codesys?)
Oder einer 16fach-Digitalausgabebaugruppe zuweisen (z.B. AT %AW2), dann leuchten die LED der Baugruppe entsprechend den Binärziffern. 
Oder den BCD-Wert als Text/String ausgeben mit jedes Bit ist ein Zeichen '0' oder '1'?

Harald


----------



## Heinileini (19 Mai 2020)

Marco_ET12 schrieb:


> Also am Ende soll dann 0001 0010 0011 0100 dastehen. Wie würde man dies am Ende dann wieder als eine BCD Zahl darstellen?


Anzeige von Binär auf Hexadezimal umstellen.


----------



## Marco_ET12 (19 Mai 2020)

Die Ziffern Sollen meiner Meinung nach einfach irgendwo nachvollziehbar sein. So genau bin ich mit CoDeSys nicht vertraut. Ich könnte ja eine VISU erstellen mit 32 Lampen. allerdings sieht das bekloppt aus und nimmt viel Zeit in Anspruch. 
Eherlich gesagt kenne ich das nur von TIA mit den Beobachtungstabellen. In Codesys wüsste nur, dass es per VISU geht. 

Das mit der Baugruppe kann ich schlecht realisieren, da ich keine physische SPS zur Verfügung habe. Das müsste ich visuell machen.


----------



## PN/DP (19 Mai 2020)

Kann man in der Codesys-Visu nicht Zahlen (Word) als 2#... formatiert ausgeben? Ich kenne mich da nicht aus.
Und einmal 16 animierte Rechtecke in der Visu malen kann doch nicht so aufwendig sein, geht vermutlich schneller als hier 3 Beiträge zu schreiben... 

Harald


----------



## oliver.tonn (19 Mai 2020)

Entweder wie Harald vorgeschlagen hat (Mit %d für Dezimalzahl im Rechteck unter der Eigenschaft Text), ansonsten kannst Du, wenn Du Online bist auch Dein Programm beobachten und die Werte der Variablen sehen.

Von irgendwas mit Internetzugang gesendet.


----------



## Heinileini (19 Mai 2020)

PN/DP schrieb:


> Oder den BCD-Wert als Text/String ausgeben mit jedes Bit ist ein Zeichen '0' oder '1'?


... oder den BCD-Wert als Text/String ausgeben mit je einem der Zeichen '0' bis '9' für jede Tetrade?
Oder die Variablen z1 .. z4 aus Haralds #34 in je einem DezimalFeld anzeigen ... aber dann würde man sich natürlich fragen, "wozu der ganze Aufwand?", wenn die Visu "intern" die Wandlung in BCD sowieso ausführt und sie dabei keiner Nachhilfe bedarf. Sie kann ja sogar den nächsten Schritt: die Umwandlung in ASCII-Zeichen.
So bekommt man mal eine Vorstellung davon, was in einer Visu oder in einem PG u.a. alles so ablaufen muss, bis die Daten in lesbarer Form anzeigt werden können.
"Wozu soll ich das noch wandeln? Am PG kann ich doch sehen, dass es im Speicher schon so drinsteht." bekam ich mal von einem Kollegen zu hören, der die Ausgabe auf einem Panel programmieren sollte, das schlicht und einfach nur die fix und fertig angelieferten ASCII-Zeichen anzeigen konnte ​


----------



## Marco_ET12 (19 Mai 2020)

Danke euch allen für die große Mühe und Hilfe. 

Ihr gebt einem wirklich gute Tipps. Auch so, dass man selbst nochj nachdenken muss. Leider gibt es immer mehr Leute, die einfach nur fertige Lösungen wollen. Und dann wundert sich der Arbeitsmarkt warum solche Leute dann nichts können. 

Danke euch allen. Falls noch Fragen sind, quäle ich euch einfach ein bisschen8):-D:-D


----------



## Heinileini (19 Mai 2020)

oliver.tonn schrieb:


> ... Mit %d für Dezimalzahl im Rechteck unter der Eigenschaft Text ...


Moooment mal, den ins BCD-Format gewandelten Krempel als DezimalZahl anzeigen??? Als HexadezimalZahl!!!


----------



## Marco_ET12 (19 Mai 2020)

> [myInt muß im Bereich 0..9999 sein!
> z1 := myInt / 1000;
> z2 := (myInt / 100) MOD 10;
> z3 := (myInt / 10) MOD 10;
> ...


----------



## plcSniffer (19 Mai 2020)

Heinileini schrieb:


> Falscher Ansatz.
> Jede DezimalStelle isolieren (Division und Mod) mit 10er Potenzen und mit entsprechender 16er Potenz multiplizieren (mal 16^3 entspricht nach links schieben um 12 Positionen; mal 16^2 entspricht nach links schieben um 8 Positionen; mal 16^1 entspricht nach links schieben um 4 Positionen). ZwischenErgebnisse zusammenODERn.
> Sorry, ausführliche Antwort kann ich im Moment nicht so schnell geben. In FUP dauert's noch viel länger ...



Da war schon jemand so freundlich


----------



## Marco_ET12 (19 Mai 2020)

Autsch, 

wer lesen kann, ist klar im Vorteil. Ich glaube ich mach dann mal Schluss für heute.:-D:-D:-D. Danke euch nochmal.


----------



## Marco_ET12 (20 Mai 2020)

Hallo alle,

leider verstehe immer noch eine Sache nicht ganz. Und zwar das Shifting mit der 16^3 und 16^2. 

Ich habe eine vierstellige positive Dezimalzahl 1234. Diese ergibt 8-4-2-1-BCD codiert eine 16 Bit Binärzahl. Klar pro Dezimalzahl gibt es eine Tetrade. 
Soweit so gut. 

Also selektiere ich jede einzelne Dezimalzahl mithilfe der MUL,DIV,ADD und MOD Operatoren. Soweit auch klar. 

Ich verstehe aber den letzten Schritt nicht. Warum schiebe ich nun die Zahl 1 um 16^3 nach links? Eine Umwandlung ins Hexadezimalystem erfolgt doch durch Division der Dezimalzahl durch 16. Also kann es das schon mal nicht sein. 

Meine Überlegung ist nun, dass es an dem 16Bit Datentyp liegt. 16^0 ist die erste Tetrade, 16^1 die zweite, 16^2 die dritte und 16^3 die vierte. Da die 1 binär ja 0001 lautet, muss diese Zahl um 12 Stellen nach links verschoben werden, sodass sie in der vierten Tetrade ist. Das wäredann die 1000von den 1234. Die vierte Tetrade ist ja 16^3. Aber warum multipliziere ich das jetzt?
Und wie wäre es , wenn ich dann eine Zahl hätte, die BCD-codiert ein DWORD ergeben würde. Also 32 BIT. das wären ja dann 8 Tetraden. Hätte ich dann  das BIT um 32^7 verschieben müssen??? Sorry für all die Frgen, aber mir ist es wirklich wichtig, dass ich es auch verstehe.


----------



## Heinileini (20 Mai 2020)

Marco_ET12 schrieb:


> Ihr gebt einem wirklich gute Tipps. Auch so, dass man selbst noch nachdenken muss.


Hier kommt von Tausenden denkbaren Lösungswegen einer zum Nachdenken:

```
iTmp1 := Eingabe;
IF iTMP1 >= 0 AND iTmp1 <=9999 THEN

    wErg  := 0;

    iTmp2 := iTmp1 / 10 ** 3;
    wErg  := wErg OR SHL(IN:= INT_TO_WORD(iTmp2); N:= 4 * 3);  // links schieben um 12 BitPos entsprechend * 16^3 also * 4096
//  wErg  := SHL(IN:= wErg; N:= 4) OR INT_TO_WORD(iTmp2);  // alternativ zur vorausgehenden Zeile
    iTmp1 := iTmp1 - iTmp2 * 10 ** 3;  // "Ersatz" für iTmp1 MOD 10^3
    
    iTmp2 := iTmp1 / 10 ** 2;
    wErg  := wErg OR SHL(IN:= INT_TO_WORD(iTmp2); N:= 4 * 2);  // links schieben um  8 BitPos entsprechend * 16^2 also * 256
//  wErg  := SHL(IN:= wErg; N:= 4) OR INT_TO_WORD(iTmp2);  // alternativ zur vorausgehenden Zeile 
    iTmp1 := iTmp1 - iTmp2 * 10 ** 2;  // "Ersatz" für iTmp1 MOD 10^2
    
    iTmp2 := iTmp1 / 10 ** 1;
    wErg  := wErg OR SHL(IN:= INT_TO_WORD(iTmp2); N:= 4 * 1);  // links schieben um  4 BitPos entsprechend * 16^1 also * 16
//  wErg  := SHL(IN:= wErg; N:= 4) OR INT_TO_WORD(iTmp2);  // alternativ zur vorausgehenden Zeile 
    iTmp1 := iTmp1 - iTmp2 * 10 ** 1;  // "Ersatz" für iTmp1 MOD 10^1
    
    iTmp2 := iTmp1 / 10 ** 0;
    wErg  := wErg OR SHL(IN:= INT_TO_WORD(iTmp2); N:= 4 * 0);  // links schieben um  0 BitPos entsprechend * 16^0 also * 1           
//  wErg  := SHL(IN:= wErg; N:= 4) OR INT_TO_WORD(iTmp2);  // alternativ zur vorausgehenden Zeile 
    iTmp1 := iTmp1 - iTmp2 * 10 ** 0;  // "Ersatz" für iTmp1 MOD 10^0
    
ELSE

    wErg  := 16#FFFF // Fehler

END_IF;
```
Sieht furchtbar unelegant und aufwändig aus. Lässt ein wenig die Systematik durchblicken, legt die Realisierung in einer FOR-Schleife nahe (FOR idx := 3 TO 0 BY -1 DO) und kommt ohne den MOD-Operator aus.
Zu Deiner Frage nach der letzten Anweisung in Haralds Vorschlag: hier wird's scheibchenweise (à la Salami) gemacht, jedoch mit LinksSchieben und Odern statt Multiplizieren und Addieren.

PS:
Hast Du schon darüber nachgedacht, warum Dein Ansatz mit WahrheitsTabelle nicht so recht zum Ziel führt?


----------



## PN/DP (20 Mai 2020)

Marco_ET12 schrieb:


> Warum schiebe ich nun die Zahl 1 um 16^3 nach links?


Weil die Zahl 1 als BCD die Stellen/Positions-Wertigkeit 16^3 hat.



> Aber warum multipliziere ich das jetzt?


Weil viele Wege nach Rom führen  und weil "a * 16 + b * 256" verständlicher und einfacher/schneller getippt ist als "SHL(a, 4) + SHL(b, 8 )" (und Compilerfreundlich alle Operanden in der Formel vom Datentyp INT sind)

In FUP sind beide Wege gleich aufwendig einzugeben:

```
+-----+              +-----+
    | MUL |              | SHL |
 a--|     |---   =    a--|     |---
    |     |              |     |
16--|     |           4--|     |
    +-----+              +-----+
```
Bei BCD sind immer 4 Bits (eine Tetrade) eine Ziffer - wie bei Hexadezimalzahlen, mit dem Unterschied daß nur die Ziffern 0 bis 9 zulässig sind. Wegen der 4 Bits ist die Positions-Wertigkeit einer Ziffer eine 16-er Potenz, entsprechend der Position wo die Ziffer steht.

Eine Dezimalzahl (Basis 10) der Form abcd hat den Wert: a * 10^3 + b * 10^2 + c * 10^1 + d * 10^0
Eine BCD-Zahl (Basis 16) der Form BCD#abcd = 16#abcd hat den Wert: a * 16^3 + b * 16^2 + c * 16^1 + d * 16^0

Bei allen Zahlenformaten/Zahlensystemen entspricht die Verschiebung um eine Ziffernstelle nach Links einer Multiplikation mit der Basis des Zahlensystems. Eine Verschiebung um eine Ziffernstelle nach Rechts entspricht einer Division mit der Basis des Zahlensystems.

Bei Binärzahlen entspricht die Verschiebung um 1 Stelle (1 Bit) nach Links der Multiplikation mit 2 (dez), um 2 Stellen nach Links der Multiplikation mit 4 (dez) ... um 4 Stellen nach Links der Multiplikation mit 16 (dez) usw.

Bei BCD- und Hexadezimalzahlen entspricht die Verschiebung um 1 Ziffernstelle (1 Tetrade) nach Links einer Multiplikation mit 16 = 16^1, um 2 Ziffernstellen nach Links einer Multiplikation mit 16 * 16 = 16^2 = 256, um 3 Ziffernstellen nach Links einer Multiplikation mit 16 * 16 * 16 = 16^3 = 4096 usw.

Eine Umwandlung einer Dezimalzahl in eine BCD-Zahl ist nicht nur eine Änderung der Ansicht des Wertes in einem anderen Zahlensystem (wie z.B. 1234 = 16#04D2) sondern die Dezimalziffern werden in jeweils 4 Bits untergebracht/neu gruppiert, wodurch sich 1234 ---> BCD#1234 = 16#1234 (= 4660 dezimal) ergibt.




> Und wie wäre es , wenn ich dann eine Zahl hätte, die BCD-codiert ein DWORD ergeben würde. Also 32 BIT. das wären ja dann 8 Tetraden. Hätte ich dann  das BIT um 32^7 verschieben müssen???


Wenn Du eine Dezimalzahl der Form 12345678 hast, dann mußt Du die 1 mit 16^7 multiplizieren oder um 7 * 4 = 28 Bit nach Links schieben.
12345678 ---> BCD#12345678 = 16#12345678:
1 * 16^7 + 2 * 16^6 + 3 * 16^5 + 4 * 16^4 + 5 * 16^3 + 6 * 16^2 + 7 * 16^1 + 8 * 16^0

Harald


----------

