# Zusammenspiel K-Bus Zyklen, Tasks und 750-652 / 750-650



## kupferdings (19 Oktober 2020)

Hallo zusammen!

Ich bin schon lange ein stiller Mitleser und konnte vieles dabei lernen und auch schon umsetzen. Jetzt bin ich an einem Punkt angekommen, wo ich leider nicht mehr weiterkomme und versuche es deshalb mal auf diesem Weg. 
Vorab: Falls der Controller an dieser Stelle wichtig ist: Ich verwende 750-881 (Produktiv) und 750-830 (Entwicklungsumgebung)

Ich habe mit ST und unter Verwendung einer 750-650 und eines RS2323 zu RS485 Wandlers ein proprietaeres, aber ausreichend gut reverse-engineered und dokumentiertes Protokoll implementiert. Es handelt sich dabei um das Homematic wired Protokoll und ich steuere darueber meine Rolladenaktoren.

Auf der Steuerung laufen 2 Tasks mit jeweils einem Programm

Main, zyklisch 10ms 
Serial, zyklisch 1ms 

Im Programm Serial wird die 750-650 initialisiert und danach nur der Baustein vom Typ SERIAL_INTERFACE aus der Wago Lib "Serial_Interface_01" aufgerufen. Der Ringbuffer fuer die empfangenen Daten und der Buffer zum Senden befinden sich gemeinsam in einer Datenstruktur, deren einzelne Komponenten SERIAL_INTERFACE mitgegeben werden.

Im Programm Main befindet sich eine Statemachine, die anhand eines eigenen Zaehlers auf den Ringbuffer die neu empfangenen Daten durchlaeuft, anhand der Statemachine die Nachrichten zusammenbaut und in einem eigenen dafuer vorgesehenem Datentyp speichert. Die Statemachine sucht z.B. dann initial nach einem Startbyte, dann einer Source- und Destinationadress, Befehlen, Pruefsumme etc. entsprechend dem Protokoll und ist dabei so aufgebaut, dass sie die Nachrichten ueber mehrere Zyklen aufbaut, da mit der 750-650 pro K-Bus Zyklus ja nur 3 Bytes in den Empfangspuffer uebertragen werden koennen. Eine typische Nachricht ist zwischen 10 und 20 Bytes gross.
Im Anschluss werden ebenfalls in Main vollstaendig empfangene Nachrichten ausgewertet und ggf. Nachrichten zum Versenden generiert und in den Sendebuffer abgelegt und fuer den SERIAL_INTERFACE Baustein im Serial Programm das xStartSend Flag gesetzt.

Soweit funktioniert das auch alles zuverlaessig, allerdings ist die Last auf dem Controller aufgrund der 1ms Zykluszeit von Serial sehr hoch. Eine geringere Zykluszeit funktioniert nicht, da der Sendebuffer auf der 750-650 mit 16 Byte zu klein ist, um komplette Nachrichten aufzunehmen und im ContinousSend zu versenden. Ist die Zykluszeit kleiner, ergeben sich zu grosse Luecken beim Versenden, so dass die Empfaenger die Nachrichten verwerfen oder nicht erkennen. Mit 1ms Zykluszeit bekomme ich nach ca. 60 bis 70ms die Antwort in Main, gemessen von der Ablage der ausgehenden Nachricht um Sendebuffer bis zum vollstaendigen Empfang einer Nachricht vom Aktor. 

Um nun die Last zu verringern habe ich eine 750-652 erworben, da diese zum einen einen wesentlich groesseren Sende- und Empfangspuffer als auch nicht nur den Austausch von 3 Byte sondern bis zu ca. 40 Byte pro K-Bus Zyklus ermoeglicht. Meine Idee war, dass dann die Zykluszeit des Tasks Serial deutlich reduziert werden kann, da Nachrichten nicht mehr haeppchenweise von der Klemme in den Buffer (oder umgekehrt) transportiert werden sondern dafuer ein Zyklus (sowohl Task als auch K-Bus) ausreicht.
Leider funktioniert das so in der Praxis bei mir leider gar nicht. Die 750-652 ist auf 48 Byte Prozessdaten mit I/O Check konfiguriert und auch in der Steuerungskonfiguration so angelegt. Wenn ich nun Serial weiter mit 1ms Zykluszeit laufen lasse funktioniert alles genauso wie mit der 750-650 und eine Antwort ist auch in der gleichen Zeit da. Wenn ich die Zykluszeit auf 5ms erhoehe, ist eine deutliche Erhoehung der Antwortzeit zu beobachten und es braucht haeufig mehrere Versuche bis zum Empfang einer Antwort (Nachricht wird nach 100ms ohne Antwort erneut gesendet). Bei 10ms Zykluszeit braucht es praktisch immer mehr als einen Sendeversuch und die Antwortzeit liegt bei >200ms.

Hab ich da grundsaetzlich was falsch verstanden oder voellig falsch umgesetzt? Ich bin etwas ratlos, wie ich mich einer Loesung naehern kann, also wie ich z.B. debugge oder ein moegliches Problem weiter eingrenzen kann.
Hat jemand vielleicht Erfahrung damit, wie sich das mit der Zykluszeit des K-Bus verhaelt oder wieso entweder der K-Bus die Prozessbreite nicht voll ausnutzt oder seltener ausgefuehrt wird? Gibt es vielleicht in der Verwendung des Baustein SERIAL_INTERFACE aus der Wago Lib "Serial_Interface_01" etwas zu beachten oder anders zu benutzen?


Viele, aber etwas ratlose Gruesse,


EDIT: Gibt es einen Trick, wie man einen Beitrag mit Umlauten posten kann? In der Vorschau wurden diese und andere "spezielle" Zeichen wie ein scharfes "S" durch unlesbare Zeichen ersetzt.


----------



## KLM (19 Oktober 2020)

