TIA Software UART am Digitalout einer S7-1200 ?

Bei 9600 Bit/s darfst du von der Bitlänge 104µs pro Bit eine maximale Abweichung von einem Zehntel/2, also 5µs haben.
Denn wenn der Empfänger nach dem Startbit in der Mitte sampelt, und dein Taktgeber läuft nach 10 Bit um >52µs daneben (z.B. immer zu kurz), dann wird das letzte Bit falsch erkannt.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich würde zunächst mal ganz einfach prüfen, ob RUNTIME() als Zeitgeber taugt und ob man den Programmcode so hoch-prior hinbekommt, daß das SPS-Betriebssystem da nicht mehr mit Unterbrechungen dazwischenfunkt. Wenn die Zeiterzeugung um mehr als 10µs schwankt, dann kann man die Idee "SoftUART mit S7-1200" wohl vergessen.
Ich habe fürs erste nur einen kurzen Testcode geschrieben, doch leider habe ich keine echte S7-1200 zum testen zur Verfügung, und TIA-PLCSIM und/oder der TIA-SCL-Compiler V13 SP1 Update 8 lassen mich als TIA-Einsteiger nicht so recht vorwärts kommen. PLCSIM crasht dauernd, wenn man den SCL-Code beobachtet, und irgendwie sind da wohl auch noch weitere Bugs von PLCSIM involviert. Z.B. habe ich stundenlang versucht rauszukriegen, warum meine einfache REPEAT...UNTIL-Schleife mit der Zeitgeberabfrage meistens zu einer Endlosschleife mit Zykluszeitüberschreitung mutiert und ob/wie ich das eventuell umgehen kann. SCL-Debugging mit TIA ist extrem schwierig. Es scheint mir so, daß zumindest bei PLCSIM die Anweisung RUNTIME() öfters gar keinen Wert in den InOut (#RT_Memory) schreibt! obwohl der Funktions-Rückgabewert #RT_Result > 0.0 signalisiert! (die Variable #RT_Memory am InOut hat nach dem Aufruf den selben Wert wie vor dem Aufruf! egal ob die Variable aus TEMP oder aus einem DB ist). Ich will aber Rundungsfehler und Serien-Addierungsfehler vermeiden und deshalb den Rohwert des Systemzeitzählers verwenden.

Egal, hier mal mein Testcode, vielleicht funktioniert der auf einer echten S7-1200 besser und liefert aufschlußreiche Ergebnisse:

Der OB30:
Code:
ORGANIZATION_BLOCK "Cyclic interrupt 2ms"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_TEMP 
      RT_Result : LReal;   [COLOR="#008000"]// [s] Laufzeit seit vorherigem Aufruf von RUNTIME()[/COLOR]
      RT_Memory : LReal;   [COLOR="#008000"]// Stand Systemzeitzähler beim vorherigen Aufruf von RUNTIME()[/COLOR]
      RT_Start : LReal;   [COLOR="#008000"]// Stand Systemzeitzähler beim Start der Zeitmessung[/COLOR]
      RT_T1 : LReal;   [COLOR="#008000"]// Endzeitpunkt[/COLOR]
      nAIRT : Int;   [COLOR="#008000"]// Anzahl aktiver Alarmsperrungen[/COLOR]
   END_VAR
   VAR CONSTANT 
      T104us : LReal := 0.000104167;
   END_VAR

BEGIN
[COLOR="#008000"]	//CPU 1215C DC/DC/DC 6ES7 215-1AG40-0XB0 V4.1
	//OB30: Cyclic interrupt 2ms
	//Priorität: 7 (nicht änderbar?!)
	//zunächst Machbarkeits-Test, Reset der Statistik mit %I0.0 in OB1[/COLOR]
	
	#nAIRT:=DIS_AIRT(); [COLOR="#008000"]//höherpriore Alarm- und Fehler-OB verzögern[/COLOR]
	
	[COLOR="#008000"]//*** Wie lange dauert nur der Aufruf von RUNTIME()? ***[/COLOR]
	#RT_Memory := 0.0;
	#RT_Result := RUNTIME(#RT_Memory); [COLOR="#008000"]//liefert den Stand des Systemzeitzählers = Anfangszeitpunkt[/COLOR]
	"DB30".RUNTIME_Now := #RT_Memory;  [COLOR="#008000"]//für beobachten[/COLOR]
	
	[COLOR="#008000"]//Wie lange dauert nur der Aufruf von RUNTIME()? (keine Anweisungen dazwischen)[/COLOR]
	#RT_Result := RUNTIME(#RT_Memory); [COLOR="#008000"]//liefert den Stand des Systemzeitzählers seit vorherigem RUNTIME()-Aufruf = Laufzeit in Sekunden[/COLOR]
	"DB30".RUNTIME_Last := #RT_Result; [COLOR="#008000"]//für beobachten[/COLOR]
	
	[COLOR="#008000"]//Statistik: Wie sehr schwankt die Laufzeit des RUNTIME()-Aufrufs?[/COLOR]
	IF #RT_Result > 0.0 THEN [COLOR="#008000"]//wenn kein Überlauf Systemzeitzähler[/COLOR]
	    IF #RT_Result < "DB30".RUNTIME_Min THEN
	      "DB30".RUNTIME_Min := #RT_Result;
	    ELSIF #RT_Result > "DB30".RUNTIME_Max THEN
	      "DB30".RUNTIME_Max := #RT_Result;
	    END_IF;
	END_IF;
	
	[COLOR="#008000"]//*** Wartezeit 104.167µs ***[/COLOR]
	#RT_Result := RUNTIME(#RT_Memory); [COLOR="#008000"]//aktuellen Stand Systemzeitzähler in #RT_Memory holen[/COLOR]
	#RT_Start  := #RT_Memory;          [COLOR="#008000"]//aktuellen Stand Systemzeitzähler als Startzeitpunkt merken[/COLOR]
	#RT_T1     := #RT_Start + #T104us; [COLOR="#008000"]//Endzeitpunkt: Jetzt + 104µs (Jetzt + 0.000104167)[/COLOR]
	
	IF #RT_T1 > #T104us THEN [COLOR="#008000"]//wenn kein Überlauf Systemzeitzähler[/COLOR]
	    [COLOR="#008000"]//Warteschleife[/COLOR]
	    REPEAT
	        #RT_Result := RUNTIME(#RT_Memory);
	    UNTIL #RT_Memory > #RT_T1 END_REPEAT; [COLOR="#008000"]//bis Systemzeitzähler_Jetzt > Endzeitpunkt[/COLOR]
	   [COLOR="#008000"] //PLCSIM: RUNTIME schreibt öfter nicht in den INOUT! Wert_Out = Wert_In doch Result > 0.0 !!![/COLOR]
	    
	    [COLOR="#008000"]//Statistik: Wie genau/schwankend wurden die 104µs getroffen?[/COLOR]
	    #RT_Result := #RT_Memory - #RT_Start; [COLOR="#008000"]//Dauer := Systemzeitzähler_Jetzt - Startzeitpunkt[/COLOR]
	    IF #RT_Result < "DB30".RT_104_Min THEN
	        "DB30".RT_104_Min := #RT_Result;
	    ELSIF #RT_Result > "DB30".RT_104_Max THEN
	        "DB30".RT_104_Max := #RT_Result;
	    END_IF;
	END_IF;
	
	#nAIRT := EN_AIRT(); [COLOR="#008000"]//Alarm- und Fehler-OB wieder freigeben[/COLOR]
	
END_ORGANIZATION_BLOCK

Der DB30 zum Beobachten der Statistikdaten:
Code:
DATA_BLOCK "DB30"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
NON_RETAIN
   VAR 
      RUNTIME_Now : LReal;
      RUNTIME_Last : LReal;
      RUNTIME_Min : LReal;
      RUNTIME_Max : LReal;
      RT_104_Min : LReal;
      RT_104_Max : LReal;
      RUNTIME_Min_us : DInt;
      RUNTIME_Max_us : DInt;
      RT_104_Min_us : DInt;
      RT_104_Max_us : DInt;
   END_VAR

BEGIN
   RUNTIME_Now := 0.0;
   RUNTIME_Last := 0.0;
   RUNTIME_Min := 1.0;
   RUNTIME_Max := 0.0;
   RT_104_Min := 1.0;
   RT_104_Max := 0.0;
   RUNTIME_Min_us := 0;
   RUNTIME_Max_us := 0;
   RT_104_Min_us := 0;
   RT_104_Max_us := 0;

END_DATA_BLOCK

Im OB1 (KOP) habe ich dann noch ein paar MOVE drin, die bei %I0.0 = 1 die Statisik neu initialisieren:
Code:
                    +--------+                            +--------+
   %I0.0            |  MOVE  |                            |  MOVE  |
----| |-------------|EN      |----------------------------|EN      |
               1.0--|IN  OUT1|--"DB30".RUNTIME_Min   0.0--|IN  OUT1|--"DB30".RUNTIME_Max
                    |    OUT2|--"DB30".RT_104_Min         |    OUT2|--"DB30".RT_104_Max
                    +--------+                            +--------+
und 4 MUL (LReal), welche mir die LReal in besser lesbare DInt wandeln:
Code:
                    +------------+
"AlwaysTRUE"        | MUL(LReal) |
----| |-------------|EN          |------------------------- ...
"DB30".RUNTIME_Min--|IN1         |
         1000000.0--|IN2      OUT|--"DB30".RUNTIME_Min_us
                    +------------+

das gleiche für
"DB30".RUNTIME_Max --> "DB30".RUNTIME_Max_us
"DB30".RT_104_Min  --> "DB30".RT_104_Min_us
"DB30".RT_104_Max  --> "DB30".RT_104_Max_us


Ein paar Gedanken noch:
- Eigenartig finde ich, daß ich bei meinem TIA die Priorität meines Weckalarms OB30 nicht ändern kann (Prio ist gesperrt auf 7). Das sollte doch möglich sein - oder?
- Wie oder in welchem OB muß der Code ausgeführt werden, damit er Priorität > 15 hat?
- Mir scheint so, daß das Betriebssystem den Weckalarm für verschiedene Aufgaben unterbricht - nicht daß das Betriebssystem der S7-1200 womöglich den Aufruf von RUNTIME() nutzt, um dem Prozess eventuell eine niedrigere Priorität zu geben und klammheimlich Unterbrechungen durchzuführen?
- Welche Auflösung haben Timer? Die Verwendung von Timern sollte mal getestet werden.
- die Code-Laufzeit in der Warteschleife muß möglichst kurz sein, sie bestimmt, wie genau die Bitzeit 104µs eingehalten werden kann

Harald
 
Für S7-1200-CPUs ab Firmwarestand V4.0 gilt:.

Wenn Sie die OBs als nicht unterbrechbar parametrieren, dann wird ein OB stets zu Ende bearbeitet, und zwar auch dann, wenn während seiner Laufzeit ein Ereignis höherer Priorität auftritt. Das bedeutet im Einzelnen:

Jeder OB mit einer Priorität >= 2 unterbricht das zyklische Programm.

Ein OB der Priorität 2 bis 25 kann von keinem Ereignis unterbrochen werden. Das gilt auch dann, wenn ein Ereignis mit höherer Priorität als der des momentan aktiven OB auftritt, also auch für einen Zeitfehler. Diese Ereignisse werden zu einem späteren Zeitpunkt bearbeitet.

Priorität 24 höher ging nicht einstellen :

EDIT:
Bild_2 ohne TIA Online, kein Netzwerkkabel an SPS


Bild_3 mit TIA Beobachtung
 

Anhänge

  • SW_UART_B2.JPG
    SW_UART_B2.JPG
    87,6 KB · Aufrufe: 27
  • SW_UART_B3.JPG
    SW_UART_B3.JPG
    96,1 KB · Aufrufe: 28
  • SW_UART_TIA_B1.JPG
    SW_UART_TIA_B1.JPG
    54,6 KB · Aufrufe: 30
  • SW_UART_TIA_B2.JPG
    SW_UART_TIA_B2.JPG
    125,8 KB · Aufrufe: 30
Zuletzt bearbeitet:
Ahh, jetzt kann ich ebenfalls die Priorität des zyklischen Alarms einstellen. Gestern war die bei mir immer ausgegraut und fest auf 7. :confused:

Daß man einstellen kann daß OBs nicht von anderen Ereignissen unterbrochen werden, ist eine interessante Sache.
Allerdings schreibt Siemens nicht explizit, ob OBs durch das Betriebssystem z.B. für Kommunikation unterbrochen werden oder ob beim Aufruf von Betriebssystem-Funktionen (z.B. RUNTIME()) noch andere Sachen eingeschoben werden können. Die Meßergebnisse des Testprogramms zeigen jedenfalls, daß sich die Zeiten bei "online Beobachten" um bis zu 30µs verlängen. (durch zusätzlichen Diagnosecode oder durch die Kommunikation?)
In der "Vergleichsliste Programmiersprachen" schreibt Siemens auf Seite 3, daß bei OB mit Priorität > 15 ein "online beobachten" die Laufzeit nicht verfälscht - was offensichtlich nicht ganz stimmt.

Die Daten aus Deinen Bildern (bei OB-Priorität 24):
Code:
                                      ohne Beobachten | mit Beobachten
--------------------------------------------------------------------------------
Dauer Aufruf RUNTIME()     :  18 - 44  : 26 µs Jitter |  18 - 83  : 65 µs Jitter
Genauigkeit Schleife 104µs : 104 - 134 : 30 µs Jitter | 104 - 156 : 52 µs Jitter

Der Aufruf von nur RUNTIME() und einer Zuweisung dazwischen schwankt von 18 bis 44 µs oder gar 83 µs !!
Das ist sehr hart an der Grenze des Nötigen und zuviel bei gleichzeitig online Beobachten.

Die Funktion RUNTIME() ist zwar unnötig umständlich implementiert (rechnet Systemzeitzähler in LReal um!), doch wodurch kann die Ausführungszeit sooo stark schwanken (mehr als 100% der kürzesten Ausführungszeit)? Nach den technischen Daten der aktuellen S7-1200 schätze ich eine Befehlsausführungszeit von ca. 2 bis 5 Anweisungen/µs.

Wenn man die Bitzeit-Schaltzeitpunkte so wie in meinem Konzept erzeugt, dann addieren sich Abweichungen nicht. Nach einem z.B. 30µs zu langen Bit ist dann ein 30µs zu kurzes Bit zu erwarten. Das ist aber trotzdem wirklich hart an der Grenze des Nötigen. Gibt es noch eine andere Möglichkeit zur Abmessung der 104µs? (normale IEC-Zeiten können nur Vielfache von 1ms)

Harald
 
Allerdings schreibt Siemens nicht explizit, ob OBs durch das Betriebssystem z.B. für Kommunikation unterbrochen werden oder ob beim Aufruf von Betriebssystem-Funktionen (z.B. RUNTIME()) noch andere Sachen eingeschoben werden können.

Genau das ist der springende Punkt …….
 
Gibt es noch eine andere Möglichkeit zur Abmessung der 104µs?
Vielleicht kann man die PWM-Ausgabe auf eine Ausgabefrequenz von knapp 100kHz (10.4µs) programmieren und die Ausgabefrequenz mitzählen oder den PWM-Ausgang auf einen schnellen Zähleingang verbinden und den Zählerstand mit Peripheriezugriffen auslesen. Dann wäre man unabhängig von RUNTIME() doppelt so genau, doch da bliebe auch noch das Problem mit den ominösen Unterbrechungen des OB30.

Oder man bastelt ein externes Schieberegister oder D-Flipflop, was durch eine PWM von 104µs getaktet wird.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,
jetzt kann ich zu Hardware der A232 Box etwas sagen.
Ich habe sie hier vorliegen.

Es ist so wie ich schon vermuret habe:
Die Box macht die Pegelanpassung von
RS232 (-/+12V) auf (0/24V)
ES ist ein Spannungsregler drinnen der aus 24V die 5V Versorgungsspannung für eine
MAX3221 macht. Dieser Max wiederum macht der die Pegelanpassung.
Also in der Box wir nichts am Protokoll geändert.
Der Sensor würde also die 0/24V aus der SPS verstehen.


Zum Software UART:
Bei der Unterbrechung die alle ca. 0,75ms kommt ist mir folgendes Aufgefallen
Die kommt wohl immer genau 0,75 nach dem Starrten des OB30 und dann wieder nach 0,75
Wenn man jetzt auf 19200 Baud gehen würde wäre man doch schon vor dieser
Unterbrechung mit einem Zeichen durch …..
 
Wie lang ist die Unterbrechung eigentlich?
Passiert/stört die Unterbrechung auch, wenn Du als Aufrufintervall 3ms wählst, und/oder mit 1ms Phasenverschiebung?

Harald
 
Das zurzeit mir zur Messung zur Verfügung stehende Equipment ist
für die Aufgabe nicht so ganz geeignet, deshalb bleibt eine Restunbekannte.
Wie lang ist die Unterbrechung eigentlich?
Ca. etwas weniger als ein 1/3 Bit (bei 9600) also so 30us
Sie ist aber nicht immer bei jeden durchlauf des OB30 , wenn sie aber da ist dann nach ca.0,75ms
Passiert/stört die Unterbrechung auch, wenn Du als Aufrufintervall 3ms wählst, und/oder mit 1ms Phasenverschiebung?
Harald
Nein ich keine entscheidende Verbesserung festgestellt. Bei eine Auflösung der Phasenverschiebung von 1ms
auch nicht so der Brüller.
Beste Ergebnisse habe ich im Moment tatsächlich mit 19200 Baud.
Da kommen auch schon mal 10 Byte hintereinander korrekt an , dann wieder Aussetzer.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
30µs länger sollte der Empfänger aber noch abkönnen. Das nächste Bit müsste dann auch um die 30µs kürzer sein. Du kannst mal das Startbit testweise 10µs kürzer oder länger machen.

Harald
 
Du kannst mal das Startbit testweise 10µs kürzer oder länger machen.
Ich habe schon fast alles Probiert ………


Ich verwende folgendes Testverfahren:
Ich sende eine 191 diese hat folgende Bit Muster:
Code:
_             _
  _ _ _ _ _ _   _ _   191

S 0 1 2 3 4 5 6 7 S

Bit xyz.X6 ist mein Indikator

Erhalte ich im Ziel keine 191 sondern eine:
223- - > Timing viel zu schnell
Code:
_          _
 _ _ _ _ _  _ _ _   223
159-- > Timing zu schnell
Code:
_          _ _
 _ _ _ _ _    _ _   159
63-- > Timing zu langsam
Code:
_            _ _
 _ _ _ _ _ _    _   63
]
127--- > Timing viel zu langsam
Code:
_              _
 _ _ _ _ _ _ _   _    127
]

