Hallo, bekanntlich lösen alle uC IRQs aus, wenn über SPI,I2C usw etc Daten eintrudeln. Bisher habe ich das so gelöst, dass für jedes Byte die ISR extra aufgerufen wird, in ihr läuft eine State Machine, die das Protokoll decodiert und Header, Cheksummen etc von Nutzdaten trennt. Ist teilweise etwas kompliziert, vor allem wenn man damit rechnen muss dass ein Datensatz abgebrochen wird, vor allem bei Infrarotsignalen. Dan hängt die Statemachine fest, weil der IRQ nicht mehr kommt und kann nur zB durch einen Timer wieder rückgesetzt werden. Nun gäbe es noch die Möglichkeit beim ersten Byte reinzuspringen, dann in der ISR bleiben und pollen bis alle Bytes gekommen sind. Man könnte einen Timer als Timeout verwenden und dann rausspringen wenn er überläuft. Bei SPI weiss ich ja, da ich beide uC programmiere, dass das nie passieren kann aber bei externen Signalen muss man damit rechnen. Was ist die "anerkannte" Methode oder wie löst ihr das? Gruss, Christian
das mit der state machine passt schon bloss sind inputs in die state machine nicht nur Ereignisse wie "Zeichen empfangen", sondern auch "Timeout" oder was anderes (zB "Schnittstelle wurde (de)aktiviert") die state machine der Schnittstelle reagiert daher nicht nur auf "Zeichen empfangen" der Zustand bleibt in einer globalen Datenstruktur, damit können andere Ereignisse auch auf den Zustand zugreifen & verändern, nicht nur die ISR damit kann Dir der Hänger nicht mehr passieren - starte einfach am Beginn der ISR einen Timer wenn der abläuft, ist es eben ein weiteres input-event in die state machine - zB Rücksetzen in den Ausgangszustand -Michael
@Christian J. (elektroniker1968) >eintrudeln. Bisher habe ich das so gelöst, dass für jedes Byte die ISR >extra aufgerufen wird, in ihr läuft eine State Machine, die das >Protokoll decodiert und Header, Cheksummen etc von Nutzdaten trennt. Falscher Ansatz. ISRs sollen möglichst kurz sein. Die Hauptaufgabe verlagert man in die Hauptschleife, siehe Interrupt. >Nun gäbe es noch die Möglichkeit beim ersten Byte reinzuspringen, dann >in der ISR bleiben und pollen bis alle Bytes gekommen sind. AUA!!! Pollen und ISR schliesst sich gegenseitig aus! Siehe Artikel oben! > Man könnte >einen Timer als Timeout verwenden und dann rausspringen wenn er >überläuft. Unsinnig. MFG Falk
Hallo, es ist leider nicht möglich komplett auf eine Decodierung in der ISR zu verzichten, da der Header Dinge wie Startbyte, Anzahl Bytes und Checksumme enthält, die es erst ermöglichen Daten in der richtigen Folge zu empfangen. SPI Zeitprobleme gibt es nicht, man darf sowieso nicht Full speed senden, also ohne Pause, weil dann der Slave keine Zeit hat abszuspeichern etc. Sieht derzeit so: state 1: prüfe Startbyte goto raus state 2: Anzahl zeichen holen goto raus state 3: datenbytes einzeln ablegen u. Checksumme bilden goto raus state 4. Checksumme prüfen Statemachine zurücksetzen goto raus Danke aber für die Bestätigung, ich werde es so machen und auf pollen verzichten.
Christian J. wrote: > Hallo, > > es ist leider nicht möglich komplett auf eine Decodierung in der ISR zu > verzichten, Warum? Die ISR kann sich damit begnügen das eine Byte, für dessen Empfang sie aufgerufen wurde, von der Schnittstelle zu holen und in den Input Buffer zu schieben. In der Hauptschleife hast du dann alle Zeit der Welt den Input Buffer sukzessive auszuwerten und laut deinem Protokoll zu analysieren. Dort löst sich dann auch dein Timeoutproblem in der Statemachine sehr viel einfacher als in der ISR. Niemand sagt, dass aus der ISR schon fertig dekodierte Datensätze herausfallen müssen. Die ISR kann sich vollauf damit begnügen, die Schnittstelle zu bedienen und 'Rohdaten' zu holen.
Christian J. wrote: > es ist leider nicht möglich komplett auf eine Decodierung in der ISR zu > verzichten, da der Header Dinge wie Startbyte, Anzahl Bytes und > Checksumme enthält, die es erst ermöglichen Daten in der richtigen Folge > zu empfangen. Natürlich ist das möglich. Was soll "in der richtigen Folge zu empfangen" denn überhaupt bedeuten? Die Daten werden in der Reihenfolge empfangen, wie sie nun mal reinkommen. Das nächste Byte ist das nächste Byte. Der normale Weg ist es, die Daten in der ISR einfach zu empfangen und in einem kleinen Ringpuffer abzulegen. Interpretiert werden sie dann in der Hauptschleife.
Gast
#1157603
Was spricht dagegen, einfach den ankommenden Datenstrom in einem Array (zeischen)zuspeichern und später (in main) auszuwerten? Also etwa so:
1 | |
2 | |
3 | |
4 | |
Das idx wird durch Timeout oder irgendeine Ende-erkennung auf null gesetzt. gleichzeitig damit wird ein "ich hab was empfangen" Flag gesetzt. Dann startet die main-Auswertung, etwa so:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
Gast
#1157614
Ich decodiere auch ein Protokol in der ISR mit einer Statemaschine. Wenn man die Instruktionen zaehlt, die aufgerufen werden, so sind es nicht sehr viele. Ein riesiges Switchkonstrukt kann pro Umlauf auch nur eine Handvoll Instruktionen enthalten. Ja, man sollte einen Timeout laufen lassen, ein Timer wird bei jedem Zeichen zurueckgesetzt. Das ist eine optomale Loesung.
Die ISR schreibt die Daten nur in eine FIFO. Das main liest dann aus der FIFO in einen Paketpuffer ein und wertet das Paket aus, wenn es vollständig ist. Das kann z.B. so aussehen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
Peter
Hallo, also mal langsam zum verstehen.... Wenn es einen Ringpuffer gibt, dann brauche ich einen Trigger, der mir anzeigt, dass der Datensatz vollständig ist. Was nützt mir eine Auswertung in Main wenn nur die Hälfte da ist, verschwendete Rechenzeit. Den Trigger gibt es also nicht, weil noch nichts ausgewertet wurde, der Prozessor läuft ja nur stur mit und schiebt rein. Wird nichts abgeholt läuft er über und fängt von vorne an. Während der Auswertung muss der Puffer eingefroren werden, also können mir Daten verloren gehen, wenn dann neue kommen. Auch das Kopieren in einen anderen Puffer kostet Zeit, wäre aber denkbar. Ich werde das aber nochmal durchdenken, derzeit ist es mir nicht so klar, da dann zwei Prozesse (ISR und Main) gegeneinander arbeiten können, weil sie auf die gleiche Datenbasis zugreifen, es als zu Konflikten kommen kann. Als Trigger könnnte bei der SPI der Slave Select dienen, weil der High geht wenn Daten fertig gesendet wurden. Bei einer IR übertragung geht das aber nicht mehr, da muss Bit für Bit ausgewertet werden und das geht wohl doch besser in der ISR, wenn die Bytes da zusammengebaut werden. Gruss, Christian
Ein Ringpuffer ist in diesem Kontext u.U. nicht optimal, da die Analyse der empfangenen Message durch das Hauptprogramm etwas darunter leidet. Wenn die Puffergrösse nicht sehr hoch ist, kann es angenehmer sein, den Puffer bei einer vollständig verarbeiteten Message aufzuräumen und den Rest aufzuschieben. Dann kann man die älteste Message sehr einfach als buffer[0..n] ansprechen und ohne zusätzliche Statemachine so lange im Hauptprogramm die Runde drehen bis genug da ist. Man muss nur darauf achten, die Verschiebeaktion interrupt-fest zu gestalten. Wenn man das 2stufig erledigt, dann ist die Sperrzeit des Interrupts dennoch sehr kurz: Erst den grad bekannten Teil ohne Interrupt-Sperre schieben, dann den ggf. zwischendrin eingetrudelten Rest mit Sperre.
Gast
#1157827
Falls man sich entschliesst mit Ringbuffern zu arbeiten, benutzt man ein kleines flag (boolean), wie datacame, das die Statemaschine im main anwirft. Heisst, das main pollt dieses flag und arbeitet die statusmaschine ab. Gleichzeitiger zugriff auf die selben daten ist kein problem, solange einer nur schreibt, und der andere nur liest.
Gast
#1157831
Hi, ok, ziehe ich mal Resumee, wenn ich heute abend wieder an den Source gehe: SPI: SPI Bytes per ISR in einen Fifo schieben ist ein Dreizeiler Mainroutine 1. Main schaut nach, ob Slave Select = HIGH ist, denn dann kommen garantiert keine neuen Daten an. Ein Datensatz läuft immer zwischen SS=1,0,1 durch. 2. Main blockiert Puffer 3. Main schaut sich puffer[0] an, ist es ein Startbyte? Nein => index = 0, Puffer vérwerfen. Funkdaten über RFM12: Hier wird auch ein "halber" Puffer durchlaufen, stellt er sich als unvollständig heraus wird er in Ruhe gelassen, bis ein Timeout abgelaufen ist, dann wird er zurückgesetzt. Die Entscheidung über unvollständig kann ich über Checksummen prüfen. Schaun mer mal :-)
Christian J. wrote: > Wenn es einen Ringpuffer gibt, dann brauche ich einen Trigger, der mir > anzeigt, dass der Datensatz vollständig ist. Nö, der FIFO sammelt nur stur die Bytes und nichts weiter. Im Main hast Du dann einen Framepuffer und eine Routine, die nur die Frameerkennung macht und diesen Puffer füllt. Das ist mein oben angegebenes Beispiel. Und erst, wenn diese erkennt, daß ein Frame vollständig ist, wird dieser ausgewertet, das ist im obigen Beispiel mit "exec();" gemeint. Diese kann z.B. Sendedaten bereitstellen und diese in die Sende-FIFO packen. Der Empfangs-FIFO-Interrupt kann derweil unbekümmert weitere Bytes sammeln. Du hast also insgesamt 3 Tasks: Interrupt: - Empfangs-FIFO Main: - Frameerkennung - Auswertung Du verschwendest damit keine Rechenzeit und brauchst auch keinen Timeout. Peter
Christian J. wrote: > Wenn es einen Ringpuffer gibt, dann brauche ich einen Trigger, der mir > anzeigt, dass der Datensatz vollständig ist. Du hast immer noch nicht verstanden, worauf wir hinauswollen. Der Ringbuffer dient nur dazu, die ISR von der eigentlichen Verarbeitung des Empfangenen zu entkoppeln. Ob deine Statemachine jetzt mit jedem Zeichen aus der ISR heraus getrieben wird ...
1 | |
2 | |
3 | |
4 | |
5 | |
.... oder ob das Zeichen in der Mainloop in die Statemachine hineingestopft wird, ist für die Statemachine 'gehupft wie gesprungen'. Der entscheidende Unterschied ist aber: Mit einem Ringbuffer ist die Abarbeitungszeit der ISR von der Zeit die für die Statemachine draufgeht entkoppelt. Das Ganze hat nichts mit Frames oder Datensätzen oder so zu tun. Du brauchst deine Statemachine wahrscheinlich noch nicht mal umschreiben. Alles was du tun musst: Die ISR schreibt das empfangene Zeichen in den Ringbuffer und in die Hauptschleife kommt dazu
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
und wenn dein restlicher Softwareaufbau vernünfitg war, dann läuft im wesentlichen alles gleich wie bisher, nur dass die die UART-Receiver-ISR nicht die Maschine blockiert, weil sie das emfpangene Zeichen unbedingt verarbeiten muss. Und wenn die Statemachine mal mit den empfangenen Zeichen nicht hinterherkommt, dann hast du zusätzlich noch einen Ringbuffer dazwischen, der eine gewisse Verzögerung in der Statemachine auffangen kann, in der der Prozessor trotzdem aufnahmebereit ist, obwohl die Statemachine noch an den vorhergehenden Zeichen kiefelt.
Gast
#1157923
Leute, ich Danke Euch für diese ausführlichen Ausführungen! :astrein-smiley: Werde ich heute abend mal umsetzen.
Gast
#1158001
PS: Ich stöbere grad nach Code für einen Ringbuffer und habe auch einen gefunden. Prinzipiell kreist da der write Zeiger von 0 auf max und fängt bei 0 wieder an, wenn Überlauf. Wird der Read Zeiger nicht nachgezogen, weil zB der Slave gerade "pennt" kommt es zum Überlauf, der write Zeiger überholt den Read Zeiger. Das würde bedeuten, dass ein Datenframe da irgendwie mittendrin liegen kann. ich habe als Startbyté bisher nur ein Byte genommen, da dieses natürlich auch in den Daten vorkommen kann wäre die Starterkennung nicht mehr sicher. Was ist denn da ein gängiges Verfahren? Würden zB 32 Bit ausreichen um es ausreichend unwahrscheinlich zu machen, dass im Datensatz die Startsignatur auftaucht. Oder ist es einfach egal, wenn man bei jedem gefundenen Startbyte stur durchprüft, ob nach x Bytes die Checksumme auch am richtigen Platz mit dem richtigen Wert steht? Peter, wie hast Du´das gemacht? Ein Satz bei mir bisher: <start> <anzahl bytes> <datenbytes> <checksumme 2 Bytes> Checksumme ist einfach die Ver-Xorung aller Bytes incl des Headers.
Gast
#1158007
>Du hast also insgesamt 3 Tasks: >Interrupt: >- Empfangs-FIFO >Main: >- Frameerkennung >- Auswertung > >Du verschwendest damit keine Rechenzeit und brauchst auch keinen >Timeout. Ein Timeout braucht man trotzdem um zu erkennen, ob eine Uebertragung abgebrochen wurde. Dann wird verworfen.
Christian J. wrote: > Das würde bedeuten, dass ein Datenframe da irgendwie mittendrin liegen > kann. Ja und? Interessiert den Verarbeiter nicht weiter. Für den ist der Ringbuffer einfach nur ein Zwischenspeicher aus dem die Bytes in derselben Reihenfolge herausfallen in der sie hineingesteckt wurden. Wo die konkret innerhalb des Buffers zu liegen kommen, ist dem Verwender völlig schnuppe (genauso wie ihn der Lese und der Schreibzeiger nicht zu interessieren haben) > Wird der Read Zeiger nicht nachgezogen, > weil zB der Slave gerade "pennt" kommt es zum Überlauf, der write Zeiger > überholt den Read Zeiger. Dafür sorgen hoffentlich die Ringbufferroutinen, dass dieser Fehlerfall sauber gehandhabt wird. Aber im Grunde ist dieses Problem ja nichts neues. Wenn in einer UART Zeichen schneller ankommen, als sie die verarbeitende Funktion abholt, dann verschwinden ein paar Bytes im Nirvana. > Was ist denn da ein gängiges Verfahren? Pragmatische Lösung: Den unvollständigen Datensatz verwerfen und solange weiterlesen, bis wieder ein Startbyte einwandfrei identifiziert werden konnte. ALles dazwischenliegende wird verworfen. Das Problem besteht einfach darin, für das Startbyte etwas zu finden, dass eindeutig in einem Datenstrom erkannt werden kann. Und zwar egal an welcher Stelle man im Datenstrom anfängt zu lesen. > Oder ist es einfach egal, wenn > man bei jedem gefundenen Startbyte stur durchprüft, ob nach x Bytes die > Checksumme auch am richtigen Platz mit dem richtigen Wert steht? Woher weißt du den, ob die nächsten 4 Bytes eine Checksumme sind oder einfach nur ein paar Datenbytes? Das Problem besteht ja doch darin, einen eingehenden Datenstrom in Datenframes aufzuteilen. Und erst wenn das gelungen ist, weißt du welche Bytes die Checksumme sind. Bei deinem gedachten Protokoll, schreib einfach mal einen Bytestrom auf, so wie ihn der Sender senden würde. Ein paar Datensätze lang. Und dann lass die ersten x Bytes einfach mal weg und geh deine Statemachine durch, ob sie von alleine wieder auf die Füsse fällt (sprich: sich wieder mit einem Datensatzanfang synchronisiert). Wenn nicht, dann ist dein Protokoll nichts wert (zumindest nicht wenn es um das Thema geht: Neusynchronisierung im Fehlerfall) und du kannst das Startbyte genausogut auch weglassen.
Christian J. wrote: > ich habe als Startbyté bisher nur ein Byte genommen, da dieses natürlich > auch in den Daten vorkommen kann wäre die Starterkennung nicht mehr > sicher. Was ist denn da ein gängiges Verfahren? Würden zB 32 Bit > ausreichen um es ausreichend unwahrscheinlich zu machen, dass im > Datensatz die Startsignatur auftaucht. Oder ist es einfach egal, wenn > man bei jedem gefundenen Startbyte stur durchprüft, ob nach x Bytes die > Checksumme auch am richtigen Platz mit dem richtigen Wert steht? Meine obige Funktion macht es genau so. Erst wenn das Startbyte empfangen wurde, zählt idx hoch. Erreicht idx die Länge (im 2.Byte), wird die CRC-Prüfung aufgerufen und dann der Frame ausgewertet. Die Daten dürfen also auch das Startbyte enthalten, da erstmal alles bis zur Länge im Framepuffer landet. Geht die Synchronisation verloren, merkt das der Master daran, daß keine Antwort erfolgt (ich beantworte jeden Frame). Dann kann er Dummy-Bytes (kein Startbyte) senden bis zur maximalen Framelänge. Das nächste Startbyte ist dann wieder synchron. Ein Timeout ist daher nicht nötig. Der einzige Unterschied ist nur, daß mein FIFO die UART einliest, daher die Funktionsnamen: kbhit - mindestens ein Byte im FIFO ugetchar - lese ein Byte aus FIFO. Peter
Christian J. wrote: > ich habe als Startbyté bisher nur ein Byte genommen, da dieses natürlich > auch in den Daten vorkommen kann wäre die Starterkennung nicht mehr > sicher. Was ist denn da ein gängiges Verfahren? Protokolle, die ein Startbyte verwenden, das an anderer Stelle ebenfalls auftreten kann, sind wertlos. Allerdings kommt sowas auch im industriellen Umfeld regelmäßig vor, also bei Protokollen, die eigentlich von Profis entworfen worden sein sollten. Insofern handelt es sich um ein zwar unbrauchbares, aber durchaus "gängiges" Verfahren. ;) Dennoch würde ich empfehlen, entweder: - das Startbyte wegzulassen, denn in dieser Form ist es nutzlos - oder dafür zu sorgen, dass es ausschließlich am Anfang eines Paketes vorkommt. Gruss Andreas
Gast
#1158273
Ich mache das immer so, dass ich eine Pause zwischen zwei Paketen von
>2Bytes festlege.
Somit landet alles im Empfangspuffer bis zum Timeout. Danach setzt die
Prüfung auf "Protokolltauglichkeit" ein.
Als innerer Aufbau des Pakets nehme ich gewöhnlich:
ByteNr | Bedeutung
-------------+---------------
0 | Header ['M']
1 | Header ['L']
2 | Anzahl Nutzdaten [0..X]
3..3+(X-1) | Nutzdaten, evtl mit weiterem inneren Aufbau
3+X | Prüfsumme, Rest zu NULL
Was spricht dagegen?
Nichts spricht dagegen, den Anfang eines Paketes mit einer Pause zu markieren. Aber die ersten beiden Bytes Deines Protokolls sind natürlich Verschwendung von Bandbreite, Speicherplatz und Prozessorzeit, da überflüssig. ;) Gruss Andreas
Gast
#1158322
>Aber die ersten beiden Bytes..
Natürlich, aber was muss, das muss...
Interessantes Thema ! Wie wäre denn ein StartByte und eine (Frame)Zählernummer. Die Wahrscheinlichkeit eine Sichere Übertragung zu haben würde sich mit jedem Frame erhöhen, denn nach jedem Frame wird doch die Kombination von StartByte und FrameZählerNr immer einmaliger und somit eindeutiger !?! Nach einer bestimmten Anzahl von DummyFrames die einfach nur zur "Feststellung der Synchronisation" dienen, sollte das ganze doch recht gut sein... Zusätzlich noch sein geXORe und gut ist ? Wie machen das denn die "richtig" guten Protokolle ??? Wie nennt sich das in der Lieteratur (NVT?) ?
Andreas Vogt wrote: > Protokolle, die ein Startbyte verwenden, das an anderer Stelle ebenfalls > auftreten kann, sind wertlos. Das ist ausgemachter Blödsinn. Die Bedeutung ergibt sich nicht durch das Byte selber, sondern durch seine Stellung im Frame. Solange es das Protokoll ermöglicht, sich wieder zu synchronisieren, unabhängig vom augenblicklichen Zustand, ist das vollkommen ausreichend. Oder willst Du behaupten, daß Du keine Wörter verstehst, wenn deren Anfangsbuchstabe im Wort nochmal vorkommt? Peter
Peter Dannegger wrote: > Das ist ausgemachter Blödsinn. > Die Bedeutung ergibt sich nicht durch das Byte selber, sondern durch > seine Stellung im Frame. > Solange es das Protokoll ermöglicht, sich wieder zu synchronisieren, > unabhängig vom augenblicklichen Zustand, ist das vollkommen ausreichend. Bist Du sicher, dass Du weißt, wovon Du redest? Es gibt viele Möglichkeiten zur Synchronisation, die Verwendung eines Startbytes ist nur eine davon. Und grundsätzlich ist für ein Protokoll auch genau eine Synchronisationsmethode ausreichend - sofern sie funktioniert. Wenn man also ein Startbyte hat, zur Synchronisierung aber weitere Berechnungen oder Untersuchungen anstellen muß, weil das Startbyte nicht eindeutig ist - was ist das Startbyte dann? Genau, überflüssiger Datenmüll. ;) Gruss Andreas
>>Wie machen das denn die "richtig" guten Protokolle ???
Die machen das mit klar defnierten Zeitfenstern. Ein 100% sicheres
Protokoll ist zb Das LIN Protokoll (aufgebohrte RS232 mit norm
definierter PHY) aus dem Automotive Bereich, damit habe ich viel zu tun
gehabt früher. Die Timeouts sind in der Spec festgeschrieben, der Slave
synchronisiert sich neu wenn abgelaufen. Für Hobby aber zu aufwendig und
überladen.
Ich halte den Timeout, also Pause zwischen Frames, für die beste
Synchronisation, mehr als ~70% Nutzdaten pro Frame sind sowieso
Wunschdenken auf Kosten der Sicherheit. IR Protokolle haben noch
weniger.
Das RFM12 Modul verwendet die Codefolge D44D als Sync Marke um den Fifo
aufzumachen, taucht beim reinen Rauschen alle paar Stunden einmal auf,
wie ich feststellte.
Bei einer Slave SPI kann man den Chipselect nehmen.
Andreas Vogt wrote: > Bist Du sicher, dass Du weißt, wovon Du redest? Ja natürlich, sonst hätte ich ja das Codebeispiel nicht gepostet. Es hat sich in der Praxis bestens bewährt und man kann zwischendurch ruhig mal den Stecker ziehen, ohne daß was schief läuft. Der Master kriegt keine Antwort, also schickt er ein Sync-Paket und dann das verlorene Paket nochmal. Warum unnötig Aufwand treiben mit Zeitfenstern, wenns auch einfacher geht. > Und grundsätzlich ist für ein Protokoll > auch genau eine Synchronisationsmethode ausreichend - sofern sie > funktioniert. Genau, ist doch meine Rede. Man könnte noch für die ganz besonders Überängstlichen, wenn das Startbyte im Datenstrom vorkommt, immer noch ein Dummybyte 0 einfügen, da ja eine Paketlänge 0 nicht vorkommt. Dieser Test ist auch nicht aufwendig, die Notwendigkeit ergab sich aber nicht. Peter P.S.: Die Überlegung mit Zeitfenstern hatte ich zuerst auch, aber da sind mir die PC-Programmierer auf die Barrikaden gestiegen. Unter Windows ein bestimmtes Zeitregime 100%-ig einzuhalten ist ne verdammt hart Nuß. Da macht jeder Programmierer lieber nen weiten Bogen rum.
Peter Dannegger wrote: > Der Master kriegt keine Antwort, also schickt er ein Sync-Paket und dann > das verlorene Paket nochmal. Sehr schön, Deine Synchronisationsmethode ist also ein Handshake. Zusätzlich verwendest Du in Deinem Code jedoch ein Startbyte mit dem Wert COMSTART. Schauen wir mal, wozu das führt: 1. Akt: Der Master sendet ein Sync-Paket, das mit COMSTART beginnt. Du empfängst das Byte, und die Zeile
1 | |
liefert true, woraufhin Du idx um eins erhöhst. Dann wird die Funktion verlassen, ich nehme an, sie wird von einer Hauptschleife in Kürze erneut aufgerufen. 2. Akt: Dummerweise herrscht gerade ein Gewitter, und alle weiteren Bytes des Sync-Paketes fallen einer EMI zum Opfer. Davon weiß Dein Programm natürlich nichts. 3. Akt: Du bist inzwischen ein paar Mal in der command-Funktion gewesen ohne etwas zu empfangen, was Dich aber nicht weiter stört. 4. Akt: Der Master beginnt jetzt mit der der Übertragung des nächsten Paketes, das wieder mit COMSTART beginnt. Auch das stört Dich zunächst nicht, Du interpetierst das Byte als Telegrammlänge. 5 Akt - der Held stirbt: Je nach dem, welchen Wert COMSTART hat, wird Dein Programm früher oder später hier:
1 | |
oder hier
1 | |
aussteigen und dieses völlig fehlerfrei und korrekt übertragene zweite Paket irrtümlich komplett wegwerfen, weil es nicht bemerken kann, dass der Fehler im vorhergehenden Paket lag. Und der arme Master muss den ganzen Quark nochmal senden... Hier haben wir also den verschärften Fall, dass ein nicht eindeutiges Startbyte nicht nur überflüssig, sondern sogar schädlich ist. Wenn Du das Startbyte weglässt und statt dessen nur das Längenbyte und den CRC prüfst, kannst Du das zweite Telegramm korrekt empfangen. ;) Gruss Andreas
Andreas Vogt wrote: > 1. Akt: Der Master sendet ein Sync-Paket, das mit COMSTART beginnt. Nein das Sync-Paket sind einfach nur 9 Dummybytes (bei max 8 Byte Daten), danach wartet der Empfänger wieder auf das Startbyte, ist also synchron. Und das sendet der Master auch nur, wenn er mal keine Quittung bekommt. > aussteigen und dieses völlig fehlerfrei und korrekt übertragene zweite > Paket irrtümlich komplett wegwerfen Ein Paket, welches asynchron ist, muß ich wegwerfen, es ist fehlerhaft. Die Übertragung soll ja sicher sein. > der Fehler im vorhergehenden Paket lag. Und der arme Master muss den > ganzen Quark nochmal senden... Ja, das ist die einzig mögliche Vorgehensweise, bei Übertragungsfehlern muß die Nachricht wiederholt werden. Z.B. der CAN-Bus macht das Retry sogar schon in der Hardware. > Hier haben wir also den verschärften Fall, dass ein nicht eindeutiges > Startbyte nicht nur überflüssig, sondern sogar schädlich ist. Warum sollte es denn schädlich sein, nur weil mal ein Paket sicherheitshalber wiederholt werden mußte? Ganz im Gegenteil, es ist ein sehr einfacher und wirksamer Synchronisationsmechanismus. > Wenn Du das Startbyte weglässt und statt dessen nur das Längenbyte und > den CRC prüfst, kannst Du das zweite Telegramm korrekt empfangen. ;) Und wie willst Du ohne Startbyte überhaupt erstmal das Längenbyte und die CRC erkennen? Peter
Gast
#1158724
>Protokolle, die ein Startbyte verwenden, das an anderer Stelle ebenfalls
auftreten kann, sind wertlos. Allerdings kommt sowas auch im
industriellen Umfeld regelmäßig vor, also bei Protokollen, die
eigentlich von Profis entworfen worden sein sollten.
Ist natuerlich Geplapper. Kommt auch auf den Kontext an. Bei einem
Master Slave System sendet eigentlich nur der Master und die Slaves
antworten. Ein Packt ist meist mit einem CRC gesichert und wird
verworfen wenn der nicht passt. So ein System resynchronisiert sehr gut.
Ich glaube Ihr redet gleichzeitig über mehrere Themen. Die sind (mit unterschiedlicher Intensität ;-): (1) framing - sicheres Erkennen von Paket-Anfang und Ende (2) Erkennung von Paketverlust und Duplikaten (3) Paketaufbau (4) Durchsatz Mein Senf dazu. Executive summary am Ende. idF: EOF = End-of-Frame Smybol SOF = Start-of-Frame Symbol idF: Frame = netto-Paket plus SOF davor plus EOF dahinter (ev plus layer-1 Schnickschnack wie Prüfsumme oder Bits zum Synchronisieren, wie die Ethernet-Preambel). grundsätzlich hat sich beim Protokolldesign durchgesetzt, dass nicht nur Anfang, sondern auch Ende von Frames unabhängig vom Paketinhalt erkannt werden kann. Vorteil: der layer2 handler braucht vom Paketinhalt nichts zu verstehen; solange er Start und Ende erkennen kann - wunderbar. Bei Multi-Protokoll-Netzen ist das gar nicht anders machbar - du kannst zB nicht jedem Ethernet-Switch upgraden, bloss weil Firma X wieder einen neuen Ethernet-Frame-Typ erfunden hat. Das Ding vermittelt Ethernet-Frames - und aus. Das ISO-Zwiebelschalenmodell hat schon seinen Sinn - die ISR braucht von der Anwendungsebene nix zu verstehen. (1) - es gibt zum Thema Framing iW zwei Wege - beide laufen sich darauf hinaus, dass ein SOF bzw EOF Symbol sicher unabhängig vom Paketinhalt erkannt werden kann. Bei bit-orientierten Protokollen wie HDLC oder Ethernet wird für sowas ein spezielles Bit-Muster verwendet, das in Nettodaten nicht vorkommen kann (siehe http://en.wikipedia.org/wiki/Bit_stuffing). Hardware erkennt/behandelt iA diese Muster schon selbsttätig und signalisiert dem Treiber daher SOF bzw EOF und damit Paketlänge. Bei Zeichen-orientierten Protokollen gibts diese Möglichkeit nicht, weil die unterste Ebene eben nur Einheiten von iA 8 bits beherrscht. Also alles, was asynchron über UARTs läuft, aber auch sowas wie BSC selig (http://einstein.informatik.uni-oldenburg.de/rechnernetze/bsc.htm). Nachdem die Zeichen, die SOF und EOF definieren, im Nettopaket vorkommen können, gibts das Erkennungsproblem, das durch "escaping" der besonderen Symbole umgangen wird. Also zB bei "transparentem" BSC ist SOF als DLE-STX definiert und EOF als DLE-ETX. Wenn DLE-ETX im Datenpaket vorkommt, wird das DLE einfach verdoppelt ("escaped") und am anderen Ende wieder auf ein einzelnes DLE eingedampft. Damit bleibt ein DLE-ETX sicher als EOF-Symbol. (2) vereinfacht gesagt: jedes Link-Protokoll, das Bestätigungen sendet, ohne zu beschreiben, was es eigentlich bestätigt, ist anfällig für das Abliefern von Duplikaten. Für das gibt es Sequenznummern - also HDLC macht das auf Ebene 2 mit Sequenzummern von 0-7 bzw 0-127 und selektiver Bestätigung. TCP machts mit Sequence Numbers, die mit Ack bestätigt werden ("habe bis hierher empfangen"). Ob das auf Ebene 2 oder auf der Transport-Ebene gemacht wird, ist eine bisschen philosophisch und auch eine Performance-Frage. Irgendwo muss es aber passieren. Bei der Internet-Architektur hat sich der Konsens durchgesetzt, sowas auf der Transport-Ebene (TCP, Layer4) zu machen und sich nicht auf zuverlässige Links zu verlassen. (3) auch für banale Protokolle rate ich bezüglich Paketaufbau: - die TLV (type-length-value) Codierung zu verwenden (http://en.wikipedia.org/wiki/Type-length-value) - alles "am Draht" ist in network byte order normiert (http://en.wikipedia.org/wiki/Network_byte_order#Endianness_in_networking) - alignment beachten. Also shorts auf Adressen modulo2 = 0, longs auf modulo 4 = 0 etc. Notfalls padding. Ein paar Bytes extra sind wurscht relativ zu dem Aufwand, den die Missachtung der Regel erzeugt. TLV deswegen, weil ein TLV Parser "versionssicher" ist - wenn der Sender neue Paketfelder sendet, erkennt und überspringt der Empfänger diese als "unerkannt", macht aber ansonsten das richtige. Network byte order: die Annahme, dass am anderen Ende immer dieselbe Rechnerarchitektur (little- oder big-Endian) sitzt, gilt vielleicht nur im Labor. Alignment: Pakete zu parsen, die das nicht beachten, bricht einem die Finger vor lauter unnötigem Hin-und Herkopieren. Siehe avrdude oder Microsoft SMB. (4) das diskutieren wir bei Interesse ein andermal. Bei 1m RS232/CAN/USB/I2C fast egal. executive summary: - Paketaufbau mit SOF und EOF verwenden. - Wenn die Link-Ebene SOF und EOF unterstützt, beide verwenden. - bei zeichenorientierten Protokollen SOF und EOF als Zeichen definieren und im netto-Paketn "escapen" (beim Senden verdoppeln, beim Empfangen das Duplikat entfernen) - Prüfsumme hinter EOF ansetzen. Prüfsummen haben mit dem Nettopaket nichts zu tun, das ist eine Ebene tiefer. - die ISR ist ein layer1 oder layer2 handler. Variante layer1: nur Zeichen in den Ringpuffer einlesen. Variante layer2: ein Frame Handler, der SOF, EOF und CRC versteht. Dann ist das interface zur nächsthöheren Ebene nicht ein Ringpuffer von Zeichen, sondern (konzeptuell) eine packet queue. In beiden Fällen hat die ISR keine Ahnung vom Aufbau des Inhalts. - Inhalt: auf der Ebene "darüber" (von mir aus in "main()") betrachten. - TLV, byte order, alignment beachten. so. genug habilitiert ;-) -Michael
Moin Michael, eine sehr gute Zusammenfassung, nur an einer Stelle muß ich widersprechen: Michael Haberler wrote: > Bei Zeichen-orientierten Protokollen gibts diese Möglichkeit nicht, weil > die unterste Ebene eben nur Einheiten von iA 8 bits beherrscht. Also > alles, was asynchron über UARTs läuft, Viele UARTs beherrschen die Übertragung von 9 Datenbits und zwar genau zu dem Zweck, Anfang und Ende von Frames zu markieren, ohne auf eine 7-bit-Kodierung heruntergehen zu müssen. Merkwürdigerweise wird diese Möglichkeit allerdings in der Praxis fast nie verwendet. Gruss Andreas
Gast
#1158871
Weil ein PC Uart das zB nicht kann ? Und auch wenn es das koennte, mit einem generischen Blockdevice Treiber obendrauf...
9 Datenbits für's Framing von Paketen ist bei UARTs absolut nicht selbstverständlich, es sei denn du arbeitest gezielt mit Paritätsfehlern. Klassisch sind 5,6,7,8 Bits, nicht aber 9. Und so geht es auch den PCs, was die Sache etwas umständlich macht (ebenso beispielsweise LPC2000). Was man bei den ganzen Spuk nicht ausser Acht lassen sollte: Bei manchen seriellen Übertragungen ist eine sichere Synchronisation auf den Byteanfang auch bei sauberer Leitung nicht unbedingt gewährleistet. Beispielsweise nicht bei UARTs. Dort ist das über etwas Pause automatisch herstellbar, bei anderen wie SPI wird es komplizierter.
Manchmal wrote: > Weil ein PC Uart das zB nicht kann ? Und auch wenn es das koennte, mit > einem generischen Blockdevice Treiber obendrauf... Vermutlich. Aber selbst bei Protokollen, die nur für die Kommunikation innerhalb definierter Busteilnehmer gedacht ist, sieht man das 9. Bit kaum.
Gast
#1158896
>>Manchmal wrote: >> Weil ein PC Uart das zB nicht kann ? Und auch wenn es das koennte, mit >> einem generischen Blockdevice Treiber obendrauf... >Vermutlich. Aber selbst bei Protokollen, die nur für die Kommunikation >innerhalb definierter Busteilnehmer gedacht ist, sieht man das 9. Bit >kaum. Wie haenge ich da einen PC zum Loggen und debuggen dran ?
Was gehen müsste: PC auf 8 Bits mit manueller Parität einstellen und Paritätsfehler als Framing-Kennung einsortieren. Bei 9 Datenbits plus Parität geht natürlich nichts mehr.
Gast
#1158909
Beim PC hat man schon lange keinen Zugriff auf das Uart mehr. Irgendwelche daemliche und duemmliche Software dazwischen mach alles kaputt. Zugegeben, es ist schon eine Weile her, aber ich hatte mal ein Projekt, da wurde waehrend dem Protokol von einem Device die Baudrate umgeschalten. Vom normalen interaktiven 2400baud modus in einen download modus mit 38400. Bei einem Uart sind das zwei register zugriffe, beim PC (Pentium 166MHz), damals unter WinNT4 dauerte dieser vorgang geschlagene 1.4 sekunden. Da war der download schon halb durch. Und deshalb haben all diese netten Waere-Gut-Features auf irgendwelchen Systemen keine Zukunft. Weil der PC da etwas auf stur schaltet.
Du musst kein Windows oder Linux verwenden. Es gibt ja FreeDOS.
Geiert nicht so mit den Bits, Jungs. Wir schreiben 2009. Die letzte Netzwerk-Architektur, wo "jedes Bit ausgenüzt wurde" (X.25), wird grade noch zum Bankomatbetrieb verwendet. Die haben das anno Schnee für die damaligen links "optimiert" und das ganze damit für heutige links unbrauchbar gemacht. Das DFN hat Millionen versenkt, um das Gegenteil zu beweisen, und hat auch aufgegeben. -- zeichenorientiertes Framing ist ok, wenn man halt nichts anderes hat. Man kann vielleicht einen esoterischen UART-Mode für Framing verwenden. Aber spätestens beim Tracen oder beim Schreiben eines Framehandlers, wo man keinen direkten HW-Zugriff mehr oder andere Hardware vor sich hat, erkennt man, dass man sich ins Knie geschossen hat. Eine Empfangsroutine, die im richtigen Zeitpunkt Framebreite umschaltet, na gute Nacht. Es gibt sogar bescheuerte Protokolle, die schalten unterwegs die Geschwindigkeit um. also - zuerst korrekt, dann schnell machen - wenn überhaupt. -Michael ps: Autobaud ist schon ok, meinte ich nicht. Aber sonst lieber nicht mit exotischen Zeichenformaten oder Geschwindkeiten "optimieren". psps: und hier noch ein Klassiker für die Badewanne: http://www.iuniverse.com/bookstore/BookDetail.aspx?BookId=SKU-000002499
Gast
#1159037
@ Michael Haberler (mah) : Kurze Frage zu >- Prüfsumme hinter EOF ansetzen. Prüfsummen haben mit dem Nettopaket >nichts zu tun, das ist eine Ebene tiefer. Meinst du damit, den Aufbau etwa so: <SOF> <Nutzdaten> <EOF> <CRC> oder eher so <SOF> <Nutzdaten> <CRC> <EOF> ?
Matthias Lipinsky wrote: > @ Michael Haberler (mah) : > > Kurze Frage zu > >>- Prüfsumme hinter EOF ansetzen. Prüfsummen haben mit dem Nettopaket >>nichts zu tun, das ist eine Ebene tiefer. > > Meinst du damit, den Aufbau etwa so: > > <SOF> <Nutzdaten> <EOF> <CRC> So - für ein layer2 Frame. > oder eher so > > <SOF> <Nutzdaten> <CRC> <EOF> So nicht. Meine Begründung ist ein bissl philosophisch. Prüfsummen über gesamte Paketinhalte sind eine Funktion der darunterliegenden ISO-Schicht, und vielleicht sogar optional in der Implementierung. Deswegen haben die im Paket selbst nichts verloren. Wenn der Empfänger die Prüfsumme nicht implementiert, wird es halt unsicherer, aber auf der Ebene darüber stimmt das Paketformat noch. Variante 2 wär daher eine "layering violation" (unter der Annahme dass alles zwischen SOF und EOF an die "Ebene darüber" geht). Das Thema war layer2 framing und da würd ichs mal so sehen. Die layer2 checksum hat im layer3 Nettopaket nix verloren, die wird eh vom layer2 handler ausgewertet. Das heisst nicht, dass unterschiedliche Ebenen eigene Mechanismen zur Fehlererkennung und Korrektur haben können. Also zB TCP checksums und Ethernet CRC32. Oder Vorwärtsfehlerkorrektur bei DVB. Bei IP mit TCP ists so: layer3 (IP) hat eine Prüfsumme nur für den IP-header. TCP hat eine Prüfsumme fürs TCP Segment. Zusammen ist das ganze Paket gesichert, auch wenn layer2 mal Fehler durchlässt. Daher funktioniert das auch mit unzuverlässigen Links. Aber IP selber hat keine Prüfsumme fürs ganze Paket (nur für den Header), weil das iA eh der layer darunter macht und der Rest in TCP und das wär dann doppelt gemoppelt. Und die Layer2 checksum ist für IP daher auch egal und optional. aber ich glaub ich drifte ein wenig ab.. -Michael
Michael Haberler wrote: >> Meinst du damit, den Aufbau etwa so: >> >> <SOF> <Nutzdaten> <EOF> <CRC> > > So - für ein layer2 Frame. > >> oder eher so >> >> <SOF> <Nutzdaten> <CRC> <EOF> > > So nicht. > > Meine Begründung ist ein bissl philosophisch. Prüfsummen über gesamte > Paketinhalte sind eine Funktion der darunterliegenden ISO-Schicht, und > vielleicht sogar optional in der Implementierung. Deswegen haben die im > Paket selbst nichts verloren. Ich denk, das ist wirklich eine philosophische Sache. Die Idee hinter dem ganzen Schichtenmodell ist ja letztendlich, dass jede drunterliegende Schicht, das 'Datenpaket' der drüberliegenden Schicht in irgendeiner Weise beim Senden einpackt und auf der Gegenseite, wenn das Paket den Stack hochwandert, die jeweils korrespondierende Schicht dieses Einpacken wieder rückgängig macht. Ob diese Schicht jetzt ein Enveolpe in der Form <SOF> Paket <EOF> <CRC> drüberlegt, oder ob das <SOF> Paket <CRC> <EOF> ist, dürfte aufs gleiche rauskommen. Mir wär die 2-te Variante sympathischer, weil EOF dann tatsächlich EOF ist und nicht nachher noch was kommt. Ist aber philosophischer Natur.
Gast
#1159101
>drüberlegt, oder ob das > <SOF> Paket <CRC> <EOF> >ist, dürfte aufs gleiche rauskommen. Mir wär die 2-te Variante >sympathischer, weil EOF dann tatsächlich EOF ist und nicht nachher noch >was kommt. Würde ich auch so sehen. Es könnte ja weiterhin folgendes sein: <SOF> Paket <CRC> <EOF> | | \|/ Paket = <SOP> Daten <EOP> ( start/end of packet für die darüberliegende Schicht )
ja, da ist was dran - das EOF packt die Prüfsumme robust mit ein und macht das frame handling etwas einfacher. Dafür ist man aber halt mit der gewählten Prüfsumme verheiratet. Wenn Du die ausn Paket rauslasst, kannst den Layer darunter (mit anderer, besserer oder gar keiner Prüfsumme) tauschen und nix fällt auf. Man kann auch overkill bei der Flexibilität betreiben, ich stehe dazu ;-) -Michael
Gast
#1159114
>Man kann auch overkill bei der Flexibilität betreiben, ich stehe dazu >;-) Vielleicht doch lieber so: <SOF> Paket <EOF> <CRC_Alg><CRC> Also vor der Prüfsumme den Berechnungsalgorithmus für selbige mitschicken ;-)
Michael Haberler wrote: > Dafür ist man aber halt mit der gewählten Prüfsumme verheiratet. Wenn Du > die ausn Paket rauslasst, kannst den Layer darunter (mit anderer, > besserer oder gar keiner Prüfsumme) tauschen und nix fällt auf. Ja, darüber hab ich auch nachgedacht. Und bin, für mich, zum Schluss gekommen, dass ich das in den meisten Fällen gar nicht haben will. Letztendlich hab ich das nämlich schon öfter beobachtet (auch bei mir selbst :-): Wenn eine Prüfsumme angegeben werden kann oder auch nicht, dann entscheidet man sich erst mal für 'nicht'. Ist weniger Arbeit und schliesslich will man das Kind ja erst mal ins Laufen bringen. Mit dem Effekt, dass es bei dem 'nicht' bleibt. Ich weiß: traurig aber gängige Praxis - brauchen wir nicht weiter diskutieren :-) > Man kann auch overkill bei der Flexibilität betreiben Flexibilität ist letztendlich immer eine Gratwanderung.
Karl heinz Buchegger wrote: > Ob diese Schicht jetzt ein Enveolpe in der Form > > <SOF> Paket <EOF> <CRC> > > drüberlegt, oder ob das > > <SOF> Paket <CRC> <EOF> > > ist, dürfte aufs gleiche rauskommen. Mir wär die 2-te Variante > sympathischer, weil EOF dann tatsächlich EOF ist und nicht nachher noch > was kommt. Das stimmt schon, allerdings muss man bei der zweiten Variante immer überlegen, ob denn nun das EOF mit in der Prüfsumme ist, oder nicht. Wenn es mit drin ist, ist die Prüfsummenberechnung etwas lästig. Ich bevorzuge daher auch die erste Variante. Vor einiger Zeit habe ich mal ein proprietäres Protokoll gesehen, bei dem der Autor sich wohl nicht für eine Variante entscheiden konnte. Der hat vor und hinter dem CRC ein EOF gesendet. War auch nicht so toll... Gruss Andreas
Andreas Vogt wrote: > Das stimmt schon, allerdings muss man bei der zweiten Variante immer > überlegen, ob denn nun das EOF mit in der Prüfsumme ist, oder nicht. Guuuuuuuutes Argument!
Andreas Vogt wrote: > Vor einiger Zeit habe ich mal ein proprietäres Protokoll gesehen, bei > dem der Autor sich wohl nicht für eine Variante entscheiden konnte. Der > hat vor und hinter dem CRC ein EOF gesendet. War auch nicht so toll... Ich vermute, da war ein Design-Komittee am Werk. -Michael
Gast
#1159392
Hallo, um mal wieder zum Thema zu kommen. Ist folgendes Konzept so ok? (Es wird von einer fehlerfreien Übertragung ausgegangen = Hobbybereich) 1. Master sendet fortlaufende 0xff zum Slave und wartet bis Slave mit ACK antwortet. 2. Master schiebt seinen Datensatz rüber und zuletzt bestätigt Slave mit ACK. 3. Master macht Verbindung wieder zu. Slave schiebt alles artig über ISR in den Ringpuffer. Main Routine prüft, ob mindestens 1 Byte im Puffer ist und ob Slave Select wieder HIGH, da Slave Select = LOW Indiz für laufende Übertragung ist. Prüft, ob dieses das Startbyte ist. Falls nicht, wird Byte verworfen, holt sich nächstes Byte. Ist es ein Startbyte zieht Main den Puffer leer und prüft, ob die Struktur des Satzes stimmt, berechnet die Position der Prüfsumme/Abschlussbytes und prüft, ob dieses ok ist, ob Endmarke da ist usw. Ist der Puffer unfertig gefüllt wartet Main N ms, ist er dann nicht gefüllt stimmt was nicht und der Buffer wird leergeluscht und resettet. Soweit ok?
Gast
#1159421
Hallo alle,
interessanter Thread - vor allem wenn man bedenkt, das das Thema
trivial ist und zig Standardimplementierungen für das Problem
existieren.
Die meisten, die ich gesehen habe, arbeiten mit einem Ringbuffer.
@Karl heinz Buchegger:
Dein cli() und sei() kannst du dir sparen. Ringbuffer müssen nicht
synchronisiert werden, wenn ausser dem Interrupt niemand den
Schreibzeiger
modifiziert!!!
Ich würde es so machen:
interrupt
{
BYTE b;
while (ReadInterface(&b))
{
ByteFifoAdd(b);
}
}
main
{
BYTE b;
MSG msg;
DWORD lastByteTimestamp;
InitMessage(&msg);
//init hardware...
while(true)
{
if (ByteFifoGet(&b))
{
lastByteTimestamp = GetTickCount();
AddToMsg(&msg, b);
if ( IsHeaderComplete(&msg) )
{
if ( IsMessageComplete(&msg)
{
ProcessMessage(&msg);
}
}
}
else
{
//Timeout, wenn länger nix empfangen
timeNow = GetTickCount();
if (timeNow-lastByteTimestamp > TIMEOUT)
{
//disable receive interrupt
//Send Break Condition
//clear Bytefifo
InitMessage(&msg);
//enable receive interrupt
}
}
}
}
Gast
#1159468
>Ringbuffer müssen nicht synchronisiert werden, wenn ausser dem Interrupt >niemand
den Schreibzeiger modifiziert!!!
@Experte
Du bist ein Byte-Experte. Laß den Schreibzeiger auf einem 8-Bitter mal
vom Typ short oder int sein. Schon geht es ohne Interruptsperre in die
Hose!
Mein Senf zum Thema:
Für die Datenübertragung nutze ich meist Datensätze im Intel-Hex-Format.
Es ist ein wenig geschwätzig, beginnt aber immer mit einem eindeutigen
Startzeichen, endet mit CR, hat eine leichte Prüfsumme und die
übertragenen Zeichen müssen im Bereich 30-39 und 41-46 liegen. Wenn das
zu langsam ist, nehme ich meist den Multiprozessormodus, der eine
eindeutige Adressierung (9.Bit) bietet.
Und dann wieder zum Thema, was im Interrupt zu bearbeiten ist. Sofern
eine Adressierung wie beim Multiprozessormodus gemacht wird, sollte die
auf jeden Fall im Interrupt gehandhabt werden. Ebenso auch bei anderen
Adressierungsarten. Wichtig dabei ist nämlich, daß bei neuer, eigener
Adresse die Ringpuffer gelöscht werden (kann/sollte/müßte), um klare
Startbedingungen für den neuen Datensatz zu schaffen.
Ausnahmen bestätigen die Regeln!
Gast
#1159667
@Gasst Na da hat ja einer aufgepasst - aber nicht so ganz wie mir schein, denn int ist auf einem 8Bitter 8Bit breit. Ansonsten ist die ganze Diskussion Kindergarten. Der Vorteil des Ringbuffers ist, dass man uns,chronisiert lesen und schreiben koennen moechte, ausser man machts wie gasst... Und wer nicht weiss wies geht, liest irgend ein Buch oder in der howto Abteilung in diesem Forum!!!
Experte wrote:
> denn int ist auf einem 8Bitter 8Bit breit.
Nö. Der C Standard schreibt vor, dass ein int mindestens 16 Bit groß zu
sein hat.
Gast
#1159743
>dass ein int mindestens 16 Bit groß zu sein hat.
... und auf ein 8-Bitter mit zwei Bytezugriffen beschrieben/gelesen
wird, die von einem Interrupt problemlos unterbrochen werden können.
@Experte
Viel Spaß bei der Fehlersuche von sporadisch auftretenden Fehlern. Oder
noch besser, laß Deinen Kunden suchen :-)
Im Standard steht zwar ein Mindestwertebereich für "int" drin, es steht aber nicht drin, dass du für alle im Interrupt verwendeten Variablen mindestens "int" zu verwenden hast.
Gast
#1159886
@gasst Also wenn du deinen Ringbuffer so programmieren willst, dass du Leser und Schreiber synchronisieren mußt, hast du grundlegend was nicht verstanden. Im Normalfall nimmt man für Lese- und Schreibindex einen Typ, der der Datenbusbreite des entsprechenden Prozessors entspricht. Da muss nix synchronisiert werden. Wenn du, gasst, auf deinen 8-Bittern damit Probleme bekommst, weil du den falschen Typ gewählt hast, weiß ich auch nicht... ...und jetzt komm nicht mit dem Frage-Argument "und was ist, wenn ich mehr als 256 Elemente ablegen will..?" ...kriegste eh nich in den RAM von deinem 8Bitter.
Gast
#1159899
>mehr als 256 Elemente.. >...kriegste eh nich in den RAM von deinem 8Bitter. mit WinZip schon ;-)
Gast
#1159902
>...kriegste eh nich in den RAM von deinem 8Bitter.
Da hast Du natürlich Recht. Ein 8-Bitter hat ja maximal 256 Byte RAM.
Hatte ich nicht dran gedacht.
Experte wrote: > mehr als 256 Elemente ablegen will..?" > ...kriegste eh nich in den RAM von deinem 8Bitter. Mumpitz, der ATmega1284P hat z.B. 16384 Byte internen SRAM. Soll ich dann deinetwegen 16128 Byte ungenutzt lassen? Viele 8-Bitter (8051, AVR) können 64kB Daten oder Code linear zugreifen. Sie benötigen aber dazu 2 Zugriffe, um den Pointer zu laden. Peter
Peter, dein Ironiedetektor müsste mal geputzt weden ;-).
Gast
#1159991
Er denkt tatsächlich nur in Bytes... 256 Elemente != 256 Bytes, gasst
Hallo, ein PIC Spezi hier? ich weiss nichtr genau, ob ich in der ISR das BF Bit abfragen sollte oder nicht? Die ISR kommt ja nur, wenn ein Byte da ist. Andere Frage: Was ist mit dem Overflow Bit? Muss das berücksichtigt werden? Wird bei Stau auf der Datenrutsche gesetzt
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
Christian J. wrote: > ein PIC Spezi hier? Welcher denn? PIC10,12,16,18,24,30,32,33 ... Und andere Threads besser nicht hijacken. Peter
Peter Dannegger wrote: > Christian J. wrote: >> ein PIC Spezi hier? > > Und andere Threads besser nicht hijacken. Ähm, ich glaube, diesen hier hat Christian J. selbst aufgemacht. ;) Gruss Andreas
Andreas Vogt wrote: >> Und andere Threads besser nicht hijacken. > > Ähm, ich glaube, diesen hier hat Christian J. selbst aufgemacht. ;) Ich bezog das auch mehr auf das Thema. Man kriegt einfach mehr Antworten, wenn man einen neuen Thread macht, wenn das Thema wechselt. Peter
Gast
#1174816
Hallo Peter, also, nach 3 Nächten bin ich nun fertig, ich arbeite mit 2 Fifos, einen für Empfang, einen fürs Senden des Antwortframes. In der Hauptroutine stehen 3 Funktionen: Check_Input_Fifo (Daten in globalen Struct extrahieren) AnalzyeData (Was muss gemacht werden? Bereite Antwort vor) SendData (Antwort senden) Die ISR zieht sich aus dem tx_Fifo ihre Daten oder schreibt sie in den rx_Fifo. Was mich nur nervt ist, dass man wohl nicht drumherum kommt die ISR ab und zu abzuschalten, damit sie nicht auf die Fifos zugreift während diese gerade gefüllt oder abgearbeitet werden. Dann kann es passieren, dass es zu "Leerbytes" im Antwortframe kommt, wenn der Master schlichtweg in dem Moment anfragt wo der Antwortframe noch nicht ganz zusammengebaut wurde. Was ich suche ist eine anerkannte Struktur solcher Sachen als Vorlage, die sich möglichst am ISO/OSI Modell orientiert und nicht zu umfangreich sein sollte. ich möchte die SPI Routinen völlig unabhängig überall nutzen können. Hast Du da was? Oder jemand anders?
Christian J. wrote: > Was mich nur nervt ist, dass man wohl nicht drumherum kommt die ISR ab > und zu abzuschalten, damit sie nicht auf die Fifos zugreift während > diese gerade gefüllt oder abgearbeitet werden. Dann kann es passieren, > dass es zu "Leerbytes" im Antwortframe kommt, wenn der Master > schlichtweg in dem Moment anfragt wo der Antwortframe noch nicht ganz > zusammengebaut wurde. Das mit dem Empfang habe ich ja mit dem Framepuffer gelöst. Der wird erst angefaßt, wenn das Frameende erkannt wurde. Das Senden kann man so lösen, daß zwar der Sendepuffer mit den Daten befüllt wird, aber der In-Index wird erst aktualisiert, wenn das letzte Byte eines Frame im Puffer ist. Der Sende-FIFO darf ja erst senden, wenn die In-/Out-Indexe unterschiedlich sind. Ich arbeite mit Index, statt Pointer, da meine Puffer <256 Byte sind und somit der Code deutlich kleiner und auch keine Interruptsperre nötig ist für den atomaren Zugriff. Peter
Gast
#1174902
Hallo Peter, alles klar, verstehe ich so, dass ich zwei Flags einführen muss: f_ValidDataInRXFIFO f_ReadyToSendTXFIFO Das letzte Flag ist für die ISR gedacht, damit die erst den Fifo auslutscht, wenn der Datensatz darin voll zusammen gebaut wurde. Beim RX FIFo lese ich immer aus, wenn delta(write,read) grösser 1 ist, also mindestens ein 1 byte vorliegt. Kommen die anderen dann nicht binnen Timeout wird alles resettet. Ich sehe schon die nächste Nacht vor mir in der meine Frau nachts um 2 hochkommt und fragt wann ich ins Bett komme :-)
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.