Moin.
Der KBus ist in CS2.3 synchron zum schnellsten applikativen Task mit KBus zugriff, läuft aber mehrfach pro applikativem Task. Ist nicht gut dokumentiert, aber ich glaube schneller, als 10ms läuft der KBus nicht. In e!C lässt sich das besser konfigurieren. In CS2.3 siehst Du den KBus Task aber zumindest im PLC-Browser mit dem tsk Befehl.
Dort siehst Du auch, die tatsächliche Abarbeitungszeit Deiner applikativen Tasks. Zyklisch 1ms für einen 88x Controller ist ein echter Killer für die Performance und die reale Abarbetungszeit kann der tsk Befehl nur mit einer ms auflösen.
Für mich klingt das nach Problemen, die sich daraus ergeben, dass Du Senden/Empfangen und Protokollimplementierung separaten Tasks hast. Das heißt auch, dass Protokoll und Rx/Tx asynchron laufen aber auf gleiche Speicherbereiche zugreifen. Kann man machen, aber es ist dann eben recht aufwendig die Task stabil einzustellen und Datenkonsistenz sicherzustellen. Ich würde beides in einem Task machen. Der COM FB liefert Dir doch ein Signal, wann eine vollständige Nachricht eingegangen ist und der Puffer ist groß genug diese zu halten. Dann kann Dir auch egal sein, wie viele KBus-Zyklen es braucht, bis die Nachricht rein oder raus ist.

Anm.: Bei mir gehen Umlaute ohne Probleme. äöüÄÖÜß Welchen Browser verwendest Du?


----------



## kupferdings (20 Oktober 2020)

Moin,

das ist ja eine sehr interessante Information mit dem synchronen K-Bus Task. Ich habe im PLC Browser das Kommando "tsk" ausgeführt, bekomme aber leider keine Information zum K-Bus Zyklus sondern nur zu meinen beiden Tasks. Hier ist der Output:

```
tsk
Number of Tasks: 2
Task 0: Main,  ID: 0
   Cycle count: 1550
   Cycletime:       1 ms
   Cycletime (min): 1 ms
   Cycletime (max): 34 ms
   Cycletime (avg): 3 ms
   Status: RUN
   Mode:   UNHANDLED
   ----
   Priority:  2
   Intervall: 10 ms
   Event:     NONE
   ----
   Function pointer: 16#00CD1864
   Function index:   74


Task 1: SerialInterface,  ID: 1
   Cycle count: 2805
   Cycletime:       1 ms
   Cycletime (min): 1 ms
   Cycletime (max): 14 ms
   Cycletime (avg): 3 ms
   Status: RUN
   Mode:   UNHANDLED
   ----
   Priority:  1
   Intervall: 5 ms
   Event:     NONE
   ----
   Function pointer: 16#00CD18B8
   Function index:   75
```

Ich verwende sonst nicht den PLC Browser dafür sondern füge dem Projekt die Bibliothek "SysTaskInfo" hinzu, dann gibt es bei der Taskkonfiguration detaillierte Infos, die auch höher als eine Millisekunde auflösen, allerdings auch nichts über den K-Bus verraten.






KLM schrieb:


> Für mich klingt das nach Problemen, die sich daraus ergeben, dass Du  Senden/Empfangen und Protokollimplementierung separaten Tasks hast.


Ich habe testweise beide Tasks auf 10ms gestellt und den Code des Programs Serial mit in Main genommen, so dass es nur noch ein Task mit 10ms Zykluszeit gab. Das Ergebnis hat sich leider nicht verbessert, es ist genau das gleiche.
Ich glaube, dass der Zugriff von zwei Tasks auf die in beiden Tasks gleiche Datenstruktur eher kein Problem ist, da nur ein Task schreibt und somit keine Race Condition entstehen kann.
Vielleicht zur Verdeutlichung oder ggf. Aufklärung eines Missverständnisses von mir der etwas vereinfachte Code zur Erklärung des Prinzips:

```
# Dieser Type stammt aus der Wago Lib Serial_Interface_01
TYPE typRING_BUFFER :
STRUCT
Index : INT;
Data : ARRAY[0..255] OF BYTE;
END_STRUCT
END_TYPE


TYPE COMData :
STRUCT
    ReceiveBuffer : typRing_Buffer;
    SendBuffer : ARRAY[0..255] OF BYTE;
    BytesToSend : BYTE;
    SendActive : BOOL;
    LastReceivedByte : TIME;
END_STRUCT
END_TYPE
```

Das Programm Serial

```
PROGRAM PLC_PRG_Serial

VAR
    COM3 : SERIAL_INTERFACE;
    COM3Init : BOOL := TRUE;
END_VAR

VAR_EXTERNAL
    COM3Data : COMData;
END_VAR


(* Initialiserung *)
...

(* Daten von der Schnittstelle in den Empfangspuffer und vom Sendepuffer in die Schnittstelle transferieren *)
COM3 ( xSTART_SEND    := COM3Data.SendActive,
    iBYTES_TO_SEND    := COM3Data.BytesToSend,
    utRECEIVE_BUFFER    := COM3Data.ReceiveBuffer,
    xINIT                    := COM3Init);
```


Das Programm Main

