Gude Bewohner der Mikrocontroller-Welt! Ich entwickle schon seit einige Jahren mit Mikrocontrollern von Atmel AVR 8-Bit. Bisher habe ich meine Programme immer sehr konventionell in C programmiert, abgesehen von meiner Diplomarbeit. Jetzt möchte ich ein Projekt angehen, dass mich mehr nicht ganz konventionelle Programmierung fordert. Mein Problem: Ich sende über die serielle Schnittstelle unterschiedlich lange Datenpakete und Zusatzinfos. Innerhalb der Übertragung wird auch die Länge übergeben, die noch übertragen wird. So sieht ein Datenpaket aus: Header, Länge, Modus, Daten, Checksum Nun möchte ich, dass jedes Datenpaket in einem eigenen Objekt gespeichert wird. Die Objekte sind durch eine Liste verlinkt um die Reihenfolge zu wahren. Sprich jedes Objekt hat als Inhalt den Modus und die Daten und einen Zeiger auf das nächste Objekt, oder auf NULL. Der Ablauf soll dann so folgen: Datenempfang beginnt (Header erkannt): Objekt wird erstellt: Länge wird empfangen: Länge in Objekt speichern Modus wird empfangen: Modus in Objekt speichern Daten werden empfangen: Array der Daten mit der Größe Länge-1 erstellen, Daten reinballern. Checksum prüfen. Anschließend wird das Objekt noch ein wenig verarbeitet und anschließend gelöscht (Zeiger neu verlinken) und der Speicher frei gegeben. Für diese Datenpaket Queue habe ich nun also ein neues Modul angefangen und möchte auch dort lokal die Speicherung vor nehmen. Zur Verfügung steht mir ein ATMega88. Jetzt habe ich mir schon so viel durchgelesen und ausprobiert aber komme einfach nicht dahinter und hoffe auf eure Hilfe. Anbei habe ich mal meine ersten Bröckchen meiner QueueHandler angehängt. Danke für die Hilfe.
Peter Stegemann schrieb:
> Und was ist die Frage?
Die Frage ist, wie mache ich diese Liste mit der dynamischen
Speicherverwaltung möglich? Sodass alles in einem Modul ist.
Heiko May schrieb: > Nun möchte ich, dass jedes Datenpaket in einem eigenen Objekt > gespeichert wird. Warum? > Die Objekte sind durch eine Liste verlinkt um die > Reihenfolge zu wahren. Warum? Wie soll sich denn die Reihenfolge ändern? > Datenempfang beginnt (Header erkannt): Objekt wird erstellt: > Länge wird empfangen: Länge in Objekt speichern > Modus wird empfangen: Modus in Objekt speichern > Daten werden empfangen: Array der Daten mit der Größe Länge-1 erstellen, > Daten reinballern. > Checksum prüfen. Warum? Was spricht dagegen, das Paket einfach insgesamt zu speichern? Eventuell noch Pointer anlegen auf die einzelnen Objekte im Paket, wenn sie nicht der Reihe nach bearbeitet werden sollen. > Anschließend wird das Objekt noch ein wenig verarbeitet und anschließend > gelöscht (Zeiger neu verlinken) und der Speicher frei gegeben. Wozu den Speicher freigeben? Du brauchst soviel Speicher, wie Pakete hintereinander eintreffen dürfen. Und den organisiert man als FIFO, d.h. der UART-Interrupt pakt alles in die FIFO und das Main holt sich daraus immer ein Paket, wenn es geparst werden soll. Der FIFO-Speicher muß also immer verfügbar sein und das Main braucht nur Speicher für ein Paket. Peter
Also... Bytes, die als Daten ankommen müssen noch weiter verarbeitet werden. Je nach Modus, zwischenzeitlich kann es sein, dass ein neues Datenpaket ankommt, bevor das letzte verarbeitet wurde. Die Daten können zwischen 2Bytes und 3KB groß sein. Aus diesem Grund will ich den Speicher jedes mal wieder frei geben um immer genügend zu haben, wenigstens für zwei Objekte. In der Liste gestapelt kann ich sie nacheinander abarbeiten. Darum dieses ganze kuddelmuddel.
Auf einem PC würde man das mit dynamischer Programmierung machen, so wie du das andeutest. Ob das auf einem µC so sinnvoll ist, ist eine andere Sache. Das Problem: Du hast nicht viel Speicher und wenn der alle ist, dann ist er alle. Solange du den verbrauchten Speicher statisch allokierst hast du zumindest zur Compilezeit eine kleine Kontrolle über den Speicherverbrauch. Machst du aber alles dynamisch, dann hast du nur sehr wenig Kontrolle darüber und vor allen Dingen: Was soll den dein µC machen, wenn der Speicher tatsächlich alle ist? Auch malloc kann nicht zaubern. Ganz im Gegenteil, die Probleme werden durch einen malloc meist nur noch größer :-) Auch spielt auf einem µC mit seinen sehr begrenzten Speicherresourcen die Speicherfragmentierung oft schon eine gewaltige Rolle: theroetisch hättest du zwar zb noch 4KB frei, aber nicht in einem Stück und deswegen geht die Allokierung von 2KB Speicher schief. Aber seis drum. Gehen wir einmal davon aus, dass du dynamisch allokieren willst. Wo liegt jetzt genau dein Problem. Eine Liste dynamisch zusammenzubauen ist ja nicht gerade Raketentechnik. Jedes Lehrbuch über Datenstrukturen beschreibt so etwas im Detail. PS:
1 | typedef union { |
2 | uint8_t LengthHigh_u8; |
3 | uint8_t LengthLow_u8; |
4 | }Length_t; |
das wird wohl nichts mit einer union.
> Die Daten können zwischen 2Bytes und 3KB groß sein.
Insbesondere dann ist eine dynamische Lösung keine allzu gute Idee,
wegen der Fragmentierung.
Beispiel:
Sagen wir, du hast insgesamt 4 KiByte zur Verfügung. Es kommt ein Paket
mit 2 KiByte und eines mit 2 Byte. Das mit 2 KiByte wurde bearbeitet und
der Platz wieder frei gegeben. Das mit 2 Byte aber noch nicht, als ein
Paket mit 3 KiByte reinkommt. Was passiert: die Platzanforderung für das
neue Paket schlägt fehl, obwohl du eigentlich insgesamt genug Platz
dafür hättest.
Mein größes Problem dabei ist die Tatsache, dass ich es in ein Modul gießen wollte und nicht damit klar kam wie ich die Objekte der Liste aufrufe. Aber allein schon diese Diskussion von uns beiden und dein letzter Beitrag läßt mich wiedermal zu dem Schluss kommen mein Konzept nochmals zu überlegen. Irgendwie muss das ganze wohl einfacher umgesetzt werden, damit es realisierbar auf einem ATMega88 wird. Mit einer festen Kommunikationsgröße. Danke dir.
Heiko May schrieb: > Mein größes Problem dabei ist die Tatsache, dass ich es in ein Modul > gießen wollte und nicht damit klar kam wie ich die Objekte der Liste > aufrufe. Aber allein schon diese Diskussion von uns beiden und dein > letzter Beitrag läßt mich wiedermal zu dem Schluss kommen mein Konzept > nochmals zu überlegen. Meiner Meinung nach, solltest du einen ganz wesentlichen Punkt nicht ausser Acht lassen: Du hast extreme Größenunterschiede zwischen deinem kleinsten und deinem größten Paket. Das kann dir enorme Schwierigkeiten machen (eben wegen der Fragmentierung). Auf der anderen Seite kannst du auch nicht 5 Pakete mit ihrer Maximallänge fix allokieren. Das würde den verfügbaren Speicher sprengen. Ich würde so ansetzen: Ich definiere mir einen Speicherchunk mit, sagen wir mal 64 Bytes. Von diesen Chunks hat das System (hausnummer) 100 Stück zur Verfügung (in einem Array zusammengefasst). Kommt nun eine Datenpaket herein, so werden ihm, ja nach Größe Chunks zugewiesen, soviele wie es eben braucht um das Datenpaket komplett abzulegen. Jeder Chunk enthält auch noch einen unsigned char, der die Chunknummer des nächsten Chunks angibt. Damit kannst du ein 3K grosses Datenpaket in mehreren Teilen speichern, wobei diese Teile nicht aufeinanderfolgend im Speicher liegen müssen. Du vermeidest damit das Problem der Fragmentierung. Soviele Chunks wie gerade frei sind, soviele Daten kannst du auch sicher speichern. Die Eingangsqueue würde ich nach Möglichkeit fix dimensionieren, um malloc rauszuhalten. Deine Queue kann vielleicht 5 oder 6 Objekte vorhalten. Diese Chunkallokierung verschlimmert zwar die Verarbeitung eines Pakets, aber das ist oftmals nicht gar so tragisch wie es sich im ersten Moment anhört. Oft geht man ja einfach nur sequentiell durch die Bytes eines Datenpakets durch und dann lässt sich ein Ptr++ einfach durch eine Funktion ersetzen, die die Chunks noch mitberücksichtigt. > Irgendwie muss das ganze wohl einfacher umgesetzt werden, damit es > realisierbar auf einem ATMega88 wird. Mit einer festen > Kommunikationsgröße. Ich fürchte das Gegenteil ist bei deinen Zahlen der Fall: Es muss eher komplizierter werden. Aber ohne dynamische Speicher-Strukturen direkt zu verwenden, bzw. so zu verwenden dass du sie unter Kontrolle hast.
>Bytes, die als Daten ankommen müssen noch weiter verarbeitet werden. Je >nach Modus, zwischenzeitlich kann es sein, dass ein neues Datenpaket >ankommt, bevor das letzte verarbeitet wurde. Die Daten können zwischen >2Bytes und 3KB groß sein. ... >Zur Verfügung steht mir ein ATMega88. Atmel schreibt zum Mega88: >SRAM (Bytes) 1024 Preisfrage: Was passt da nicht zusammen? Oliver
Oliver schrieb: >>Zur Verfügung steht mir ein ATMega88. > Atmel schreibt zum Mega88: >SRAM (Bytes) 1024 Ooops. Hab ich bisher überlesen. Na dann wirds ja richtig eng.
Heiko May schrieb: > zwischenzeitlich kann es sein, dass ein neues Datenpaket > ankommt, bevor das letzte verarbeitet wurde. Und genau dazu nimmt man eben einen FIFO. > Die Daten können zwischen > 2Bytes und 3KB groß sein. Dann vergiß mal ganz schnell Dein kuddelmuddel, Du hast nämlich nur 1kB RAM. Die einzige Chance ist ein FIFO. Und aus diesem holst Du die Bytes einzeln raus und verarbeitest sie sofort. Bei 1kB RAM würde ich max 512Byte FIFO einrichten, d.h. Du kannst temporär bis 512 Byte in Verzug sein, ohne das es kracht. Du mußt sie aber im Mittel schneller verarbeiten, als sie ankommen. Peter
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.