190 -- > Startbit zu lange
Code:
_ _          _
   _ _ _ _ _  _ _   190



191 schaffe ich bei 19200 fast fehlerfrei
Sende ich aber eine 85
Code:
_  _  _  _  _
  _  _  _  _  _   85

gibt es durcheinander,
das Jitter‘n ist zu groß

Gibt es keinen besseren weg direkt an die Runtime Variable zu kommen ?
 
Zuletzt bearbeitet:
Um auszuschließen dass der Wert von Runtime wackelt, würde ich mal probieren, eine entsprechende Anzahl an Anweisungen zwischen dem Zugriff des PAW einzufügen, die dann die Zeitverzögerung darstellen. Beispielsweise ein Merkerwort um 1 erhöhen, und dann entsprechend oft hintereinander einfügen. Zugriffe auf Merkerwörter werden soweit ich das geprüft habe wohl nicht optimiert.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Um auszuschließen dass der Wert von Runtime wackelt, würde ich mal probieren, eine entsprechende Anzahl an Anweisungen zwischen dem Zugriff des PAW einzufügen, die dann die Zeitverzögerung darstellen. Beispielsweise ein Merkerwort um 1 erhöhen.......

So, war mein allererster Ansatz noch ohne Runtime . Jittert genauso bzw. Etwas schlimmer ...