```
VAR
    readIndex : BYTE := 0;

    MessageReceiveBuffer : ARRAY[0..HM_MESSAGE_RECEIVE_BUFFER_SIZE] OF HM_RawMessage;
    MessageReceiveBufferIndex : BYTE := 0;
    WorkedReceiveBufferIndex : BYTE := 0;

    MessageSendBuffer : ARRAY[0..HM_MESSAGE_SEND_BUFFER_SIZE] OF HM_RawMessage;
    MessageSendBufferIndex : BYTE := 0;
    WorkedSendBufferIndex : BYTE := 0;
END_VAR
VAR_EXTERNAL
    COM3Data : COMData;
END_VAR


WHILE readIndex <> COM3Data.ReceiveBuffer.Index DO
    (* Byte für Byte verarbeiten und anhand der Statemachine die Nachricht zusammensetzen *)
    (* Vollständig empfangene Nachrichten werden in einen Buffer gespeichert *)
    ...

    readIndex := readIndex + 1;
END_WHILE


WHILE WorkedReceiveBufferIndex <> MessageReceiveBufferIndex DO
    (* Hier kommt was zum Verarbeiten einer oder mehrerer vollständig empfangener Nachrichten. *)
    (* Ergeben sich daraus zu versendende Nachrichten, werden diese in einem Buffer gesammelt. *)
    ...
    
    WorkedReceiveBufferIndex := WorkedReceiveBufferIndex + 1;
    IF WorkedReceiveBufferIndex > HM_MESSAGE_RECEIVE_BUFFER_SIZE THEN
        WorkedReceiveBufferIndex := 0;
    END_IF
END_WHILE


IF COM3Data.SendActive = FALSE AND BusIdleTimeReached AND WorkedSendBufferIndex <> MessageSendBufferIndex THEN
    (* Die zu sendende Nachricht in den Sendepuffer von COM3Data schreiben *)
    ...

    COM3Data.BytesToSend := messageLength;
    COM3Data.SendActive = TRUE;
END_IF
```

Das Grundprinzip ist also, dass im Programm Serial in den Empfangspuffer von COM3Data geschrieben wird und in Main dann aus diesem Puffer gelesen wird, wobei innerhalb von Main ein eigener Index geführt wird, der auf das letzte verarbeitete Byte im Empfangspuffer verweist. Selbst wenn nun Main durch Serial unterbrochen wird ist das kein Problem, bei der Fortsetzung von Main wird dann die WHILE-Schleife einfach länger ausgeführt oder wenn es sich ganz blöd überschneidet dann halt erst im nächsten Zyklus weitergemacht.
Beim Senden ist es ähnlich, hier schreibt Main in den Sendepuffer von COM3Data der dann von Serial an den Baustein SERIAL_INTERFACE weitergereicht wird. Wurde alles versendet, wird das SendActive-Flag durch SERIAL_INTERFACE zurückgesetzt und erst dann wird von Main wieder in den Sendepuffer geschrieben.
Nach meinem Verständis dürfte es da eigentlich kein Problem geben, aber ich möchte natürlich nicht ausschließen, dass ich da etwas übersehe, denn ich komme nicht aus der SPS Welt sondern bin eigentlich in der .NET Welt unterwegs.




KLM schrieb:


> Der COM FB liefert Dir doch ein Signal, wann eine vollständige Nachricht  eingegangen ist und der Puffer ist groß genug diese zu halten


Das ist ein Missverständis, denn dafür müsste er das genau Protokoll kennen, sprich auf ISO/OSI Layer 7 arbeiten. Dieser Baustein von Wago arbeitet aber nur ganz Low-Level und abstrahiert soeben den direkten Umgang mit den Registern der Klemmen. Er schreibt empfangene Daten stumpf in einen Puffer und sendet Daten genauso stumpf aus einem Puffer. Das neue Daten empfangen wurden signalisiert auch nicht ein Flag sondern ist nur indirekt über den geänderten Index zu erfahren. Der Baustein arbeitet also eher auf ISO/OSI Layer 2.


Hast du vielleicht noch weitere Ideen, was ich ausprobieren oder im Code umstellen könnte? Ich bin dankbar für jede Hilfe! 
Vielleicht nutzt der Wago Baustein die zur Verfügung stehenden Bytes im Prozessabbild nicht vollständig aus bei Verwendung der 750-652? 

Viele Grüße,


P.S.: Nun scheinen Umlaute und Co. auch wieder zu funktionieren, keine Ahnung was da los war.


----------



## PN/DP (20 Oktober 2020)

Das mit den verhuntzten Umlauten ist ein schon laaange vorhandenes Verhalten des Forums, wenn man während Beitrag schreiben durch Timeout ausgeloggt wird und wieder einloggen muß. Workaround: beim Anmelden den Haken "Angemeldet bleiben" aktivieren, oder: den fertigen Beitrag komplett markieren (Strg+A), in die Zwischenablage kopieren (Strg+C), auf Vorschau gehen, wieder anmelden, den kaputten Text komplett löschen und aus der Zwischenablage wieder einfügen (Strg+V).

Harald


----------



## ClMak (20 Oktober 2020)

Hallo,

welche zeitlichen Anforderungen stellt das von dir verwendete Protokoll? Wie schnell müssen Antworten gesendet werden und wie groß dürfen Lücken im Frame des Telegramms sein?
Wie lang sind die zu sendenden Telegramme. Hast du den Eingang "cfFLOW_CONTROL" des COM Baustein auf den Wert HALFDUPLEX (=4) initialisiert?

Ich würde das Programm und der COM Baustein in einer Task aufrufen und nicht in zwei getrennten (hast du ja aber schon ausprobiert...)

VG
ClMak


----------



## KLM (20 Oktober 2020)

Moin,
da habe ich mich geirrt. Der Task, denn man in CS2.3 nicht konfigurieren kann aber im PLC-Browser sieht ist der Task für die Visu, nicht der für den KBus. Den kannst Du nicht einsehen auf einem 88x Controller.
Dein Test bezog sich noch immer auf zwei Tasks? Oder lief der COM FB nur in einem anderen Programm, aber sequenziell zum Protokoll Programm?
Von Bool'scher Signal war keine Rede, aber der Index kann ja mehr oder weniger als solches verwendet werden. Ein Signal für ein vollständig eingegangenes Protokoll-Telegramm kann der FB nicht liefern, da er Dein Protokoll ja nicht implementiert hat. Das ist ja das, was Du mit dem Protokoll Programm machst.
Wenn ich den KBus richtig verstanden habe, was wohl dank fehlender Dokumentation sicherlich niemand wirklich hat, schiebt der KBus Task die 48 Byte der Klemme in mehreren KBus Zyklen aber innerhalb eines applikativen Zykluses raus. Letzterer ist der schnellste applikative mit KBus Zugriff, also bei Dir der mit dem COM FB.
Wenn sich die beiden Task nicht gegenseitig behindern, was sie nicht tun sollten, wenn Du beide Programme in einem Task hast, dann ich möglicherweise das Protokoll selbst die Ursache. Soll heißen, ggf. müssen ausreichend Pausen auf dem Bus sein für eine Antwort auf Deine Telegramme. Du bist also ggf. nicht zu langsam, sondern zu schnell.
Ich schließe mich daher der Frage von ClMak an, was sind die Anforderungen des Protokolls?
Anm.: Wenn Du ein Oszi zur Hand hast, würdest Du im Detail sehen was da abgeht.


