Forum: Compiler & IDEs Umfangreiches Objekt in C


von Heiko M. (may_day)


Angehängte Dateien:

Lesenswert?

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.

von P. S. (Gast)


Lesenswert?

Und was ist die Frage?

von Heiko M. (may_day)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Heiko M. (may_day)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

> 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.

von Heiko M. (may_day)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Oliver (Gast)


Lesenswert?

>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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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
Noch kein Account? Hier anmelden.