Hallo, ich zermartere mir hier schon das Hirn, aber ich komme nicht so richtig auf die Lösung. Das Problem: Ich bekomme über einen Busanbindung Daten. Diese Daten können Strings (Text bis max 64 Byte länge), Kommandos (im Prinzip auch Text bis max 64 Byte länge), Ausgang An/Aus (Ausgang Nr 1-255 und Status An Aus) usw... Die Busdaten werte ich in einem ISR aus und würde die nun gerne in einen Puffer schreiben und dort von der Mainloop abarbeiten lassen, da dies etwas dauern kann. So weit so gut. Für das Handling des Softwareupdate muss ich sowieso einen Puffer von ca 130 Byte vorhalten, den würde ich gerne hierfür nutzen. Also dachte ich an einen Ringpuffer. ABER: Dann kann ich meine Strings oder Kommandos nicht mehr einfach mit "strcmp" prüfen und auswerten, da die Daten ja "über das Ende zum Anfang gehen können". Auch gefällt mich nicht, das ich im ISR die Max 64 Byte von dem Seriellen Bus puffer in den Ringpuffer kopieren muss. Das dauert doch schon recht lange ?! Wenn ich das vor der Verarbeitung erst wieder in einen anderen "in reihe liegenden Puffer" kopieren muss, braucht das ja wieder Zeit und Speicher für die Max 64 Byte. Vielleicht denke ich auch nur zu Kompliziert oder sehe das zu kritisch ? Achja das ganze läuft auf einem Mega16 bzw Mega32, programmiert mit avr-gcc. Vielleicht hat einer eine gute Idee. Danke Juergen Sachs
Wie wäre es mit einem Pointer-Array in dem du die Startadressen deiner Strings speicherst?!
mehrfacher STK500-Besitzer wrote: > Wie wäre es mit einem Pointer-Array in dem du die Startadressen deiner > Strings speicherst?! Wenn ich deinen Vorschlag richtig verstehe, habe ich aber immer noch das Problem, das die eintreffenden Daten nicht am Stück in den Ringpuffer passen, sondern wieder vorne rann müssen, Bsp: Ringpuffer ist jetzt nur 20 Byte groß um es zu vereinfachen: - Kommando 1 mit 12 Byte im ISR empfangen und in Ringpuffer ab Pos 1(0) kopiert. - Hauptprogramm beginnt Auswertung Kommando 1. - Kommando 2 mit 5 Byte im ISR empfangen und in Ringpuffer ab Pos 13 (12) kopiert. - Hauptprogramm beendet Auswertung Kommando 1 und gibt "Speicher frei". - Hauptprogramm beginnt mit Auswertung Kommando 2 - Kommando 3 mit 7 Byte im ISR empfangen und in Ringpuffer ab Pos 18-20 und 1-5 kopiert. - Hauptprogramm beendet Ausführung Kommando 2 und gibt "Speicher frei". usw..... So würde ich da im Moment mit einem Ringpuffer verstehen und lösen. Da hilft mir auch das Pointer Array nicht weiter ?!?! Aber die Verarbeitung von Kommando 3 im obigen Beispiel ist schon schwieriger, da es ja "Zerlegt" ist. Eben Pos 18-20 und Pos 1-5 durch den Ringpuffer. Eine Möglichkeit wäre natürlich auch, immer alle Daten aufzurücken. Also wenn vorne 10 Byte frei werden alle nachfolgenden Bytes um 10 Byte vorrücken. So bleibt am Ende immer ein Zusammenhängender Speicher übrig. Aber das ist ja "kopier" Wahnsinn :-( Das muss doch schöner gehen.
A) Nach ein paar Mega Zeilen Code siehst du das nicht mehr so eng ;-) B) Bau dir eine eigene "strcmp" die direkt mit dem Ringbuffer umgehen kann. C) Lass doch die ISR die 130 Byte direkt versorgen. Hänge dann noch die 64 Bytes dran und du hast sogar einen 194 Bytes Ringbuffer. Alles eine Sache der "Verwaltung". Kommuniziert wird von ISR zu main nur Index und Länge, und main gibt den Bereich über ein Flag (zB. NULL setzen der beiden Variablen) wieder frei.
Wenn Du nicht mit jedem Byte sparen mußt, danmn würde ich z.B. ein Feld von 10 Strings zu je 64 Byte anlegen. Dieser Strings verwendest Du zylkisch, wie einen Ringbuffer. Ein Pointer zeigt jeweils auf den nächsten freien String in den die seriellen Kommandos abgelegt werden und auf den String, den der Mainloop als nächstes Abarbeiten soll. Klaus
@Werner B. A) naja auf dem PC ist mir das auch egal, nur eben beim AVR... B) Eine komplette "Toolchain" aus Defines, Funktionen usw... Aber so richtig gefallen will mir das nicht gefallen. C) Hauptbahnhof ! Wie soll ich die 64 Byte hinten anhängen ? @Klaus Falser Bei nur 1kb Ram des Mega16 und 2kB des Mega32 ist das nicht drin. Sonst wäre das die Lösung meiner wahl. Der Punkt ist, da ich das ganze im Moment als "kernel" über eine API Entwickelt habe. Die eigentliche Nutzanwendung kommt später je nach Anwendung dazu. Daher will ich so viel Ram wie möglich sparen. Da hier das Handling so einfach wie Möglich sein soll, hätte ich Ringpuffer gerne vermieden. Natürlich könnte ich im ISR das ganze erst in den Ringpuffer kopieren (passt am meisten rein). Max 64 Byte, das dürften so an die 250 Zyklen verdonnern (geschätzt) und wenn die App so weit ist, das ganze wieder in einen Zweiten 64 Byte Puffer kopieren, nochmal an die 250 Zyklen weg :-( Dort kann das ganze dann in Ruhe verklaubt werden ohen Rücksicht auf Verluste.
Deine zweite Lösung ist auch nicht soo gut. Du brauchts 64 Byte zusätzlich in der ISR und wieder 64 in der Applikation, zusätzlich zum Ringbuffer (+ die Rechenzeit). Da kannst Du Dir z.B. ein Feld mit 4 oder 5 Feldern zu 64 Byte nach meiner Lösung leisten, sind ca. 300 Byte. Wieviele Befehl im voraus soll man denn überhaupt speichern können. Wenn die befehle zu schnell kommen, muss man sie halt verwerfen oder einen negative Antwort an den Sender zurückgeben. So etwas mußt Du Dir sowieso überlegen.
Jürgen Sachs wrote: > Das Problem: > Ich bekomme über einen Busanbindung Daten. Diese Daten können Strings > (Text bis max 64 Byte länge), Kommandos (im Prinzip auch Text bis max 64 > Byte länge), Ausgang An/Aus (Ausgang Nr 1-255 und Status An Aus) usw... Ich verwende dazu einen Linearpuffer. Die UART schreibt die Bytes ab dem Anfang hinein und wird das Endezeichen (0x0A) erkannt, dann wird es durch 0 ersetzt und ein Bit gesetzt. Das Main testet das Bit und kann nun immer ab Pufferanfang das Kommando parsen. Ist es fertig, dann ruft es eine Löschroutine auf, die das Kommando entfernt, d.h. alle zwischenzeitlich empfangenen Bytes wieder an den Pufferanfang schreibt. Der große Vorteil ist, man braucht nur einen einzigen Puffer. Und man muß viel weniger umkopieren (nur alle zwischenzeitlich empfangenen Bytes). Peter
Anbei mal ein Beispielcode für den Linearpuffer. Er kann sogar gemischt Text und Binärdaten empfangen. Mit kbhit stellt man fest, ob ein (binär-)Byte empfangen wurde, mit getchar holt man es ab. Um festzustellen, ob ein komplettes Kommando im Puffer steht, ruft man rx_get_cmd auf. Wenn man das Kommando fertig geparst hat, ruft man rx_clear_cmd auf. Daß hier der UART-Empfang mit Polling erfolgt, ist egal. Als Interrupt geht es genauso gut. Peter
Im Prinzip ist das was Du beschreibst das was ich im Moment tue. Alles in den Seriellen Puffer im ISR und dann Verarbeiten in Mainline. Da kommt mein Timing aber bei vielen Kommandos gehörig durcheinander :-( Daher muss ein Teil der Auswertung mit in die ISR, der Rest des Anwendungsprogramms in Main und hat "alle Zeit der Welt" dazu für Auswertung und LCD Beschriftung. Auf das Protokoll habe ich keinen Einfluss, ist vorgegeben und nicht mehr veränderbar. Ich sehe mir dein Beispiel Morgen mal in Ruhe an. Danke soweit für das Beispiel. Peter Dannegger wrote: > Jürgen Sachs wrote: > >> Das Problem: >> Ich bekomme über einen Busanbindung Daten. Diese Daten können Strings >> (Text bis max 64 Byte länge), Kommandos (im Prinzip auch Text bis max 64 >> Byte länge), Ausgang An/Aus (Ausgang Nr 1-255 und Status An Aus) usw... > > Ich verwende dazu einen Linearpuffer. > Die UART schreibt die Bytes ab dem Anfang hinein und wird das > Endezeichen (0x0A) erkannt, dann wird es durch 0 ersetzt und ein Bit > gesetzt. > Das Main testet das Bit und kann nun immer ab Pufferanfang das Kommando > parsen. > > Ist es fertig, dann ruft es eine Löschroutine auf, die das Kommando > entfernt, d.h. alle zwischenzeitlich empfangenen Bytes wieder an den > Pufferanfang schreibt. > > Der große Vorteil ist, man braucht nur einen einzigen Puffer. > Und man muß viel weniger umkopieren (nur alle zwischenzeitlich > empfangenen Bytes). > > > Peter
Ich verwende einen "Doppelpuffer" und brauche dank diesem überhaupt keine Bytes umkopieren. Ein "ActiveBuffer"-Flag mit den Werten (A, B) gibt jederzeit an, welcher der beiden Puffer gerade zum Abspeichern benutzt wird. Zu Anfang werden die eintreffenden Bytes in Puffer A gespeichert, beginnend bei Position 0. Ist das Kommando vollständig empfangen, wird ein "CmdComplete"-Flag gesetzt. Außerdem wechselt der Wert von ActiveBuffer. Weitere eintreffende Zeichen werden ab jetzt in Puffer B gespeichert (dabei der alte Inhalt überschrieben), wieder beginnend bei Position 0. In der Main wird das "CmdComplete"-Flag geprüft. Ist es 1, wird der Inhalt des gerade nicht aktiven Puffers ausgewertet, und anschließend das "CmdComplete"-Flag wieder gelöscht. Die Auswertung eines Kommandos darf somit sinnvollerweise gerade soviel Zeit in Anspruch nehmen, wie das komplette Übermitteln eines Kommandos (genauer: des nächsten) dauert.
>> B) Bau dir eine eigene "strcmp" die direkt mit dem Ringbuffer >> umgehen kann. Das ist die Variante, die ich favorisieren würde. > B) Eine komplette "Toolchain" aus Defines, Funktionen usw... > Aber so richtig gefallen will mir das nicht gefallen. Na, ja. Sooo schlimm ist das nun auch wieder nicht. So viele Funktion benutzt man ja beim Kommando-Parsen nun auch wieder nicht. Und die die benutzt werden, sind eigentlich ziemlich trivial selbst zu implementieren. > Da hier das Handling so einfach wie Möglich sein soll, hätte ich > Ringpuffer gerne vermieden. Wovon sprichst du? Zugriffe auf Ringbuffer sind ziemlich simpel. Du darfst nur nicht den Fehler machen und aus der Anwendung heraus direkt auf den Buffer zuzugreifen. Schreib dir 2 Zugriffsroutinen (eine für Schreiben und eine für Lesen) und ab da kann es der restlichen Applikation völlig egal sein, ob da jetzt ein Ringbuffer zugrunde liegt oder nicht. > Natürlich könnte ich im ISR das ganze erst in den Ringpuffer kopieren > (passt am meisten rein). Max 64 Byte, das dürften so an die 250 Zyklen > verdonnern (geschätzt) Flexibilität hat eben ihren Preis
Karl heinz Buchegger wrote: >> B) Eine komplette "Toolchain" aus Defines, Funktionen usw... >> Aber so richtig gefallen will mir das nicht gefallen. > > Na, ja. Sooo schlimm ist das nun auch wieder nicht. > So viele Funktion benutzt man ja beim Kommando-Parsen > nun auch wieder nicht. Und die die benutzt werden, sind > eigentlich ziemlich trivial selbst zu implementieren. > Nein, soo schlimm ist das nicht, aber man muss es machen :-) >> Da hier das Handling so einfach wie Möglich sein soll, hätte ich >> Ringpuffer gerne vermieden. > > Wovon sprichst du? Zugriffe auf Ringbuffer sind ziemlich simpel. > Du darfst nur nicht den Fehler machen und aus der Anwendung heraus > direkt auf den Buffer zuzugreifen. Schreib dir 2 Zugriffsroutinen > (eine für Schreiben und eine für Lesen) und ab da kann es der > restlichen Applikation völlig egal sein, ob da jetzt ein > Ringbuffer zugrunde liegt oder nicht. Das ist der Einzige Weg wie ich gehen würde. Alles andere ist zu Fehleranfällig ! >> Natürlich könnte ich im ISR das ganze erst in den Ringpuffer kopieren >> (passt am meisten rein). Max 64 Byte, das dürften so an die 250 Zyklen >> verdonnern (geschätzt) > > Flexibilität hat eben ihren Preis Das ist Richtig, aber 250 Zyklen (worst case) + Protokollverarbeitung im ISR, uff. Da hätte der Vorschlag von AVRFan schon was für sich. Ich splitte meinen 130 Byte Puffer in 2 Virtuelle Puffer. Nachteil in 99% der Zeit wird der nie richtig genutzt und ich habe nur 2 Puffer statt mehrer, je nach Stringgrößen. Hummmmmm. Ich drehe mich im Kreis.
>Das ist Richtig, aber 250 Zyklen (worst case) + Protokollverarbeitung im >ISR, uff. Aber andererseits: 250 Zyklen sind nun auch nicht so grausam viel. Ein mit 8 MHz getakteter AVR braucht dafür 31.25 µs. Oder willst Du noch zehn extrem zeitkritische Hochgeschwindigkeitstasks parallel auf Deinem Controller laufen lassen? Also mein Vorschlag wäre, einfach mal zu prüfen, ob Du mit den 250 (oder auch 500) Zyklen in Deinem System vielleicht schlicht leben kannst. Ich schätze die Wahrscheinlichkeit, dass dem so ist, als recht hoch ein.
Jürgen Sachs wrote: > Im Prinzip ist das was Du beschreibst das was ich im Moment tue. > Alles in den Seriellen Puffer im ISR und dann Verarbeiten in Mainline. > Da kommt mein Timing aber bei vielen Kommandos gehörig durcheinander :-( Kannst Du mal erläutern, was da durcheinanderkommt? Die Kommandos werden in genau der gleichen Reihenfolge abgearbeitet, wie sie eintreffen und nicht durcheinander. Ich benutze jedenfalls keine Ringpuffer mehr, die sind mir zu aufwendig. Ich hatte mal nen Ringpuffer für genau 256 Byte geschrieben und die Parserfunktionen so, daß sie nur das Lowbyte des Pointers hochzählen. Damit kann man über das Ende hinaus parsen. Ist mir dann aber zu tricky gewesen. Peter
Hallo, für ein Projekt hatte ich damals einen Ringbuffer benutzt und kam auch bei den Punkt an, wenn er über die Buffergrösse kommt das Frame wieder zusammen zubasteln. Ich hab dann ein Flag gesetzt und die Information in einen anderen Zwischenbuffer gespeichert und bei Frameende den Rest dazu kopiert. Die zwei Buffermethode war Rechenzeitintensiv und auch sehr Speicherintensiv. Nachdem ich lese das Peter nur ein Linearenbuffer nutzt werde ich für zukünftige Sachen die gleiche Methode verwenden, weil er länger Mikrocontroller programmiert als ich.
>Die zwei Buffermethode war Rechenzeitintensiv und auch sehr >Speicherintensiv. Speicherintensiv ist klar - man braucht ja zwei Puffer. Aber wieso rechenzeitintensiv??
>Aber wieso rechenzeitintensiv??
Die Instructions zum Flag setzen, auswerten und von ersten zum zweiten
Buffer kopieren.
>Die Instructions zum Flag setzen, auswerten Äh... die Instructions zum Flag setzen und auswerten? Die müssen doch in jeder Lösung gegenwärtig sein, oder seh ich das falsch? >und von ersten zum zweiten Buffer kopieren. Ich frage mich, ob Du das Prinzip richtig verstanden hast, denn es wird nie etwas vom ersten zum zweiten Puffer kopiert - das ist ja gerade der Vorteil dieser Methode. Beide Puffer werden abwechselnd zum Abspeichern der eintreffenden UART-Bytes, sowie zum Lesen plus Auswerten der Daten verwendet. Während das Programm die UART-Bytes in Puffer A speichert, kann es unbehelligt und in aller Ruhe die Daten aus Puffer B lesen, weil die sich ja nicht ändern. Wenn das Kommando vollständig ist, ist auch das Programm mit dem Auswerten fertig. Das Programm macht sich nun an die Auswertung der Daten in Puffer A, während es neu eintreffende UART-Bytes in Puffer B speichert. Welchen der beiden Puffer das Programm zu irgendeinem Zeitpunkt zum Speichern und welches zum Auslesen benutzen soll, weiß es anhand der "ActiveBuffer"-Variable, die nach jedem Kommando seinen Zustand wechselt (A -> B -> A -> B -> A -> ...).
Hat einer von Euch schon Erfahrungen mit "Rotierenden" Puffern auf dem AVR ? Alle Informationen werden hinten angehängt, sofern noch Platz da ist. Der Puffer wird von vorne abgearbeitet und bearbeitete Daten gelöscht indem die anderen Daten nach vorne kopiert werden (aufrücken). Der Vorteil hierbei ist, das alle Daten weiterhin am Stück vorliegen. Nachteil: es muss viel kopiert werden 1. Kopieren der Daten in den Bearbeitungspuffer im ISR 2. Bearbeiten der Daten in Mainloop 3. Löschen der Daten durch aufrücken der nachfolgenden Daten in Mainloop Grob geschätzt dürfte der Kopieraufwand ungefähr mit dem des Ringpuffers gleich kommen IM ISR. Das was ich mir dabei nicht so gefällt, ist das nachrücken der Daten. Während dieser Zeit müsste ich vermutlich die ISR sperren ?! Das wäre beim Ringpuffer ja auch nicht anders ?!?! Die Ausnutzung wäre genau so gut wie beim Ringpuffer, der Overhead etwas größer... Aber sehr einfaches Handling in Mainloop mit Std. Funktionen. Was mir nur nicht ganz klar wäre ist ob ich wirklich während des ganzen Aufrückvorgangs den ISR sperren müsste. Also worst case um 130 Byte aufrücken, wenn am Anfang ein Kommando mit 1 Byte war. Wenn ich dabei die ISR sperre dürfte das für ca 130 Byte lesen + 130 Byte schreiben + 130 Zyklen für loop + 130 Zyklen hochzählen. 520 Zyklen, UPS. wie gesagt worst case bei vollem Puffer. Sollte nie der Fall werden.
AVRFan wrote: > Beide Puffer werden abwechselnd zum Abspeichern der eintreffenden > UART-Bytes, sowie zum Lesen plus Auswerten der Daten verwendet. > Die Idee ist gut. In meinem Fall ist es eben so, das ich Daten im Umfang von 5 Byte bis zu 70 Byte (durch den Protokoll Overhead) kommen. Je nach "Wert" geht die Verarbeitung schneller oder langsamer. Ein Relais schalten geht eben schneller als die Daten für ein Display aufzubereiten. Der Nachteil der Wechselpuffer wäre hier eben das ich nur 2 Puffer hätte. Theoretisch könnte ich aber viel mehr Pakete im Puffer speichern. Sonst würde ich das so umsetzen ! Beim Ringpuffer oder Rotierenden Puffer habe ich eben maximale Ausnutzung des Puffers, aber auch den größten Overhead.
Bei einem Ringpuffer kopierst du keine Daten "nach vorne". Du hast zwei Pointer. (Lese/Schreibpointer) die immer auf eine bestimmte Position im Ring zeigen. Wenn du also aus dem Puffer lesen willst, liest du immer über den Lese-Pointer und inkrementierst den. Willst du was schreiben, benutzt du den Schreib-Pointer. Die Zeiger müssen zu Programmstart natürlich übereinander liegen. Da muss nichts kopiert werden. Ein Ringpuffer ist für die meisten Anwendung eigentlich die beste Lösung.
Simon K. wrote: > Bei einem Ringpuffer kopierst du keine Daten "nach vorne". > Das ist mir klar. Er hat eben nur den Nachteil das die Daten nicht fortlaufend zur Verfügung stehen. Beim Rotierenden Puffer währe das der Fall. Allerdings ist hier Overhead höher. Im Moment betrachte ich beide Lösungen. Mal sehen was mehr Vorteile bringt.
>In meinem Fall ist es eben so, das ich Daten im Umfang von 5 Byte bis zu >70 Byte (durch den Protokoll Overhead) kommen. Ich versteh Dich schon. Die Länge Deiner Pakete ist variabel. >Je nach "Wert" geht die Verarbeitung schneller oder langsamer. Ein >Relais schalten geht eben schneller als die Daten für ein Display >aufzubereiten. OK, Relaisschalten geht unbestreitbar schneller, aber möglicherweise geht auch beides noch viel schneller als die Übertragung eines einzigen Bytes über den UART! Also mal rechnen: Bei 9600 Baud dauert der Transfer eines Bytes 1/960 s = 1.041666 ms. Das sind bei 8 MHz Taktfrequenz 11333 Zyklen, in denen ein AVR ca. 7000 Instruktionen abarbeiten kann. Right? Wenn nun die Aufbereitungsroutine für die Displaydaten diese 7000 Instruktionen gar nicht ausschöpft, macht Dein Ziel, kurze Befehle auch "schnell" abzuarbeiten, keinen Sinn. Bist Du sicher, dass Deine Kommandos nicht vielleicht unabhängig von ihrer Länge praktisch alle instantan abgearbeitet werden, aufgrund der schieren Leistungsfähigkeit eines AVR? Das mit dem Ringpuffer bei variabler Paketlänge ist ein Problem, das keine "gute" Lösung besitzt (bei fester Paketlänge ist der Dual-Puffer eine). Willst Du das Parsen mit den normalen Stringfunktionen erledigen, bist Du zum gelegentlichen zeitraubenden Umkopieren gezwungen. Im worst case musst Du viel umkopieren. Willst Du nichts umkopieren, musst Du spezielle Stringfunktionen verwenden, die mit den "misaligneten" Strings im Puffer (vorderer Stringteil am Addressbereichsende, hinterer am Anfang) durch eine zusätzliche Ebene der Indirektion klarkommen. Diese Funktionen werden dann (etwas) langsamer sein. So schaut's aus :-)
Jürgen Sachs wrote: > Simon K. wrote: >> Bei einem Ringpuffer kopierst du keine Daten "nach vorne". >> > Das ist mir klar. > > Er hat eben nur den Nachteil das die Daten nicht fortlaufend zur > Verfügung stehen. > Beim Rotierenden Puffer währe das der Fall. Allerdings ist hier Overhead > höher. Achso, das verstehst du unter rotierendem Puffer. Nene, sowas ist Murks meiner Meinung nach. Wofür brauchst du denn unbedingt, dass Daten fortlaufend zur Verfügung stehen? Wenn du beispielsweise einen String über den UART ausgeben willst, bastelst du dir halt eine UARTSendStringCircular (o.ä.) Funktion, die immer ein Byte aus dem Ringpuffer holt und sendet. Und falls man am Ende des Ringpuffers ist, wird vorne wieder angefangen.. Null Problemo. Btw, Beispielsweise In Karl Heinz "Gedankenimplementierungen" eines RingBuffers hat er auch immer eine Schnittstelle vorgesehen um Bytes vom Ringpuffer abzuholen. Dementsprechend müsste die UART String Sende Funktion immer nur über die passende Funktion ein Byte aus dem Ringpuffer holen und rausschieben (Solange, bis man die String-Ende-0 hat)
AVRFan wrote: >>In meinem Fall ist es eben so, das ich Daten im Umfang von 5 Byte bis zu >>70 Byte (durch den Protokoll Overhead) kommen. > > Ich versteh Dich schon. Die Länge Deiner Pakete ist variabel. > >>Je nach "Wert" geht die Verarbeitung schneller oder langsamer. Ein >>Relais schalten geht eben schneller als die Daten für ein Display >>aufzubereiten. > > OK, Relaisschalten geht unbestreitbar schneller, aber möglicherweise > geht auch beides noch viel schneller als die Übertragung eines > einzigen Bytes über den UART! Also mal rechnen: Bei 9600 Baud dauert > der Transfer eines Bytes 1/960 s = 1.041666 ms. Das sind bei 8 MHz > Taktfrequenz 11333 Zyklen, in denen ein AVR ca. 7000 Instruktionen > abarbeiten kann. Right? Wenn nun die Aufbereitungsroutine für die > Displaydaten diese 7000 Instruktionen gar nicht ausschöpft, macht Dein > Ziel, kurze Befehle auch "schnell" abzuarbeiten, keinen Sinn. Bist Du > sicher, dass Deine Kommandos nicht vielleicht unabhängig von ihrer Länge > praktisch alle instantan abgearbeitet werden, aufgrund der schieren > Leistungsfähigkeit eines AVR? Baudrate ca 21kBit (ja ist so krumm) Der Punkt ist dabei das ich die Datem vom Protokoll her Empfange und diese dann weitergebe an die Anwendungsschicht. Bei Relais an aus nie Problem, bei Kommandos ist das viel aufwendiger. Im Prinzip ein permanenter Stringvergleich bis ein Treffer kommt. Das dauert bei vielen Kommandos in Folge zu lange und ich komme aus dem Tritt und ich kann nicht mehr schnell genug, also dem Timing Entsprechend, Antworten. Daher muss jetzt die Komplette Kommandoverarbeitung abgetrennt und die Aufwendige Protokollauswertung in den ISR. Bisher hängt diese auch in der Mainloop. Nur der Empfangspuffer wird bisher im ISR befüllt. Theoretisch hier alles so Empfohlen im Forum. Nur Pufferbearbeitung im ISR, Rest in Mainloop. > Das mit dem Ringpuffer bei variabler Paketlänge ist ein Problem, das > keine "gute" Lösung besitzt (bei fester Paketlänge ist der Dual-Puffer > eine). Willst Du das Parsen mit den normalen Stringfunktionen erledigen, > bist Du zum gelegentlichen zeitraubenden Umkopieren gezwungen. Im worst > case musst Du viel umkopieren. Willst Du nichts umkopieren, musst Du > spezielle Stringfunktionen verwenden, die mit den "misaligneten" Strings > im Puffer (vorderer Stringteil am Addressbereichsende, hinterer am > Anfang) durch eine zusätzliche Ebene der Indirektion klarkommen. Diese > Funktionen werden dann (etwas) langsamer sein. > > So schaut's aus :-) Ja, keine Vorteile ohne Nachteile. Wieso hat ein AVR auch keine 32k Ram :-)
Simon K. wrote: > Jürgen Sachs wrote: >> Simon K. wrote: >>> Bei einem Ringpuffer kopierst du keine Daten "nach vorne". >>> >> Das ist mir klar. >> >> Er hat eben nur den Nachteil das die Daten nicht fortlaufend zur >> Verfügung stehen. >> Beim Rotierenden Puffer währe das der Fall. Allerdings ist hier Overhead >> höher. > > Achso, das verstehst du unter rotierendem Puffer. Nene, sowas ist Murks > meiner Meinung nach. Das kommt auf den Anwendungsfall an... > Wofür brauchst du denn unbedingt, dass Daten fortlaufend zur Verfügung > stehen? Hauptanwendung: Ich bekomme Texte die ich als Kommando Interpretieren muss. Also Hauptsächlich Text suchen und vergleichen. Eventuell einen Teil des Textes aufs Display bringen. Aber was mit dem Kommando passiert ist wieder Anwendungssache. Im Prinzip bin ich dabei mir eine saubere API zu bauen, wo ich nur noch meine Anwendung aufsetze. Genau das habe ich jetzt schon, im Prinzip. Nur bekomme ich ab und zu Timingprobleme... Der Grund ist klar, daher muss ich die Zwischenpuffer. > Wenn du beispielsweise einen String über den UART ausgeben willst, > bastelst du dir halt eine UARTSendStringCircular (o.ä.) Funktion, die > immer ein Byte aus dem Ringpuffer holt und sendet. Und falls man am Ende > des Ringpuffers ist, wird vorne wieder angefangen.. Null Problemo. > Nur noch Text parsen und reagieren, aber wie gesagt ist das Anwendungssache, darauf habe ich keinen Einfluss. Deswegen mache ich mir ja die Mühe das Optimale zu finden. Da ich die Anwendungsschicht nicht im Griff habe, also noch nicht kenne, ist es wichtig das "richtig" zu machen.
Simon K. wrote: > Achso, das verstehst du unter rotierendem Puffer. Nene, sowas ist Murks > meiner Meinung nach. Ist das gleiche, was ich Linearbuffer genannt habe und überhaupt kein Murks. Er braucht auch nur ganz wenig Umkopiererei. Im Mittel müssen ja die Kommandos langsamer kommen, als sie ausgewertet werden. Damit können nicht sehr viele neue Bytes kommen, wenn ein Kommando abgearbeitet wird und nur diese müssen an den Anfang kopiert werden. Es steht natürlich der gesamte Puffer für eine höhere Spitzenlast zur Verfügung. > Wofür brauchst du denn unbedingt, dass Daten fortlaufend zur Verfügung > stehen? Ich habe keine Lust, mir ein strchr, strcmp, atoi, atof usw. alles selber zu basteln. Wozu sind Bibliotheken da, wenn man sie nicht nutzen kann? Peter
Peter Dannegger wrote: > Simon K. wrote: > >> Achso, das verstehst du unter rotierendem Puffer. Nene, sowas ist Murks >> meiner Meinung nach. > > Ist das gleiche, was ich Linearbuffer genannt habe und überhaupt kein > Murks. > > Er braucht auch nur ganz wenig Umkopiererei. Im Mittel müssen ja die > Kommandos langsamer kommen, als sie ausgewertet werden. Damit können > nicht > sehr viele neue Bytes kommen, wenn ein Kommando abgearbeitet wird und > nur diese müssen an den Anfang kopiert werden. > Es steht natürlich der gesamte Puffer für eine höhere Spitzenlast zur > Verfügung. > Ahja, dann hab ich das falsch Verstanden. Das mit den Bytes sehe ich eben so. >> Wofür brauchst du denn unbedingt, dass Daten fortlaufend zur Verfügung >> stehen? > > Ich habe keine Lust, mir ein strchr, strcmp, atoi, atof usw. alles > selber zu basteln. > Wozu sind Bibliotheken da, wenn man sie nicht nutzen kann? > So geht es mir auch. Warum das Rad neu erfinden. Zudem ist dann jeder in der Anwendungsschicht von MEINEN Routinen abhängig für den Ringpuffer. Wie gesagt ich drehe mich hier im Kreis zwischen Vor- und Nachteilen.
Ich werde mich mal den Ring und Rotierenden (Linear) Puffer demnächst genauer widmen. Hab voraussichtlich ab Ende nächster Woche Urlaub, da hab ich mal wieder Zeit :-) Ich kann ja mal Melden was dabei raus kam. Gruss und Danke soweit
Man kann bei linearem Puffer durchaus vermeiden, die Interrupts für erhebliche Zeit sperren zu müssen. Indem man das zweimal macht. Der erste Durchlauf schiebt bei offenen Interrupts. Der zweite schiebt bei gesperrten Interrupts nur noch jene wenigen Bytes, die zwischenzeitlich aufgelaufen sind und aktualisiert den Schreibindex. Paranoide Gemüter reduzieren die für Interrupts gesperrte Zeit weiter, indem sie das ganze solange wiederholen, bis zwischenzeitlich nichts mehr auflief, was die gesperrte Zeit auf diesen Test und den Update vom Schreibindex reduziert.
Andreas Kaiser wrote: > PS: Die kritische Phase beschränkt sich in der verschärften Fassung auf > einen compare-and-exchange Befehl. Nein, es sind mehrere Befehle. Das aufgelaufene Byte muß kopiert werden, der Index erhöht und dann dem Schreibpointer zugewiesen werden. Ich kann mir nicht vorstellen, daß es ne CPU gibt, die diese vielen Instruktionen in einem Befehl schafft. Beim 8051 könnte das so aussehen:
1 | void rx_clear_cmd( void ) // remove last command |
2 | {
|
3 | u8 dst, src; |
4 | |
5 | dst = 0; |
6 | src = 0; |
7 | while( Rx_buff[src++] ); // search end of previous command |
8 | do{ |
9 | while( src < rx_in ){ |
10 | Rx_buff[dst] = Rx_buff[src]; // copy to Rx_buffer start |
11 | dst++; |
12 | src++; |
13 | }
|
14 | if( EA == 0 ) // if interrupts disabled |
15 | rx_in = dst; |
16 | EA = ~EA; // toggle interrupt enable |
17 | }while( EA == 0 ); // until interrupts enabled again |
18 | }
|
Beim GCC muß man noch drauf achten, daß rx_in volatile ist. Peter
Peter Dannegger wrote: > Nein, es sind mehrere Befehle. Das aufgelaufene Byte muß kopiert werden, > der Index erhöht und dann dem Schreibpointer zugewiesen werden. Nein. In C dürfte die verschärfte Variante etwa so aussehen (ad hoc Version):
1 | uint8_t buffer[]; |
2 | volatile int writer; // buffer write index |
3 | |
4 | void consume(int n) |
5 | {
|
6 | bool flag; |
7 | do { |
8 | int wx = writer; |
9 | memmove(buffer+0, buffer+n, wx-n); |
10 | int new_writer = wx - n; |
11 | cli(); |
12 | flag = (wx == writer); |
13 | if (flag) |
14 | writer = new_writer; |
15 | sei(); |
16 | } while (!flag); |
17 | }
|
> Ich kann mir nicht vorstellen, daß es ne CPU gibt, die diese vielen > Instruktionen in einem Befehl schafft. Was man braucht: compare R1 with [memory] store R2 to [memory] if equal Ebendies leistet der CMPXCHG Befehl von x86. Sowas wird routinemässig benötigt, um in SMP-Betriebssystemen verkettete Listen verarbeiten zu können ohne das jedesmal mit Semaphoren absichern zu müssen. Und daher gibt es bei x86 auch eine Doppelwortversion davon (CMPXCHG8B).
@Andreas Kaiser Tja, was soll man dazu noch sagen :-( Du solltest Dir vielleicht überlegen, daß man ne Diskussion zerstört, wenn man seinen Artikel löscht, auf den jemand geantwortet hat. Peter
Besser:
1 | uint8_t buffer[]; |
2 | volatile int writer; // buffer write index |
3 | |
4 | void consume(int n) |
5 | {
|
6 | int offset = 0; |
7 | bool flag; |
8 | do { |
9 | int wx = writer; |
10 | int count = wx - (offset+n); |
11 | memmove(buffer+offset, buffer+offset+n, count); |
12 | offset += count; |
13 | cli(); |
14 | flag = (wx == writer); |
15 | if (flag) |
16 | writer = offset; |
17 | sei(); |
18 | } while (!flag); |
19 | }
|
Peter Dannegger wrote: > Du solltest Dir vielleicht überlegen, daß man ne Diskussion zerstört, > wenn man seinen Artikel löscht, auf den jemand geantwortet hat. Versehentlich. Sorry. Lautete: PS: Die kritische Phase beschränkt sich in der verschärften Fassung auf einen compare-and-exchange Befehl. Wenn es den ununterbrechbar gibt (x86) oder er äquivalent implementierbar ist (PowerPC), dann ist das gleichermassen interruptfest und tauglich für SMP/multithreading ohne irgendwas explizit sperren oder blockieren zu müssen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.