----------



## kupferdings (20 Oktober 2020)

Hallo,

die Initialisierung sieht so aus:

```
COM3 ( bCOM_PORT_NR   := 2,
cbBAUDRATE            := BAUD_19200,
cbsBYTESIZE           := BS_8,
cpPARITY              := PARITY_EVEN,
csSTOPBITS            := STOPBITS_1,
cfFLOW_CONTROL        := HALFDUPLEX,
utRECEIVE_BUFFER      := COM3Data.ReceiveBuffer,
ptSEND_BUFFER         := ADR(COM3Data.SendBuffer),
xINIT                 := COM3Init,
xOPEN_COM_PORT        := TRUE,
xSTART_SEND           := COM3Data.SendActive);
```

Wenn ich die Doku richtig verstehe, ist damit der Einstellung HALFDUPLEX continous send aktiviert, d.h. eigentlich müsste die zu sendende Nachricht aus dem Sendepuffer zuerst komplett in den Buffer der 750-652 übertragen werden und dann komplett in einem Stück auf den Bus gesendet werden. Weiter gedacht müsste bei dem konfigurierten 48 Byte Prozessabbild ein Sendepuffer von 20 Bytes in einem K-Bus Zyklus zur Klemme übertragen werden. Umgekehrt müsste eine Antwort, die entweder kleiner oder max. etwa ähnlich groß ist, ebenfalls in einem "Rutsch" in den Empfangspuffer des Programms übertragen, max. 2 Zyklen wenn sich der Empfang der Nachricht mit einem K-Bus Zyklus überschneidet.
Liege ich mit der Annahme richtig?
Praktisch scheint das nicht irgendwie nicht der Fall zu sein.

Das Timing des Protokolls sieht folgendes vor:

Der Abstand zwischen zwei direkt hintereinander gesendeten Nachrichten beträgt ca. 7,5ms
Wenn der Bus besetzt ist, erfolgt eine zufällige Wartezeit zwischen 5ms und 20ms
Im Discovery Mode (zum Finden von Geräten am Bus) beträgt die Wartezeit auf eine Antwort 8ms
Nach dem Senden einer Nachricht wartet der Sender bis 100ms auf eine Antwort, danach wird die Nachricht wiederholt.

Den Discovery Mode habe ich nicht implementiert, da ich ihn nicht brauche. Es ist eine feste Installation und die Adressen der Aktoren sind bekannt. 

Grundsätzlich ist die Verarbeitung von Nachrichten nicht so zeitkritisch, das kontinuierliche Senden wiederum schon. Deshalb auch die ursprüngliche Aufteilung in zwei Tasks, als ich mit der 750-650 begonnen habe, die ja nur 16Byte Sendepuffer hat und pro K-Bus Zyklus nur 3 Bytes erhalten kann. Mit der 750-652 sollte das ja in jeder Hinsicht besser sein, nur übersehe ich scheinbar irgendwas oder habe etwas grundsätzlich falsch verstanden 

Viele Grüße,


----------



## kupferdings (20 Oktober 2020)

Hallo nochmal,

da hat sich meine Antwort mit dem Beitrag von KLM überschnitten.

Ok, danke für die Erklärung mit den Tasks, das beruhigt mich etwas 



KLM schrieb:


> Dein Test bezog sich noch immer auf zwei Tasks? Oder lief der COM FB nur  in einem anderen Programm, aber sequenziell zum Protokoll Programm?


Mein Test lief komplett in einem Programm in einem Task. Ich habe den Code aus dem Programm Serial einfach an den Anfang von Main kopiert und den Task von Serial dann entfernt.



KLM schrieb:


> Von Bool'scher Signal war keine Rede, aber der Index kann ja mehr oder  weniger als solches verwendet werden. Ein Signal für ein vollständig  eingegangenes Protokoll-Telegramm kann der FB nicht liefern, da er Dein  Protokoll ja nicht implementiert hat. Das ist ja das, was Du mit dem  Protokoll Programm machst.



Kannst du das etwas näher erläutern? Ich glaube, ich verstehe den Ansatz nicht. Die Nachrichten / Telegramme sind in der Länge variabel, daher ist mir nicht klar, wie ich den Index da gewinnbringend nutzen kann außer in der Art, wie ich das momentan mache (siehe oben). Sorry, wenn das vielleicht eine dämliche Frage ist.



KLM schrieb:


> Wenn ich den KBus richtig verstanden habe, was wohl dank fehlender  Dokumentation sicherlich niemand wirklich hat, schiebt der KBus Task die  48 Byte der Klemme in mehreren KBus Zyklen aber innerhalb eines  applikativen Zykluses raus. Letzterer ist der schnellste applikative mit  KBus Zugriff, also bei Dir der mit dem COM FB.



Das wäre natürlich der Knaller, wenn dem so wäre. Dann wäre in der Konsequenz das größere Prozessabbild möglicherweise völlig nutzlos?




KLM schrieb:


> Soll heißen, ggf.  müssen ausreichend Pausen auf dem Bus sein für eine Antwort auf Deine  Telegramme. Du bist also ggf. nicht zu langsam, sondern zu schnell.



