PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Vorgehensweise beim einlesen eines Telegramms variabler Länge



demmy
13.10.2014, 19:37
Hi zusammen,

wie der Titel schon verrät habe ich ein logisches Problem beim einlesen eines Telegrammes variabler Länge. Das Telegramm läuft über eine RS485-Verbindung.

Aktuell ist es so, das ich ein Telegramm mit fixer Länge verwende. Da ich weiß wie lang das Telegramm sein muss, warte ich ab bis die notwendige Anzahl an Bytes im Eingangspuffer stehen und werte die Bytes bzw. das gesamte Telegramm dann auf einmal aus.
Ich prüfe dann ob das Telegramm für diesen Teilnehmer ist, ob die Prüfsumme passt usw. usw..
Ist es nicht für mich oder stimmt irgendwas nicht, wird es einfach verworfen.

Wie mache ich das jetzt aber, wenn ich eine variable Länge habe? Ich weiß ja nicht auf wieviele Bytes ich warten muss bis das Telegramm vollständig empfangen wurde? Lese ich zunächst nur den Telegrammkopf ein und werte diesen aus? Von dem weiß ich ja die Fixlänge. Brauche ich da dann eine eigene Prüfsumme für den Telegrammkopf? Wenn ich in dem Kopf dann schon sehe das es nicht für mich ist, dann muss ich trotzdem die Nutzdaten abwarten und verwerfen?

Aktuell soll das Telegramm so aussehen: (jeweils 1 Byte)

Zieladresse / Quelladresse / Länge der Nutzdaten / Registerzugriff / ( hier kommen jetzt die variablen Nutzdaten) / Prüfsumme

Bisher war es auch so, das ich den Telegrammkopf und die Nutzdaten in die Prüfsumme habe mit einfließen lassen. Geht das dann auch noch oder macht es keinen Sinn?

Ich hoffe Ihr könnt mir weiterhelfen. Ich stehe gerade ein wenig auf dem Schlauch.

peterfido
13.10.2014, 20:10
Bei mir wird jedes Telegramm mit einem CHR(13) abgeschlossen. Bei "empfindlichen" Dingen kommt auch ein Zeichen für den Beginn eines Telegramms.

demmy
13.10.2014, 20:55
Ok,

und wie gehst du vor beim Einlesen deines Telegramms?

Peter(TOO)
14.10.2014, 01:32
Hallo,

Ich habe solche Decoder immer als State-Maschine im Interrupthandler eingebaut.

Eine Prüfsumme über die beiden Adressen und die Länge ist nötig, wenn sich ein Fehler bei der Länge einschleicht, findest du das Ende nicht.
Zudem brauchst du noch eine Möglichkeit, welche eindeutig den Anfang oder das Ende markiert. Sonst kannst du den Anfang der nächsten Meldung nicht finden, wenn die Länge kaputt ist.

Möglich sind irgendwelche Statusleitungen, die Zeit, wenn sichergestellt ist, dass die minimale Zeit zwischen zwei Meldungen deutlich grösser ist als die maximale Zeit zwischen zwei Zeichen oder halt ein spezielles Zeichen, bzw. ein Escape-Kombination wenn die Protokoll binär ist.

Als Escape-Zeichen nimmt man ein Bitkombination, welche normalerweise eher selten im Datenstrom vorkommt.
Nehmen wir mal z.B. 0xAA
Als Start- oder Endmarke sendest du die Bytes 0xAA, 0xFF
Wenn 0xAA im Datenstrom vorkommt, verdoppelst du dies zu 0xAA 0xAA beim Sender.
Wenn der Empfänger 0xAA 0xAA erhält, wird das erste 0xAA entfernt und das Zweite im Buffer abgelegt.

Scheinbar programmierst du in Bascom, da kann ich dir kein Beispiel liefern, nur in C könnte ich was schreiben.

MfG Peter(TOO)

Andre_S
14.10.2014, 10:54
Hallo!

Auch von mir noch ein etwas anderer Ansatz bei variabler Telegrammübertragung ebenfalls mit Start/Endbyte, bei welchem die Eindeutigkeit dieser gesichert ist. Allerdings müssen dazu mindestens die Nutzdaten und wenn benötigt (Sicherheitsstufe) die Prüfsumme codiert werden. Die Prüfsumme würde ich prinzipiell über alles, ausgenommen Start/Endbyte, nehmen.

Aufbau:


1. Byte Startbyte (0xAA)
2. Byte Sender (0xFF)
3. Byte Empfänger (0xF1)
4...18 Nutzdaten (H/L codiert)

0xA3 (wird zu 0x0A und 0x03)
0x3C (wird zu 0x03 und 0x0C)
usw...


19/20. Prüfsumme XOR (0xA4 -> H/L codiert)

0x0A
0x04


21. Endbyte (0xBB)