Eine Optimierung vom Tia findet aber doch dann nur einmal vorm übertragen statt ,und nicht mehr zur Laufzeit oder ?
 
Zuletzt bearbeitet:
Gibt es keinen besseren weg direkt an die Runtime Variable zu kommen ?
Vermutlich nicht. Du könntest aber einen eigenen RUNTIME-Zähler erzeugen wie ich in #48 schrieb. Ich kenne die S7-1200 nicht genau genug, doch es muß möglich sein, für einen PWM-Ausgang eine Ausgabefrequenz zu programmieren - die wird dann freilaufend in Hardware erzeugt. Vielleicht kann man den Peripherie-Ausgang abfragen und mitzählen, notfalls kann man den aber mit einem schnellen Zähleingang verbinden und den Zählerstand oder den Eingang abfragen. Eine eventuelle Unterbrechung des OB30 kann man dadurch aber nicht verhindern - vielleicht kommt die Schwankung aber auch nur dann wenn man RUNTIME() aufruft?

Harald
 
Ich bewundere Eure Hartnäckigkeit.
Da hätte ich schon längst ein PIC-gesteuertes Modul in's Auge gefasst, welches über einen DO getriggert wird.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich bewundere Eure Hartnäckigkeit.
Ich will es jetzt einfach wissen …..
Da hätte ich schon längst ein PIC-gesteuertes Modul in's Auge gefasst, welches über einen DO getriggert wird.
Mit einen 8 Bit‘er PIC oder AVR kann das ja jeder …. zu einfach ….
Nein die Problemstellung vom Anfang war ja „ohne vor Ort zu müsse“.
Es soll alles über die Fernwartung lösbar sein.
 