Es wird pro Programmzyklus nur eine Nachricht aus der Sende-Queue in den Sendepuffer geschrieben. Das heißt die Pause zwischen zwei Nachrichten kann nach meinem Verständnis nicht kürzer als 10ms sein, oder? Die nächste Nachricht wird erst dann gesendet, wenn eine Antwort (entweder die angefragten Daten oder ein leeres ACK) empfangen wurde. Alternativ wird nach 100ms ohne Antwort die Nachricht bis zu zweimal wiederholt. 


Oszilloskop muss ich mal schauen, ob ich da was organisiert bekomme.

Viele Grüße,


----------



## ClMak (21 Oktober 2020)

> Das wäre natürlich der Knaller, wenn dem so wäre. Dann wäre in der Konsequenz das größere Prozessabbild möglicherweise völlig nutzlos?



Nein, das ist definitiv nicht so. Das größere Prozessabbild ist in jedem Fall nützlich in Bezug auf Geschwindigkeit, weil pro K-Bus Zyklus die "48 Byte" geschoben werden können und beim älteren Modul nur 5 Byte.



> Soll heißen, ggf. müssen ausreichend Pausen auf dem Bus sein für eine Antwort auf Deine Telegramme. Du bist also ggf. nicht zu langsam, sondern zu schnell.



Vorstellbar ist es schon, dass die Kommunikation verlangsamt werden muss. Bei der Halfduplex RS485 Kommunikation muss ständig zwischen Senden und Empfang umgeschaltet werden. Vielleicht ist der angesprochene Teilnehmer noch nicht wieder auf Empfangsmodus, wenn du bereits ein neues Telegramm sendest.


----------



## KLM (21 Oktober 2020)

Das größere Prozessabbild der Klemme heißt, dass Du das in einem applikativen Zyklus absetzen kannst. Also ist die bessere 652 vorteilhaft.
Wenn ich das richtig in Erinnerung mach der KBus immer 12 Byte pro KBus Zyklus. Das spielt aber keine Rolle, da der ja mehrfach pro applikativen Taskzyklus läuft und die 20 Byte Deiner Telegramme in einem app. Zyklus rausbekommt.
Bei den vorgegebenen Pausenzeiten des Protokolls, sollte Dein Task also auch viel langsamer laufen können.
Für den Index wollte ich Dir eigentlich vom Dienstrechner ein Bsp. kopieren, habe ich aber leider vergessen. Versuche es morgen erneut.



> Vorstellbar ist es schon, dass die Kommunikation verlangsamt werden muss. Bei der Halfduplex RS485 Kommunikation muss ständig zwischen Senden und Empfang umgeschaltet werden. Vielleicht ist der angesprochene Teilnehmer noch nicht wieder auf Empfangsmodus, wenn du bereits ein neues Telegramm sendest.


Gute Begründung.

Soll heißen: Lass beides in einem Task und/oder Programm laufen und erhöh die Zeit Deines Sende-Auslösers. Letzteres meint nicht die Task-Zykluszeit. Die kannst Du sicherlich ebenfalls verlangsamen, aber damit triggerst Du ja nicht das Senden.


----------



## kupferdings (27 Oktober 2020)

Hallo!

danke nochmal für die Ideen und Denkanstöße! 

Ich habe nun wie empfohlen alles in einem Programm und in einem Task mit 20ms Zykluszeit. Das Verhalten hat sich leider keineswegs gebessert, sondern ist dadurch deutlich schlimmer geworden, was aber nach meinem ersten Test am Anfang des Threads ja zu erwarten war. Der Empfang einer Antwort dauert zwischen 150ms und 300ms. Das ist natürlich viel zu viel, daher bin ich weiter auf Ursachenforschung gegangen.
Ich habe nun einen Counter im Programm, so dass ich jeden Zyklus eindeutig identifizieren kann und loge in ein Array, sobald über über die Klemme und den Baustein SERIAL_INTERFACE Daten empfangen wurden. Die gute Erkenntnis ist, dass es tatsächlich Zyklen gibt, in denen viele Daten ankommen, d.h. das große Prozessabbild funktioniert. Die eher schlechte Erkenntnis ist, dass trotz lange nicht in jedem Zyklus Daten ankommen, wenn etwas empfangen wird sondern eine Nachricht teils über drei Zyklen ankommt und diese drei Zyklen auch nicht direkt hintereinander liegen sondern immer 3-4 Zyklen dazwischen sind, in denen nichts passiert.

Ich konnte mir erst keinen Reim darauf machen, bis ich das SendActive Flag mit gelogt habe. Dieses wird vom Programm gesetzt, sobald die Daten aus dem Sendepuffer versendet werden sollen und wird von dem Baustein SERIAL_INTERFACE zurückgesetzt, wenn die Daten versendet wurden. Nach dem Log ist dieses Flag fast immer gesetzt, was eigentlich nicht sein kann oder sein sollte, denn das Versenden von max. 20 Bytes dauert bei einer Baudrate von 19200 nicht 100ms oder 200ms.
Da ist also wahrscheinlich eher noch was in meinem Programm falsch.
Ich bleibe dran und berichte, sobald ich mehr gefunden habe 

Viele Grüße,


----------



## ClMak (27 Oktober 2020)

Hallo,



> IF COM3Data.SendActive = FALSE AND BusIdleTimeReached AND WorkedSendBufferIndex <> MessageSendBufferIndex THEN    (* Die zu sendende Nachricht in den Sendepuffer von COM3Data schreiben *)
> ...
> 
> COM3Data.BytesToSend := messageLength;
> ...


in deinem Code Ausschnitt ist für mich nicht klar erkennbar, wie das SendActive Flag gesetzt wird. Wäre es möglich, dass das Flag länger als einen Programmzyklus bzw. mehrmals nacheinander auf TRUE getriggert wird?

