Hallo,
auf der jetzigen Firmware läuft die UART ohne einen Ringpuffer.
Zwischen der Applikation und der UART Implementierung soll ein
Ringpuffer
für die Senderichtung eingesetzt werden. Der Ringpuffer soll so
gestaltet sein, dass dieser insgesamt 10 Frames mit jeweils einer
maximalen Länge von 300 Bytes aufnehmen kann. Gibt es hier in dem Forum
Personen die mir eventuell bei der Umsetzung helfen könnten?
Du hast ein Array aus Frames, einen Read-Index und einen Write-Index.
Wer in den Puffer schreiben möchte, schreibt in das Array an die Stelle,
auf die der Write-Index zeigt und incrementiert diesen dann (zeigt er
hinter das letzte Element, wird er auf das erste Element gesetzt).
Lesezugriffe nutzen den Read-Index.
Es gibt eine Mehrdeutigkeit zu beachten, wenn Read- und Write-Index
gleich sind. Dann ist der Ringpuffer entweder voll oder leer, abhängig
davon, ob der letzte Zugriff ein Schreib- oder ein Lesezugriff war.
Diese Mehrdeutigkeit kann man vermeiden, indem man das Array ein Element
größer macht als notwendig und immer einen Frame frei lässt.
So, jetzt bist du dran.
Darf ich fragen wo du für dich ein Problem siehst? Auf den ersten Blick
ist das doch etwas Speicher plus ein kleiner Zustandsautomat.
Woher weis dein Puffer in einem seriellen Datenstrom was ein Frame ist?
Muss er das überhaupt wissen?
Viel Erfolg & halt uns auf dem laufenden
Hauspapa
Problem:
In der Zeit wo die UART noch beschäftigt ist die Daten zu versenden,
kann möglicherweise wieder ein neues Frame von der
Ethernet-Schnittstelle ankommen. Somit muss dann dieses Frames in einen
Ringpuffer kopiert werden. Wenn die UART wieder bereit ist soll der
Inhalt vom Ringpuffer über die UART gesendet werden.
leo schrieb:> Problem:>> In der Zeit wo die UART noch beschäftigt ist die Daten zu versenden,> kann möglicherweise wieder ein neues Frame von der> Ethernet-Schnittstelle ankommen. Somit muss dann dieses Frames in einen> Ringpuffer kopiert werden. Wenn die UART wieder bereit ist soll der> Inhalt vom Ringpuffer über die UART gesendet werden.
Warum meinst Du, stellt das ein Problem dar? (Es geht mir mit der Frage
darum, Dich auf eine strukturierte Denkweise hinzulenken).
Zunächst einmal wäre es gut, sich zumindest eine Übersicht zu
verschaffen, was die Peripherie eines uC überhaupt kann.
Der UART kann full-duplex arbeiten. Das bedeutet er kann gleichzeitig
empfangen und senden. Es gibt nur eine Operation, welche im Zusammenhang
mit dem Datentransfer nicht simultan erledigt werden kann: Nämlich die
Daten übernehmen bzw. übergeben. (Ob da noch DMA dazwischen ist, spielt
dabei eine Nebenrolle).
Deine Angaben sind auch widersprüchlich: Im Eröffnungspost schreibst Du
nur von UART, nun aber kommt auch die Ethernet-Schnittstelle ins Spiel.
Falls Du über Ethernet empfängst, aber über UART sendest, dann
verursacht das eher kein Problem, dass mittels eines Ringbuffers zu
lösen ist. Es besteht allerdings die Möglichkeit, dass die
Ausführungszeiten schon so knapp sind, dass ein Ringbuffer tatsächlich
das Projekt rettet, ohne das ein Redesign oder auch nur ein anderer uC
nötig ist.
Dann wieder malloc: Es ist an sich klar, dass Du Code oft nicht einfach
nur Eins-Zu-Eins übernehmen kannst. Es hängt ein bisschen davon ab, was
Deine Vorkenntnisse und Erfahrungen sind und in welchem Kontext Du hier
Deine Frage stellst: Bist Du Schüler ohne Erfahrungen ist das was
anderes als wenn Du Student im Praktikum bist oder gar auf PCs schon
viel Code geschrieben hast, nur auf uCs noch wenig Erfahrung hast.
An sich wird man malloc auf uCs vermeiden und feste Buffer definieren.
Es wären einmal ausführliche Angaben über Situation (sowohl zum Projekt
als auch zu Deiner Person) nützlich.
Andernfalls solltest Du wenigstens schreiben, wobei Du nun konkret Hilfe
brauchst. Bitte bedenke, dass hier in der Regel Hilfe zur Selbsthilfe
geleistet wird.
Hier im Forum, in der Wikipedia und im Netz wirst Du an vielen Stellen
den vollständigen Code für einen Ringpuffer finden.
Also: "Suchet, so werdet Ihr finden".
1) Die Adressen in Deinem Ringpuffer bilden einen Kreis.
2) Definiere einen Schreib- und einen Lesezeiger, die jeweils den
Schreib- und Leseadressen in Deinem Puffer entsprechen.
3) Nach jedem Schreiben in den Puffer wird der Schreibzeiger um 1 (oder
n) inkrementiert.
4) Nach jedem Lesen aus dem Puffer wird der Lesezeiger um 1 (oder
n)inkrementiert.
5) Ist vor dem Lesen der Lesezeiger gleich dem Schreibzeiger, dann ist
der Puffer leer.
6) Ist vor dem Schreiben der Schreibzeiger gleich dem Lesezeiger, dann
wird der Puffer überlaufen.
Zu Deinem Code:
1) Wenn Du kein Malloc verwenden willst, dann muss der Aufrufer den
Speicher mit übergeben. Das ist das sauberste. Am einfachsten, indem du
ein Makro zur Verfügung stellst, dass bei gegebener Fifo-Größe den
Gesamt-Platzbedarf in Bytes errechnet. Der Anwender legt dann ein
Static-Array damit an.
2) Die Operation % (X+1) ist nicht sehr performant. Spielt hier keine
Rolle, aber um den Charakter des Überlaufs besser zu zeigen wäre besser:
1
2
buffer->writeIndex++;
3
/* Bei size+1 umlaufen lassen um rIdx==wIdx zu vermeiden, ist OK */
4
if(buffer->writeIndex>buffer->size)
5
{
6
buffer->writeIndex=0;
7
};
3) Warum du immer nur ein Byte kopierst ist mir auch nicht klar. Eine
Nachricht sollte doch immer komplett kopiert werden
4) Der Read-Index hat am Ende von Append nichts zu suchen. Im Gegenteil:
Am Anfang muss geschaut werden, ob genügend Platz drin ist. Darum ist es
in der Regel einfacher, neben rIdx und wIdx noch einen cnt mitzuführen.
Man spart damit das "size+1" und kann direkt sagen, ob und wieviel drin
ist (beim Lesen) oder ob genug Platz zum Schreiben ist. Die Alternative
((wr-rd+(wr<rd?size:0)) zerfällt nur bei size=2^n und unsigned-typen in
praktisch nichts.
Mach ich auch immer so. Ich habe zwei FIFOs/Ringspeicher. Einer für Rx
und einer für Tx. Dann eine getchar() für Rx und eine putchar() für Tx.
Im Interrupt der UART habe ich dann das jeweilige Gegenstück zu getchar
und putchar.
Achim S. schrieb:> Die Alternative> ((wr-rd+(wr<rd?size:0)) zerfällt nur bei size=2^n und unsigned-typen in> praktisch nichts.
Genau. Da nimmt man halt als Puffergröße statt 3001 eben 4096 Bytes und
gut isses. Es schadet sicher nix, wenn bis zu drei Frames mehr in den
Puffer passen als gefordert und es macht den Code erheblich schneller.
Solange man die 1095 Bytes zusätzlichen Puffer nicht dringend irgendwo
anders benötigt, spricht absolut nix dagegen, das so zu machen.
Hallo ich bin dabei die folgende Implementierung auf dieser Seite
https://www.google.de/url?sa=t&rct=j&q=&esrc=s&sou... umzusetzen.
Ich habe nun die Speicherreservierung statisch gestaltet. Allerdings
sehe ich im Debug Mode, dass die Variablen von der Struktur
ringbuffer_handler_t nicht initialisiert werden.
Abgesehen davon ,das dein Beispiel immer noch nicht komplett ist:
In deiner createFIFO() Funktion vermischt du den eigentlichen Handler
für den Buffer mit dem Datenspeicher.
Ich kann mit nicht vorstellen, das das ganze im Original mit malloc auch
so aussah.
Schau dir noch einmal an, welche Menge speicher für welchen Zweck mit
malloc reserviert wird und versuche das entsprechend mit statischen
Variablen nachzubauen.
Für den kompletten Speicher habe ich 6000 Bytes vorgesehen. Deshalb das
globale char Array Memory. Das FIFO hat eine maximale Größe von 400 * 10
Bytes.
400 --> Framegröße von 400 Bytes
size --> Frame Anzahl = 10
Natürlich funktioniert das nicht, weil dein *buffer nicht dort hin
zeigt, wo du denkst.
http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/012_c_zeiger_007.htm
Vorher war der FiFo Pointer ebenfalls nicht korrekt aber das hast du ja
scheinbar korrigiert, auch wenn du immer noch nicht weißt wie ;-)
Lies dir einfach den Link von mir durch. Insbesondere, wie man einen
Pointer auf ein Array verwendet (was du in der Funktion createFIFO()
Code 2 mal versuchst)
Ok die Zeiger sind falsch.
Die Adresse von buffer in der create FIFO Funktion ist nicht gleich mit
der Adresse von buffer in der appendFIFO Funktion. Mir fällt keine
Lösung ein.
Ich zitiert mal aus meinem Link:
[Zitat]
Durch die Anweisung
1
ptr=element;
wird dem Zeiger ptr die Adresse des Arrays element übergeben. Dies
funktioniert ohne den Adressoperator, da laut ANSI-C-Standard der
Array-Name immer als Zeiger auf das erste Array-Element angesehen wird.
Hier sind der Beweis und das Beispiel dazu:
[/Zitat]
Leider kann dir dein Compiler für deine Fehler nicht auf die Finger
klopfen, da du ihm durch den cast sagst: "das ist schon so richtig, ich
weiß was ich tue"
@ leo (Gast)
>Ich bekomme das Problem nicht gelöst.
Was nicht verwunderlich ist, wenn man diese ganzen hektischen Unsinn
hier sieht. Zum Programmieren braucht es Ruhe und Zeit.
Wenn du mit Pointern und deren Arithmetik noch nicht so vertraut bist,
dann benutz doch einfach einen ganz klassischen Zähler zum Signalisieren
der Lese- und Schreibposition...
@Vincent Hamp (vinci)
>Wenn du mit Pointern und deren Arithmetik noch nicht so vertraut bist,>dann benutz doch einfach einen ganz klassischen Zähler zum Signalisieren>der Lese- und Schreibposition...
Man könnte ja auch fertigen, getesteten Quelltext nutzen, der schon
angeboten wurde. Muss man aber nicht . . .
Im Netz habe ich bereits schon recherchiert. Diese Lösung würde zu
meinem Problem passen. Ich benötige einen Ringpuffer der Nachrichten in
variabler Länge Speichert und wieder löscht.
leo schrieb:> Die ganze Zeiger und Adresse usw da kommt man durcheinander. Da versteht> man dann nix mehr
Sorry aber wenn man sein Handwerkszeug nicht beherrscht dann kann man
auch kein Gesellenstück bauen.
Dann bleibt nur
Falk B. schrieb:> Man könnte ja auch fertigen, getesteten Quelltext nutzen, der schon> angeboten wurde. Muss man aber nicht . . .
Aber man muss dann zumindest soviel vom Programmieren verstehen daß man
den Code versteht und anpassen kann falls nötig.
Programmieren ist halt nicht wie backen mit Dr. Oetger. Fertigmischung,
Milch, rühren, Backofen, lecker (vieleicht).
Das muss man halt LERNEN.
leo schrieb:> Bin hier quasi alleine.
Wie soll man das verstehen? Alleine beim Hausaufgaben machen oder
alleine in einer Firma beim Entwickeln einwes Programms für das Kunden
dann Geld zahlen sollen?
leo schrieb:> Könnt ihr mir da nicht noch eine weitere Hilfestellung geben?
kann ich leider nicht da selbst C lernender. Kann dir aber sagen was ich
gemacht habe.
Mir ist dieser ganze Ringbufferkram mit seine Nebenwirkungen und Risiken
zu hoch. Das ist zwar alles jetzt wunderbar kompakt aber eben gewollt
hochkomplex.
Deshalb habe ich den "Ringbuffer" durchgeschnitten und zwei Queues draus
gemacht.
1
// globals in interrupt
2
unsignedcharLeftBuffer[85];// Left/Right Names for easy distinguishing)
3
unsignedcharRightBuffer[85];
Darauf dann zwei Zeiger
volatile unsigned char *pDataBuffer = &LeftBuffer;
volatile unsigned char *pRcvBuffer = &RightBuffer;
Wenn die queue voll ist schalte ich das ganze einfach um
1
2
pRcvBuffer=&RightBuffer;
3
pDataBuffer=&LeftBuffer;
Das ist jetzt nicht so ausgefuchst wie ein Ringbuffer und braucht auch
mehr Speicher. Dafür blicke ich da durch kann mir die Daten im Debugger
anschauen und die Bearbeitung ist auch wesentlich schneller da ich nur
die Prüfung auf Message Ende (wird eh gebraucht) und overflow habe.
X4U schrieb:> Wenn die queue voll ist schalte ich das ganze einfach um>> pRcvBuffer = &RightBuffer;> pDataBuffer = &LeftBuffer;
Sorry da fehlt noch Daten links -> rechts :
>> Warum tut dann das ganze nicht auf dem Mikrocontroller ?
Weil Du das Array namens "Memory" als Auto-Variable definiert hast,
welche bei der letzten Geschweiften-Klammer-Zu vernichtet wird. Das
heißt, der Speicher wird wieder freigegeben und kann für andere Aufgaben
genutzt werden. Der µC nutzt ihn für anderes und stürzt ab, Dein
PC-Programm zufälligerweise nicht - vermutlich, weil Du nur den einen
kleinen Teil des µC-Programms auf den PC portiert hast.
Dir fehlt gundlegendes Wissen in C, dass Du Dir dringend aneignen
solltest. Sonst würdest Du die Speicherklasse "static" kennen und solch
einen Anfängerfehler vermeiden.
Schreibe also
1
staticcharMemory[6000];
statt
1
charMemory[6000];
und teste erneut.
Das erlöst Dich aber nicht von der dringlichen Aufgabe, erst einmal C
grundlegend zu lernen als solch eine eher fortgeschrittene
Programmierübung zu lösen.
Deine read/write Index Berechnung ist falsch, es kann also schon mal gar
nichts funktionieren! (FIFO ist immer "leer")
Und wieso castest du einen Pointer vom Typ userdata auf eine Variable??
Erzeuge doch gleich eine lokale Variable vom Typ userdata im Modul. Dann
sparst du dir auch gleich die ganze Pointerschubserei in den Argumenten.
Mehr als 1 FIFO brauchst du ja nicht.
mfg
buffer->writeIndex wird hier an 2 Stellen manipuliert, nämlich einmal
links vom Gleichheitszeichen und einmal durch den Postinkrement rechts.
Welche der beiden Operationen gewinnt, ist undefiniert und kann auf
jedem System anders sein. Dasselbe gilt für alle anderen
Sequence-Point-Warnings.
Die memcpy-Warnung stammt (hoffentlich) daher, dass Du hier lediglich
bei Posten des Codes vergessen hast, das dafür notwendige Include dem
Code voranzustellen, das Include aber in Deinem Source doch drin ist.
Fazit:
Den Code, den Du hier irgendwo geklaut hast, taugt nichts. Schreibe Dein
eigenes FIFO-Modul. Wenn Du das nicht hinbekommst, empfehle ich Dir,
erstmal C systematisch von der Pike auf zu lernen und Dich dann (nach
ein paar Monaten) nochmal diesem Problem zu widmen.