Man könnte auch einfach eine RS232-Schnittstelle an die S7-1200 anbauen:
6ES7241-1AH32-0XB0 Kommunikationsmodul CM 1241 RS232 ca. 115,-
oder mit etwas Bastelfaktor
6ES7241-1CH30-1XB0 Kommunikationsboard CB 1241 RS485 ca. 75,-

Doch das kann ja jeder. Andererseits: was nicht eingebaut ist kann auch nicht kaputt gehen. Hesse will ja auch noch die Adapterbox A232 einsparen. Und falls die Lösung für mehrere Anlagen ist, dann könnte sich der Entwicklungsaufwand vielleicht auch rechnen. Desweiteren schadet es sicher nicht für den eigenen Skill, mal etwas tiefer in die SPS-Programmierung reinzuschauen, Lösungen und Grenzen zu finden und auch mal die Dokumentationsaussagen von Siemens unter die Lupe zu nehmen.

Also ich finde das Projekt schon interessant, man kann dabei viel lernen. Ich habe früher auch mal eine Softwarelösung geschrieben, um das Display von Frequenzumrichtern am USS für Parametereingaben für die SPS zu benutzen. Heutzutage wird einfach ein vollgrafisches Display angebaut und eine Klicki-Oberfläche gemalt ;)

Harald
 
Zurück
Oben