Hallo zusammen,
ich steh hier vor einem ganz großen Problem und würde mich hierbei um
eure Hilfe freuen.
Es geht darum eine Datenkommunikation zwischen PC (serielle
Schnittstelle) und µC ( ATMega8515 ) aufzubauen. Erst einmal nur zu
Lernzwecken ein single Master Protokoll:
<SenderID><EmpfängerID><Länge><Nutzdaten><Checksumme>
Dabei habe ich erst einmal damit anfangen am PC ein kleines Datenpacket
zu schnüren und dieses dann an den µC zu senden. PC-Seitig klappt das
auch. Mein Problem ist jetzt allerdings der Empfang am/im µC.
Es würde mich freuen, wenn ihr mir euer Vorgehen einmal schildern
würdet, denn fertige Implementierungen, die man als Anfänger auch
verstehen kann habe ich bisher nicht gefunden.
Ich dachte mir das folgendermaßen, fühle mich dabei aber wie wenn ich
auf dem Holzweg wäre:
Ich erzeuge ein globales Array, in das ich nach und nach die per UART
reinkommenden Daten im UART-RX-Interrupt schreibe. Wird eine längere
Zeit nichts empfangen, wird das Array wieder vom ersten Element weg
beschrieben.
Wenn eine Packet angekommen ist wird ein Flag gesetzt und in der
main-Routine wird das Array dann "ausgewertet", d.h. checksumme prüfen
etc.
Leider entdecke ich jetzt schon einige Probleme: als erstes das timeout.
Wie erkenne ich wie lange keine Daten mehr eingetroffen sind?
Oder was mache ich, damit mir das Array nicht überschrieben wird wenn
noch nicht alle Daten verarbeitet wurden?
Wäre schön wenn mir grad jemand mit seiner Idee auf die Sprünge helfen
würde.
Vielen vielen Dank
Julian
Julian E. schrieb:> Lernzwecken ein single Master Protokoll:> <SenderID><EmpfängerID><Länge><Nutzdaten><Checksumme>
Du hast einen der wichtigsten Punkt vergessen:
Du brauchst entweder vorne oder hinten (oder an beiden Enden) eine
EINDEUTIGE Kennung, die dir die Synchronisierung auf den Anfang eines
Datenpaketes ermöglicht.
Das übersehen Neulinge gerne. Neben den Nutzdaten benötigt man für ein
stabiles Protokoll auch immer ein gewisses Mass an Verwaltungsinfo,
welche nur dazu dient die Übertragung selbst zu steuern.
> Es würde mich freuen, wenn ihr mir euer Vorgehen einmal schildern> würdet, denn fertige Implementierungen, die man als Anfänger auch> verstehen kann habe ich bisher nicht gefunden.> Ich dachte mir das folgendermaßen, fühle mich dabei aber wie wenn ich> auf dem Holzweg wäre:> Ich erzeuge ein globales Array, in das ich nach und nach die per UART> reinkommenden Daten im UART-RX-Interrupt schreibe. Wird eine längere> Zeit nichts empfangen, wird das Array wieder vom ersten Element weg> beschrieben.
Kann man so machen, aber Timeouts zur Synchronisierungssteuerung sind
meistens keine gute Idee.
> Wenn eine Packet angekommen ist wird ein Flag gesetzt und in der> main-Routine wird das Array dann "ausgewertet", d.h. checksumme prüfen> etc.> Leider entdecke ich jetzt schon einige Probleme: als erstes das timeout.> Wie erkenne ich wie lange keine Daten mehr eingetroffen sind?
Timer mitlaufen lassen.
Aber mit einer dezidierten Framesteuerung in Form eines eindeutigen
Frameanfangs ist das normalerweise nicht notwendig. Ungültige Frames
können da normalerweise sauber identifiziert werden.
> Oder was mache ich, damit mir das Array nicht überschrieben wird wenn> noch nicht alle Daten verarbeitet wurden?
Das Array muss groß genug sein, damit das längste mögliche Paket da auch
tatsächlich reinpasst.
Julian E. schrieb:> Ich erzeuge ein globales Array, in das ich nach und nach die per UART> reinkommenden Daten im UART-RX-Interrupt schreibe. Wird eine längere> Zeit nichts empfangen, wird das Array wieder vom ersten Element weg> beschrieben.
Ich würde das globale Array als "Ringpuffer" organisieren. Ein Löschen
ist dann "LeseIndex = SchreibIndex;"
Julian E. schrieb:> Oder was mache ich, damit mir das Array nicht überschrieben wird wenn> noch nicht alle Daten verarbeitet wurden?
Der Punkt erledigt sich von selbst wenn der Ringpuffer groß genug ist.
Der Puffer muß dann um soviele empfangene Bytes größer sein wie Zeit
benötigt wird um die alte Botschaft zu verarbeiten.
Gruß Anja
Also ich muss sagen, ich mach es mir da etwas einfacher, und hatte damit
noch keine Probleme...
Checksumme usw... muss nicht sein, kommt halt darauf an wie fehlersicher
deine Übertragung sein soll...
Ich nutze immer einen Frame der folgendermaßen aufgebaut ist:
S01!
S = Frame anfang
01 = Daten / Kommando...
! = bezeichnet das ende des Frames
Bei der übertragung von PC -> µC prüfe ich per Polling das RS232
Empfangsflag, wird was empfangen gehe ich in die RS232 Empfangsroutine
die das empfangene Zeichen auswertet ist es ein "S" wird weiterhin immer
das Empfangsflag geprüft, wird was empfangen kommt die Prüfung nach dem
Kommando
und danach könnten weitere Daten / Kommandos folgen oder dann das "!"
was das ende des Strings kennzeichnet.
Aber, viele Wege führen nach Rom...
LG. Christian
Julian E. schrieb:> Erst einmal nur zu Lernzwecken ein single Master Protokoll:> <SenderID><EmpfängerID><Länge><Nutzdaten><Checksumme>
Wozu braucht ein Single Master System eine SeinderID?
> Es geht darum eine Datenkommunikation zwischen PC (serielle> Schnittstelle) und µC ( ATMega8515 ) aufzubauen.
Wenn du eine "normale" serielle Schnitte hast, dann ist sogar die
Empfänger-ID ziemlich eindeutig...
> <SenderID><EmpfängerID><Länge><Nutzdaten><Checksumme>
Wie willst du die <Checksumme> verpacken?
Binär oder als ASCII-Zahl?
> <SenderID><EmpfängerID><Länge><Nutzdaten><Checksumme>
Dann also eher so:
<STX><SenderID><EmpfängerID><Länge><Nutzdaten><Checksumme><CR>(oder
<ETX>)
Und die Daten am besten als ASCII-Zeichenkette. Also nicht 0x22 = 34
verschicken, sondern '3','4'...
Christian T. schrieb:> und danach könnten weitere Daten / Kommandos folgen oder dann das "!"> was das ende des Strings kennzeichnet.
Es gibt tolle Zeichen, die optimal für sowas geeignet sind. Ein
Carriage-Return ist so eines. Wenn ich mich dann zum Debuggen in eine
serielle Verbindung reinhänge, bekomme ich am Ende eines Datenpakets
automatisch und umsonst eine neue Zeile...
Der Empfänger-ID kommt zuerst, da ein uC, welcher den Empfänger mittels
Interrupt macht ev. dann für einen definierten Timeout nicht hinhören
will
oder einfach besseres zu tun hat. Sollte es möglich sein, die Adressen
so vergeben, daß eine Parity damit gegeben ist und so eine 9bit
Übertragung mit
einem PC kompatibel ist.
Hi,
wenn schon checksumme, wäre zu überlegen, ob du den
Header auch Checksummen-sicherst, ansonsten kannst du nicht sicher
sein, ob das was im Header steht korrekt ist.
Ein Start- und Stop Signal habe ich bisher noch nicht gebraucht.
In solchen Fällen greife ich zu einem finiten Automaten:
Zustand 1:
Idle (warte auf Daten)
Zustand 2:
Header empfangen
Zustand 3:
Daten empfangen
Übergänge:
1-->2 erstes Header-Byte empfangen
2-->2 weitere Header-Bytes empfangen, solange Header nicht komplett
2-->3 wenn Header Checksumme ok
3-->3 Daten-Bytes empfangen, solange noch welche fehlen
3-->1 letztes Datenbyte empfangen
2-->1 Timeout (länger als 2s nichts empfangen)
2-->1 Checksumme Header falsch
3-->1 Timeout (länger als 2s nichts empfangen)
3-->1 Checksumme Datenblock falsch
Wie lang der Header ist, weiß ich ja. Wenn ich also einen Header
empfangen habe, steht da drin, wieviele Daten noch kommen müssen. Mit
diesen beiden Informationen betreibe ich den Automaten.
Den Header würde ich in eine Stack-Variable schreiben, wenn ich dann
weiß,
wieviele Nutzdaten kommen (am Übergang 2->3) würde ich bspw.
Heap-Speicher allokieren und den Header reinkopieren und die Nutzdaten
dahinter schreiben. Wenn das letze Datenbyte empfangen ist, kann der
Heap-Speicher weitergereicht werden und das Protokoll wartet wieder im
Zustand 1 auf Daten.
...eine von vielen Möglichkeiten
@Karl Heinz Buchegger
>Du hast einen der wichtigsten Punkt vergessen:>Du brauchst entweder vorne oder hinten (oder anbeiden Enden) eine>EINDEUTIGE Kennung, die dir die Synchronisierung auf den Anfang eines>Datenpaketes ermöglicht.
Kopfschüttel! Da bin ich anderer Meinung. Die Protokolldefinition
liefert diese Informationen bereits - oder was denkst du, wie sagen wir
mal UDP funktioniert?
@Lothar Miller
>Wenn du eine "normale" serielle Schnitte hast, dann ist sogar die>Empfänger-ID ziemlich eindeutig...
Verstehe ich nicht. Angenommen, ich habe in meiner Software ein
Protokollmodul, welches die serielle Schnittstelle öffnet und oben drauf
sitzen zwei Komponenten (bspw. Datenkanal und Steuerkanal). Nehmen wir
weiter an, dass diese Architektur an beiden Enden der Schittstelle
implementiert ist.
Da halte ich es schon für notwendig, dass es Absende- und Empfänger ID
gibt,
damit man eine gute Architektur und Komponenten-Kapselung machen kann
und die verschiedenen Nutzdaten an der richtigen Stelle ankommen.
Peter schrieb:> @Karl Heinz Buchegger>>Du hast einen der wichtigsten Punkt vergessen:>>Du brauchst entweder vorne oder hinten (oder anbeiden Enden) eine>>EINDEUTIGE Kennung, die dir die Synchronisierung auf den Anfang eines>>Datenpaketes ermöglicht.> Kopfschüttel! Da bin ich anderer Meinung.
das sei dir gegönnt.
Jeder der an einer seriellen Schnittstelle Ausfälle wegen abgesteckter
Kabel hatte, oder Geräte, die während einer laufenden Übertragung ein
und ausgeschaltet wurden, denkt da anders darüber.
Es gibt auch Leute, die meinen eine Versionsnummer am Anfang eines Files
sei überflüssiger Luxus.
PS: UDP setzt auf einem Ethernet Frame auf. Und ein Ethernet Frame
beginnt mit einer Präambel :-)
http://de.wikipedia.org/wiki/Ethernet#Formate_der_Ethernet-Daten.C3.BCbertragungsbl.C3.B6cke_und_das_Typfeld
@Karl Heinz Buchegger
>Jeder der an einer seriellen Schnittstelle Ausfälle wegen abgesteckter>Kabel hatte, oder Geräte, die während einer laufenden Übertragung ein>und ausgeschaltet wurden, denkt da anders darüber.
Ein Sync-Token hilft da leider nicht, das bestätigt dir jeder, der
sich ernsthaft mit sicherer Datenübertragung auseinandersetzt.
Ein Protokoll kann nur Datenintegrität zusichern - bspw. mit Checksumme.
Eine Ausfall-Erkennung implementiere ich immer funktional bspw. mit
zyklischen Handshake-Messages.
Peter schrieb:> @Karl Heinz Buchegger>>Jeder der an einer seriellen Schnittstelle Ausfälle wegen abgesteckter>>Kabel hatte, oder Geräte, die während einer laufenden Übertragung ein>>und ausgeschaltet wurden, denkt da anders darüber.> Ein Sync-Token hilft da leider nicht,
Solange sicher gestellt ist, dass der Sync Token in den Daten nicht
vorkommen kann, funktioniert das perfekt. Die Protokolldefinition ohne
einleitendes Sync-Token hilft dir noch weniger, weil du nicht mit 100%
Sicherheit davon ausgehen kannst, dass du nicht mitten in einen
laufenden Frame einsteigst und die empfangenen Bytes vollkommen
missverstehst.
Wartest du aber auf das Sync Token für den Framestart, dann hast du
schon mal die Gewähr, dass du auch tatsächlich die Bytes für den
Frameanfang hältst, die auch tatsächlich den Frameanfang markieren. Und
ab dort übernimmt dann die Protokolldefinition und erlaubt das Parsen
des Frames.
> Eine Ausfall-Erkennung
Es geht nicht um Ausfallerkennung.
@Harl Heinz Buchegger
>PS: UDP setzt auf einem Ethernet Frame auf. Und ein Ethernet Frame>beginnt mit einer Präambel :-)
Du bringst da was durcheinander. Die Präambel signalisiert nicht den
Beginn eines Ethernet Frames sondern war ganz früher mal gebräuchlich,
um Sender und Empfänger zu synchronisieren. Sie ist lt. OSI unterhalb
des MAC Layers definiert und somit nicht Bestandteil des Ethernet
Frames.
Auch kenne ich keinen Netzwerk-Chip, geschweige denn Treiber, der sich
auf die Ethernet Präambel abstützen würde. Es wird immer nur mit Header
und Nutzdaten hantiert. Die Ausfallsicherheit wird dann weiter oben
bspw. mit TCP gemacht.
http://de.wikipedia.org/wiki/Ethernet#Formate_der_Ethernet-Daten.C3.BCbertragungsbl.C3.B6cke_und_das_Typfeld
Peter schrieb:> @Harl Heinz Buchegger>>PS: UDP setzt auf einem Ethernet Frame auf. Und ein Ethernet Frame>>beginnt mit einer Präambel :-)> Du bringst da was durcheinander.
Mag sein.
Trotzdem ist dein Beispiel mit UDP nicht zutreffend, da UDP nicht die
unterste Schicht ist und sich nicht um Framesynchronisierung kümmert.
UDP setzt implizit voraus (durch das darunter liegende IP), dass es
möglich ist ein Datenpaket an die Gegenstelle zu schicken, und das diese
das Paket auch tatsächlich wieder als Paket erhält (wenn es das Paket
überhaupt erhält). Aber UDP kümmert sich nicht darum, was zu passieren
hat, wenn du eine Netzwerkkarte an ein Kabel stöpselst auf der gerade
ein anderes Paket übertragen wird. Auch IP kümmert sich noch nicht
darum. Die Ebene der Frames ist die OSI Schicht 1.
> Es wird immer nur mit Header und Nutzdaten hantiert.
Logo. Weil sich die Frame-Synchronisierung die Chips untereinander
ausmachen. Ausserhalb der Chips interessiert das keinen.
Aber all das ist letzten Endes im gegenständlichen Fall ziemlich
uninteressant. Der TO hat es mit einer UART zu tun. Und da gibt es nun
mal keine Frames, die von den UART Bausteinen gehandhabt werden. Also
muss man sich die, samt zugehöriger Steuerung, in Software selber
machen.
Peter schrieb:> Du bringst da was durcheinander.
Ich gehe da mit Karl Heinz Buchegger konform, und sehe den Irrtum auf
deiner Seite ;)
Nicht umsonst nutzen darauf aufbauende Protokolle wie TCP eine
Sequenznummer. Ein UDP Paket ist vergleichbar mit einem einzelnem Byte
das du über die RS232 empfängst, auch da gibt es auf der Ebene
"dadrunter" eine Schicht mit Start und Stopbit welche die Syncronisation
sicherstellt und dir dann ein fertiges Paket (in diesem Fall ein byte)
liefert. Ebeneso bekommst du ein UDP-Paket, was aber auch nicht auf
magische Weise aus dem nichts entsteht, sondern einfach das Ergebnis
einer sehr viel tieferliegenden Syncronisation der beiden
Kommunikationspartner ist.
Karl Heinz Buchegger schrieb:> Und da gibt es nun> mal keine Frames
Wie gesagt, im kleinerem Rahmen schon ;)
Hallo,
wie realisiert man sowas in C?
Stehe vor einer ähnlichen Aufgabe, aber weiss nicht genau wie ich
Programmiertechnisch mir ein solchen Frame zusammenbaue zum senden und
wie ich die einzelnen States ändere bzw. ändern muss.
Wäre für ein paar hilfreiche Tipps dankbar.
Gruß
paule
Moin
ich hätte noch 2 kleine weitere dinge, die etwas höher angesiedelt sind.
1. ein kommando um die Protokoll version in erfahrung bringen zu können.
ggf in der zukunft notwendig damit der PC ggf auf die unterschiedlichen
Komandos / protokoll verhalten reagieren kann. Software und die
protokolle entwickeln sich, die im Feld alle auf gleichem level halten
zu können ist ein ding der unmöglichkeit.
2. ein Protokoll sollte bei unbekannten kommandos die er nicht kennt
nicht einfach nichts tun. kann dazu führen, das der sender des kommandos
sich zu tode wartet weil er auf ne antwort wartet (oder ein timeout
kommt. wobei timeout immer böse sind, bzw wann kommt das timeout, .)
besser ist es hier wenn das Protokoll ein NAC quasi eine negative
antwort beitet z.B. Unbekanntes Kommando.
Ich gehe auch mit der meinung von Karl Heinz Buchegger konform. Habe
hier von einem Lieferanten ein ser.Protokoll, das über UART
rausgeknüppelt wird und keine EscSequenzen zur Synchronisation
beinhaltet, sondern dies per Timeouts realisiert. Grauslich! Dann habe
ich einen RFID-Leser, der per USB (VCOM) mit dem PC kommuniziert. Mit
EscSequenzen. Läuft rund - auch wenn bei USB ein Kabelabziehen zu
wesentlich drastischern Konsequenzen unter Windows führt.
@paule:
bastel Dir auf dem Papier eine Statemachine zusammen, die EscSequenzen
hinzufügt bzw. entfernt. Ist eigentlich sehr einfach, wenn man das
Prinzip gerafft hat. Die Umsetzung ist dann sehr einfach.