VG


----------



## kupferdings (29 Oktober 2020)

Hallo,

ich habe das versucht zu debuggen und glaube, dass nicht mehrfach getriggert wird. Da mir die Ideen ausgehen, habe ich mich dem Thema jetzt mal ganz anders genähert. Mich wundert weiterhin, dass das Flag so viel gesetzt ist und daher habe ich ein minimales Programm geschrieben, dass einfach nur 20 Bytes sendet und nichts weiter macht außer die Zeit vom Setzen des Flags bis zum zurücknehmen durch SERIAL_INTERFACE zu messen.
Die Ergebnisse sind sehr erstaunlich, denn es scheint, als hätte das Setting "continous send" einen sehr negativen Effekt auf die Dauer eines Sendevorgangs.
Hier die Ergebnisse:
750-652
1ms: 40ms
5ms: 40ms
10ms: 70ms
20ms: 120ms

no continous
1ms:  10ms
5ms:  10ms
10ms: 20ms
20ms: 40ms


750-650
1ms:  50ms
5ms:  50ms
10ms: 90ms
20ms: 160ms

no continous
1ms:  40ms
5ms:  40ms
10ms: 80ms
20ms: 160ms


Die erste Erkenntnis ist, dass eine Zykluszeit kleiner als 5ms keine Änderung oder Verbesserung zur Folge hat.

Die zweite, viel interessantere Erkenntnis ist, dass mit continous send die eigentlich viel bessere Klemme 750-652 nicht wirklich besser als eine 750-650 ist. Eher durch eine zufällige Idee habe ich continous send abgeschaltet musste überrascht feststellen, dass sich die Klemme 750-652 nun so verhält, wie ich das aufgrund des erheblich größeren Prozessabbildes auch erwartet hätte. Die 750-650 kann nicht wirklich schneller werden, weil sie pro Zyklus nur 5Byte bekommen kann.

Jetzt die Preisfrage, kennt das jemand schon so oder ist da bei mir ganz grundsätzlich was faul?
Wie kann das sein, ist da in der seriellen Lib von Wago ein Bug drin oder kann sich das Verhalten jemand herleiten?
Mit contious send sind wirklich viele auf den ersten Blick überflüssige Zyklen nötig.

Viele Grüße,


----------



## kupferdings (2 November 2020)

Hallo,

neben der Testumgebung mit dem Controller 750-830 habe ich jetzt die Zeiten auch nochmal mit dem eingesetzten Produktivcontroller 750-881 ermittelt. Letzterer soll ja etwas leistungsstärker sein, daher hatte ich das für sinnvoll erachtet.

Hier die Ergebnisse, zusammen mit den ermittelten Zeiten aus der vorherigen Beitrag, gemessen vom Setzen des xStart_Send Flags bis zum Zurücksetzen durch den FB SERIAL_INTERFACE


verwendete Busklemme 750-652, 48 Byte Prozessabbild, 20 Byte senden

Zykluszeit750-830
continous send750-881
continous send750-830
no continous send750-881
no continous send1ms40ms32ms10ms9ms5ms40ms35ms10ms10ms10ms70ms40ms20ms10ms, selten 20ms20ms120ms40ms40ms20ms


verwendete Busklemme 750-650, 5 Byte Prozessabbild, 20 Byte senden

Zykluszeit750-830
continous send750-881
continous send750-830
no continous send750-881
no continous send1ms50ms36ms40ms24ms5ms50ms50ms40ms40ms10ms90ms50ms80ms40ms20ms160ms80ms160ms80ms


Es ist gut zu erkennen, dass auf dem Controller 750-881 der K-Bus offensichtlich schneller / öfter arbeitet. Das grundsätzliche Verhalten, dass continous send offensichtlich langsamer ist, bleibt bei beiden Controllern und auch bei beiden Klemmen.

Ich nehme daraus mit, dass mein Task mit 5ms laufen muss und ich die 650-652 ohne continous send verwenden muss, damit ich eine Chance habe, schnell genug Antworten zu senden ohne dass die Aktoren in einen Timeout laufen.

Unsicher bin ich mir jetzt nur bei der Aufteilung auf Programme und Tasks. Mein Bauchgefühl sagt mir, dass in einem Task mit 5ms und hoher Priorität wirklich nur der FB SERIAL_INTERFACE aufgerufen werden sollte und das dann nachgelagerte Auswerten der empfangenen Daten, darauf reagieren und Ereignisse verarbeiten, Antworten generieren, etc. in einem langsameren Task mit niedriger Priorität erfolgen sollte, weil das weniger zeitkritisch ist und auch vom Code / Rechenaufwand höher ist und der Controller dann auch noch seine anderen, eher zeitkritischen Aufgaben erledigen kann.
Alternativ könnte man überlegen, wenn es denn alles in einem Programm sein soll, dort recht viele IF-Verzweigungen einzubauen, damit die zeitintensiven Dinge nicht jedes Mal mit ausgeführt werden und die Last auf dem Controller bzw. die regelmäßige Ausführungszeit des Programms so verringert wird.


Vielleicht besteht noch die Möglichkeit, dass jemand vom Wago Support die oben angeführten Zeiten und Beobachtungen bestätigen kann oder ggf. Verbesserungsvorschläge für die Nutzung der Wago Bibliothek machen kann?


Viele Grüße,


----------



## Thruser (3 November 2020)

Hallo,

da Du eh Deine State Machine hast, hast Du mal überlegt direkt über das Prozessabbild ohne die Bibliothek zu arbeiten? Oder eventuell auch nur mit der sercomm.lib?

Die Kommunikation über das Prozeßabbild ist ja zum gröten Teil sehr ausführlich beschrieben. Nur das eigentliche Senden fehlt irgendwie. Es wird nur gezeigt wie man Daten in den Sendepuffer transferiert, aber nicht wie dann gesendet wird. Ich glaube da ist ein Fehler in der Doku mit dem kontinuierlichem Senden. Einmal kann man daß in der Gerätekonfiguration durch I/O Check einstellen und dann gibt es das Bit. Ich vermute das dieses Bit dazu da ist, daß der Inhalt des Sendepuffers dann über die Schnittstelle versendet wird.