Start/Endbyte sind natürlich so zu wählen, dass keine Dopplung mit den Master/Slave Adressen auftreten kann. Ist dies nicht zu realisieren, müssen diese ebenfalls codiert werden.
Das ganze kann man ebenfalls im eigenen Tread/IRQ autark laufen lassen, allerdings kann man grundsätzlich beim Empfang des Startbytes zurücksetzen und bis zum Erreichen des Endbyte einlesen, da diese eindeutig sind und nicht in den anderen Daten vorkommen können.
Nachteil ist, dass sich das Volumen der Nutzdaten/Prüfsumme erhöht und deshalb bei Volumentarifen ungünstig ist.

Somit passiert folgendes beim Empfang des obigen Beispiels:


Startbyte 0xAA empfangen -> Reset Counter
Bytes lessen -> Bufferspeicher (counter++)
Byte ist Endbyte 0xBB

Bufferspeicher auswerten

Adressen 0xFF/0xF1 -> OK
decodieren
XOR über alle Byte (außer Start/Endbyte) –> vergleich mit Prüfsumme -> OK Telegramm empfangen!





Um nicht unnötig lesen zu müssen können auch unterschiedliche Start/Endbyte je Richtung festgelegt werden. Also Slave -> Master z.B. 0xAA/0xCC und Master -> Slave 0xBB/0xDD.

Für ein besseres Fehler/Wiederholungsandling kann man auch noch die Information „Anzahl der Telegramme“ und „Nummer des aktuellen Telegramms“ einbinden.


4. Byte Anzahl Telegramme (0x04)
5. byte Aktuelles Telegramm (0x02)


Könnte die Anzahl der zusammengehörigen Telegramme die Werte des Start/Endbytes erreichen müssten diese ebenfalls innerhalb der Codierung mitgeführt werden.

Und je nach Sicherheitsstufe ist es natürlich auch noch möglich die Information „Anzahl der Bytes“ innerhalb des Telegramms codiert zu hinterlegen.


Gruß André

peterfido
14.10.2014, 11:55
Ich nutze immer folgendes Schema:




Do
If Ischarwaiting() = 1 Then
Gosub Empfangen
End If
loop
end
Empfangen:
B = Inkey()
If B = 13 Then 'CR
Gosub Auswerten
Else
Bb = Len(seingang) 'Noch Platz im Eingangspuffer?
If Bb < 26 Then
Seingang = Seingang + Chr(b)
Else
Seingang = ""
end if
End If
Return

Auswerten:
If Len(seingang) >= 1 Then 'Überhaupt was Verwertbares?
...
...
...
end if

Andre_S
14.10.2014, 12:26
Ich nutze immer folgendes Schema:




Do
If Ischarwaiting() = 1 Then
Gosub Empfangen
End If
loop
end
Empfangen:
B = Inkey()
If B = 13 Then 'CR
Gosub Auswerten
Else
Bb = Len(seingang) 'Noch Platz im Eingangspuffer?
If Bb < 26 Then
Seingang = Seingang + Chr(b)
Else
Seingang = ""
end if
End If
Return

Auswerten:
If Len(seingang) >= 1 Then 'Überhaupt was Verwertbares?
...
...
...
end if


Hallo Peter!

Das geht doch aber nur, wenn Du sicherstellen kannst, dass der Datenstrom keine binären Daten enthält.


Gruß André

peterfido
14.10.2014, 13:34
Jo, stimmt. Binäre Daten in dem Sinne nutze ich so nicht. Da hängt das Protokoll dann vom anderen Baustein ab. Rein nach RS... werden Werte allerdings in Textform übertragen. Da braucht dann eine 200 halt 3 Bytes. Binäre Daten in dem Sinne sind dann was für 1 Wire, TWI und so. Bei komplett eigenen Projekten, wo ich beide Kommunikationspartner programmiere, wird alles so gemacht, wie ich es gepostet habe. "Binäre" Daten werden dann mit gesetztem 7.Bit übermittelt. So erkenne ich immer noch sicher den CHR(13) als Telegrammende. Bisher nur in meinem T300x Projekt (T-Hack) so gemacht.

Wsk8
14.10.2014, 14:20
http://de.wikipedia.org/wiki/Type-Length-Value
Am Schluss noch eine Checksum anfügen und schon hast du ein zu 99% sicheres und einfaches Protokoll.

mfg

Andre_S
14.10.2014, 14:27
Jo, stimmt. Binäre Daten in dem Sinne nutze ich so nicht. Da hängt das Protokoll dann vom anderen Baustein ab. Rein nach RS... werden Werte allerdings in Textform übertragen. Da braucht dann eine 200 halt 3 Bytes. Binäre Daten in dem Sinne sind dann was für 1 Wire, TWI und so. Bei komplett eigenen Projekten, wo ich beide Kommunikationspartner programmiere, wird alles so gemacht, wie ich es gepostet habe. "Binäre" Daten werden dann mit gesetztem 7.Bit übermittelt. So erkenne ich immer noch sicher den CHR(13) als Telegrammende. Bisher nur in meinem T300x Projekt (T-Hack) so gemacht.

Hallo,

ja,… das funktioniert in der Form dann auch!
Es gibt halt immer mehrere Wege zum Ziel…;)