Gruß


----------



## kupferdings (3 November 2020)

Moin,

also darüber nachgedacht habe ich schon, hatte das aber eigentlich wieder verworfen, da ich eigentlich kein SPS Entwickler bin und davon ausgegangen bin, dass solche Low-Level Sachen von Wago wesentlich besser und effizienter gelöst sind, als ich das mit meiner begrenzten Zeit und Erfahrung je tun könnte. Ehrlich gesagt graut es mir ein wenig davor und ich würde es gerne vermeiden, mit dem eigentlichen Protokoll hatte ich schon reichlich zu tun 
Gibt es irgendwo ein fertiges Beispiel in ST, wie man das direkt mit der sercomm.lib macht?

Vielleicht kann ja noch jemand seine Erfahrungswerte und beobachtete Timings beisteuern? Dann gäbe es zumindest einen Anhaltspunkt, ob ich mit meinen Zeiten alleine bin, also was faul ist, ober ob andere auch ähnliche Zeiten beobachten konnten.

Es kann natürlich auch sein, dass es einfach nicht üblich ist, die zeitlichen Anforderungen wie ich sie jetzt mit dem Protokoll habe, so mit einer SPS zu bedienen und das einfach ein exotischer Anwendungsfall ist?
Ich hoffe noch auf eine Äußerung oder Einschätzung von einem der Wago User, die hier so unterwegs sind. 

Viele Grüße,


----------



## Thruser (15 November 2020)

Hallo,

Ich weiß nicht ob Du noch an dem Thema dran bist. Hast Du mal Kontakt zum Support aufgenommen?

Ich habe zumindest endlich mal geschafft meinen 8204 mit 652 aufzubauen und damit zu spielen. Ich kann bestätigen, daß die Einstellung kontinuierliches Senden ziemlichen Einfluß auf Zeit hat. Aber auch die Sendegeschwindigkeit hat Einfluß.

Ich habe hier jetzt ein 8204 mit Codesys 2.3 verwendet. Den 750-652 habe ich auf 48 Bytes Prozessabbild eingestellt. Genutzt habe ich ihn als RS-232 Vollduplex.

Das Programm habe ich normal als PLC_PRG als freilaufenden Prozeß zum Testen genommen. Da wurde automatisch eine Zykluszeit von 10ms genommen.

Hier mal das einfache Programm zum Testen:

```
PROGRAM PLC_PRG
VAR
    iBytesToSend : INT;
    TimeCycleDiff : TIME;
    TimeSendDiff : TIME;
    iCycleCountDiff:INT;

    i                 : INT;

    TimeAct : TIME;
    TimeSendOld: TIME;
    TimeCycleOld : TIME;

    iCycleCount : INT;
    iCycleCountOld:INT;

    iCount2:INT;
    xInitDone:BOOL := FALSE;

    baSendBuffer:ARRAY [0..99] OF BYTE;



(* COM2  ********************************************)
    COM2             : SERIAL_INTERFACE;
    xOpenPort2        : BOOL := TRUE;
    xInitPort2        : BOOL;
    xSendActive2    : BOOL;
       xSendActive2Old : BOOL;
    (*SendString2        : STRING(100) := '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789';*)
    (*SendString2        : STRING := '01234567890123456789012345678901234567890123456789012345678901234567890123456789';*)
    (*SendString2        : STRING := '01234567890123456789';*)
    ReceiveBuffer2    : typRing_Buffer;

    ReceiveBuffer2OldIndex    : INT;
    ReceiveString2     : STRING(255);
    pReceiveStr2      : POINTER TO ARRAY[0..255] OF BYTE;
    help2Idx         : INT;
    iTo2            : INT;

END_VAR
```


```
TimeAct := TIME();
iCycleCount := iCycleCount+1;

IF (NOT xInitDone) THEN
    iBytesToSend := SIZEOF(baSendBuffer);

    FOR i:=0 TO iBytesToSend DO
        baSendBuffer[i] := INT_TO_BYTE (i);
    END_FOR
    xInitDone := TRUE;
END_IF

(* Send and Receive for COM2 *)
COM2(    bCOM_PORT_NR     := 2,
          cbBAUDRATE         := BAUD_9600,
        cbsBYTESIZE        := BS_8,
        cpPARITY        := PARITY_ODD,
        csSTOPBITS        := STOPBITS_1,
        cfFLOW_CONTROL    := NO_FLOW_CONTROL,
        (*cfFLOW_CONTROL    := NCS_NO_FLOW_CONTROL,*)
        utRECEIVE_BUFFER := ReceiveBuffer2,
        ptSEND_BUFFER    := ADR(baSendBuffer),
        xINIT            := xInitPort2,
        xOPEN_COM_PORT     := xOpenPort2,
        iBYTES_TO_SEND    := iBytesToSend,
        xSTART_SEND        := xSendActive2 );


IF (COM2.Com_Port_Ist_Offen) THEN

    IF (xSendActive2 AND NOT xSendActive2Old) THEN
        TimeSendOld := TimeAct;
        iCycleCountOld := iCycleCount;
    END_IF
    IF (NOT xSendActive2 AND  xSendActive2Old) THEN
        TimeSendDiff := TimeAct - TimeSendOld;
        iCycleCountDiff := iCycleCount-iCycleCountOld;
    END_IF
    xSendActive2Old := xSendActive2;

    TimeCycleDiff := TimeAct - TimeCycleOld;
    TimeCycleOld := TimeAct;
    IF (NOT xSendActive2) THEN
        iCount2 := (iCount2+1)MOD 100;
        IF (iCount2 =0) THEN
            xSendActive2 := NOT xSendActive2;
        END_IF
    END_IF
END_IF
```