Allerdings möchte ich gern noch ergänzen:
„Rein nach RS... werden Werte allerdings in Textform übertragen“…
Das würde ich nach meinen Erfahrungen nicht verallgemeinern. Ich habe mit verschiedenen Geräten zu tun, welche auf dieser Basis binär Daten übertragen und erwarten, da oft nur die elektrischen Schnittstellen aber nicht das Protokoll definiert wurde. Ein einfaches Beispiel wäre das M-Bus Protokoll, welches durch Pegelwandler auch auf RS4../RS2.. umgesetzt wird.
Bei allen wo ich wiederum Einfluss habe, nehme ich aus Gründen der Störsicherheit das komplette „Gedeck“ mit allen Sicherungen wie oben teilweise angedeutet.


Gruß André

demmy
14.10.2014, 18:15
Wow, da ist ja jetzt einiges an Information zusammengekommen.

Also es ist so, das mein Telegramm momentan rein Binär aufgesetzt ist, weil ich glaube das ich so die länge des Telegrammes und somit die Übertragungszeit drastisch reduzieren kann.
Das bringt aber auch den Nachteil mit sich, das es für mich schwierig ist ein Startbyte bzw. Endbyte zu definieren, da ja jedes Nutzdatenbyte theoretisch jeden beliebigen Wert zwischen 0 und 255 annehmen kann. Wie soll ich also verhindern das ein Nutzdatenbyte den Wert des Startbytes bzw. Endbytes annimmt und diese dann nicht mehr eindeutig sind?

Ich hatte mir überlegt eine Überwachung über die Zeit zu realisieren.

Und zwar soll das einlesen des Telegramms in zwei Stufen erfolgen.
Zunächst wird gewartet bis die Anzahl an Bytes für den Telegramkopf im Eingangspuffer sind, da der Kopf ja immer die selbe Länge hat.
Im zweiten Schritt wird dann der Rest des Telegramms anhand der variablen Länge eingelesen. Diese variable Länge ist ja inzwischen durch den Kopf bekannt.
Ist dann der Rest des Telegramms komplett eingegangen kann es weiter überprüft und verarbeitet werden (Richtige Teilnehmeradress, CRC usw.).

Nun zur zeitlichen Überwachung:
Parallel mit dem Eintreffen des ersten Bytes wird eine Zeitüberwachung angestartet.
Die Zeit der Überwachung ist abhängig von der Anzahl der Bytes die eingelesen werden sollen und wird nach dem Einlesen des Kopfes nochmals korrigiert wenn die Länge der variablen Daten feststeht.
Ist nun die Zeit abgelaufen und das Telegramm nicht vollständig eingegangen oder es stehen Daten über die erwartete Länge hinaus im Eingangspuffer.
Weiß man schon mal, das was schiefgelaufen ist und der Puffer wird geleert. Somit steht er wieder für den Empfang eines neuen Telegramms bereit.
Ist das Telegramm korrekt empfangen wird die Zeitüberwachung gestoppt.

Im Fehlerfall hat ja der Master keine Rückmeldung erhalten, somit muss er irgendwann sein Telegramm wiederholen und erneut versuchen den Slave zu erreichen um die Kommunikation wieder aufzunehemen.

Was haltet Ihr davon?

peterfido
14.10.2014, 18:26
Kann man machen. Würde ich aber nicht so lösen. Was ist so zeitkritisch, wenn es im Fehlerfall eh wiederholt werden kann? Der Master muss sich bei mehreren Slaves dann auch eine Menge merken, um es nochmal zu senden, wenn keine Antwort kam. Stehen da evtl. schon neue Daten an, braucht es weitere Puffer. Was lässt Dich an einer zuverlässigen Übertragung zweifeln?

Wieviele Slaves gibt es, was sollen die so machen und wieviel neue Daten kommen in welchem Zeitraum zusammen? Das würde ich alles berücksichtigen und darauf das Protokoll aufbauen. Die Baudrate wähle ich anhand der Verbindungsqualität und Leitunsglänge.

demmy
14.10.2014, 18:46
Naja eine Fehlerbehandlung muss ja in irgendeiner Form rein!? Das mit der Überwachungszeit ist rein nur als Fehlerbehandlung gedacht um im Fehlerfall wieder auf einen grünen Zweig zu kommen, also den Telegrammempfang zu bereinigen.

Und wenn ein Fehler im Buszyklus auftritt muss das doch auch irgendwie behandelt werden!? Oder feuert ihr einfach alle Telegramme nacheinander einfach raus und wartet gar nicht nach jedem anpollen eines Slaves auf dessen Antwort?

Ich meine man muss doch immer mal damit rechnen das ein Telegramm mal zerstört wurde durch Einstreuungen oder was weis ich! :)

Also Ziel soll es sein, einen Ausgangsdatenspeicher, welcher im Master permanent aktualisiert wird an die Slaves zu verteilen und umgedreht die aktuellsten Daten abzuholen um einen Eingangsdatenspeicher zu füllen. Die Daten stehen also so und so die ganze Zeit in der Steuerung zur Verfügung und werden dann nur nochmal in der zu diesem Zeitpunkt aktuellsten Form gesendet.