Da ist jetzt nichts weiter optimiert für die Messung.

Bei 9600 Baud und NO_FLOW_CONTROL (kontinuierliches senden) ergaben sich folgende Werte:

```
Bytes Cycle Time Send Time Send Cycles
      ms         ms        
 10    10         50        5
 20    10         60        6
 30    10         70        7
 40    10         80        8
 51    10        110       11
 60    10        120       12
 70    10        130       13
 80    10        140       14
```
Bei 9600 Baud und NCS_NO_FLOW_CONTROL (kein kontinuierliches senden) ergaben sich folgende Werte:

```
Bytes Cycle Time Send Time Send Cycles
      ms         ms        
 10    10         10        1
 20    10         10        1
 30    10         10        1
 40    10         10        1
 50    10         70        7
 60    10         70        7
 70    10         70        7
 80    10         70        7
 90    10         70        7
100    10        120       12
```
Zumindest die Breite des Prozeßabbild scheint richtig berücksichtigt zu werden. Aber keine Ahnung, warum sich beim kontinuierlichem Senden die Zyklusanzahl pro 10 Bytes um 1 erhöht, plus Sprung wenn die Breite des Prozeßabbilds überschritten wird.

Der Sprung tritt  auch bei nicht kontinuierlichem Senden auf.

Aber auch die Baudrate hat Einfluß. Hier mal für 19200
Bei 19200 Baud und NCS_NO_FLOW_CONTROL (kein kontinuierliches senden) ergaben sich folgende Werte:

```
Bytes Cycle Time Send Time Send Cycles
      ms         ms        
 40    10         10        1
 50    10         40        4
 90    10         40        4
100    10         70        7
```

Da der 8204 auch noch eine eigene serielle Schnittstelle, zusätzlich zur Programmierschnittstelle, besitzt, habe ich auch mal die verwendet
Bei 9600 Baud und NCS_NO_FLOW_CONTROL (kein kontinuierliches senden) ergaben sich folgende Werte:

```
Bytes Cycle Time Send Time Send Cycles
      ms         ms        
 10    10          0        0
 20    10          0        0
 30    10          0        0
 40    10          0        0
 50    10          0        0
100    10          0        0
```
Da wird anscheinend der gesamte Puffer in einem Zyklus übergeben.

Gruß


----------



## Oberchefe (28 November 2020)

Kannst du nicht die Konfigurations/Programmierschnittstelle benutzen? Da müssen die Daten nicht über den K-Bus und es geht deutlich schneller.


----------



## kupferdings (2 Dezember 2020)

Hallo!

Ich bin mit dem Thema noch nicht durch und habe noch eine weitere Erkenntnis, die ich gerne teile.
Offensichtlich braucht die Klemme eine sehr lange Zeit zum Initialisieren. Es gibt ja einmal das Flag xInit, das ist bei 5ms Zykluszeit nach zwei Zyklen wieder auf FALSE und die 750-652 sollte dann bereit sein. Das ist sie theoretisch auch, aber das erste Senden dauert unglaublich lang. Ab dem zweiten Sendevorgang ist dann alles normal und wie erwartet.

Hier mal ein Screenshot aus meinem Log, verwendet wurde ein 750-830 mit der 750-652:




Die Zeit ist die seit dem Programmstart vergangene Zeit und die Zahl ist die Nummer des Zyklus. Da habe ich einfach einen Counter, der bei jedem Zyklus um eins erhöht wird.
Wie zu sehen, habe ich das jetzt so gelöst, dass als Teil einer eigenen Initialisierung ein paar 0-Bytes gesendet werden. Die anderen Busteilnehmer ignorieren dass, solange sie nicht das Start-Byte erhalten.

Was mir jetzt noch Probleme bereitet, ist der hohe Jitter in der Ausführung des Tasks. Teilweise gibt es große Pausen zwischen den Tasks, was dann dazu führt, dass die anderen Busteilnehmer in einen Timeout gehen, weil die Steuerung es nicht schafft, schnell genug eine Antwort zu senden.
Dazu kommt, dass die 15 Bytes der erwarteten Antwort nie in einem Rutsch ankommen, sondern laut Log einmal in Zyklus 36 und der Rest in Zyklus 38. Das heißt, es dauert allein 15ms um 15 Bytes von der Klemme bis ins Programm zu bekommen. Die Daten sind sehr viel schneller in der Klemme bei 19200 Baud aber aus irgendeinem Grund dauert es dann sehr lange, bis ich sie im Programm überhaupt weiterverarbeiten kann.




Thruser schrieb:


> Hast Du mal Kontakt zum Support aufgenommen?


Habe ich bisher nicht, und habe ehrlich gesagt als Privatperson da auch sehr bescheidene Erfahrungen gemacht. Im beruflichen Kontext läuft das, aber privat möchte ich das eigentlich nicht mehr machen. Meine Hoffnung war, dass sich hier einer der zahlreichen Wago-User des Problems annimmt. 




Oberchefe schrieb:


> Kannst du nicht die  Konfigurations/Programmierschnittstelle benutzen? Da müssen die Daten  nicht über den K-Bus und es geht deutlich schneller.


Ich bräuchte auf jeden dann noch einen zusätzlichen Wandler auf RS485. Das geht natürlich, ich bin mir allerdings nicht sicher, ob die Lösung sicher genug ist. Sicher in der Hinsicht, dass die interne Schnittstelle in meinem Fall wohl die Programmierschnittstelle wäre und die ist nach meinem Kenntnisstand nicht gegen Überspannung etc. gesichert. Das selber schaltungstechnisch robust hinzubekommen, traue ich mir nicht zu.
Vielleicht ist aber tatsächlich die einzige Lösung für mein Problem. Noch habe ich die Hoffnung nicht aufgegeben 

Viele Grüße,


----------

