Servus,
Ich möchte dem Benutzer die Möglichkeit geben, eine Art Setup zu machen,
dessen Einstellungen dann im EEPROM gespeichert werden.
Im Program sollen diese Daten dann dazu verwendet werden, verschiedenen
Struct-Arrays Elemente hinzuzufügen.
In den Structs selbst verwende ich einerseits Zeiger, die teilweise
wieder auf elemente anderer Struct-Arrays zeigen als auch variablen mit
einem, zwei oder drei Bit Länge.
Jetzt bin ich mir nicht ganz sicher, wie ich zur Laufzeit malloc /
realloc auf Struct-Elemente anwende, also welche Größe ich den
Funktionen übergebe.
Funktioniert da?
Ich würde im Quellcode bereits einen "Prototypen" jedes Arrays
erstellen, funktioniert dann realloc auch noch? Habe gelesen daß das
eigentlich nur für Blöcke funktioniert, die per malloc/calloc zugewiesen
wurden.
Phillip Hommel schrieb:
> Jetzt bin ich mir nicht ganz sicher, wie ich zur Laufzeit malloc /> realloc auf Struct-Elemente anwende, also welche Größe ich den> Funktionen übergebe.
Die Anzahl der Bytes die du haben möchtest.
> Funktioniert da?>
1
>xyz=realloc(*array,(sizeof(array)/
2
>sizeof(array[0]))+sizeof(array[0]))
3
>
Das kommt einzige und alleine auf deine Arrays an. Aber so richtig
sinnvoll sieht das nicht aus.
Zeig ein bischen was von deiner Umgebung.
PS: Gerade wenn man wenig Speicher zur Verfügung hat, ist es oft besser
einfach eine Obergrenze für den dynamischen Teil einzubauen und alles
mit dieser Obergrenze statisch zu allokieren.
* malloc und Co benötigen für sich selbst auch Speicher
* Vorhersagen, wann der Speicher voll ist, sind schwierig bis unmöglich
Dementsprechend steigt die Absturzwahrscheinlichkeit
Mit einer fixen Dimensionierung umgeht man beide Problemkreise.
Das ist ohnehin ein wenig zur Seite gedacht.
Erstmal ist malloc/free/realloc für Speicher im Ram gedacht.
Lies Dir mal die entsprechenden Docs bzw. Kapitel in einem C Buch durch.
:-)
Ob struct-Elemente oder nicht ist eigentlich völlig wurst. Aber das
liest Du dann ja alles.
Theoretisch wäre es möglich Funktionen zu schreiben, die wie malloc/free
arbeiten aber in Bezug auf das EEPROM.
Das es noch niemand gemacht hat, nehme ich als Hinweis das es keinen
Wert hat. Aber das kann ja jeder anders sehen.
Schreib Dir zwei Defines in Deinen Code die Anfang und Ende eines
Bereiches im EEPROM bezeichnen.
Dann nimm die Schreib-Lesefunktionen aus Deinem C-Compiler der für AVR
angepasst ist.
Wiesel schrieb:
> Theoretisch wäre es möglich Funktionen zu schreiben, die wie malloc/free> arbeiten aber in Bezug auf das EEPROM.
so wie ich das verstanden habe, liest er aus dem EEPROM irgendwelche
Konfigurationsinfo, die dann dazu benutzt wird im SRAM irgendwelche
Datenstrukturen aufzubauen.
Allerdings macht mich die Bezeichnung "Struct-Array" stutzig. Das alles
klingt irgendwie nach: Er hat sich verrannt.
/*verweist auf Variable20, portc Ungruppiert, Pin0, portc Ungruppiert Pin1, min 0, max 60, increment 1, kein Modulo*/
84
};
Im Setup soll jetzt eingestellt werden, wieviele Portin/ Out ("Schalter
und Lämpchen") sowie Encoder und weitere, aus Übersichtsgründen im o.g.
Beispiel weggelassenene Geräte am Controller hängen und an welchen Pins
sie anliegen.
In der Init-Funktion zu Beginn der Programms sollen dann die Arrays
1
in_pots[]
,
1
out_ports[]
und
1
encoders[]
Um diese Geräte erweitert werden, entsprechend den Daten im EEPROM.
>Allerdings macht mich die Bezeichnung "Struct-Array" stutzig. Das alles>klingt irgendwie nach: Er hat sich verrannt.
Da ich nicht direkt aus der IT-Sparte komme, sondern mir das ganze
gezwungenermaßen selbst beibringe drücke ich mich wohl manchmal nicht
ganz fachlich Korrekt aus. Ich kenne keine richtigere Bezeichnung für
eine Sammlung an Structs wie zB encoders[], daher bitte entschuldigt die
manchmal Amateurhafte Ausdrucksweise.
>so wie ich das verstanden habe, liest er aus dem EEPROM irgendwelche>Konfigurationsinfo, die dann dazu benutzt wird im SRAM irgendwelche>Datenstrukturen aufzubauen.
Genauso ist es, allerdings will ich die Strukturen nicht direkt aufbauen
- habe sie ja schon definiert -, sondern zu den entsprechenden "Arrays"
eben neue Elemente hinzufügen..
Phillip Hommel schrieb:
> Genauso ist es, allerdings will ich die Strukturen nicht direkt aufbauen> - habe sie ja schon definiert -,
:-)
Struktur kann beides bedeuten:
die struct Definition als auch die sich dann tatsächlich im Speicher
ergebende Datenstruktur in ihrer vollen Pracht.
> sondern zu den entsprechenden "Arrays"> eben neue Elemente hinzufügen..
Genau das würde ich auf einem AVR nicht machen.
So wie du das machst, macht man das auf einem PC. Na ja fast. Dort würde
man anstelle der Arrays wahrscheinlich Listen benutzen. Sind einfacher
zu erweitern ohne dass ständig alles umkopiert werden muss.
Auf einem AVR hast du aber ein Problem. Du hast nur sehr wenig SRAM und
die Fragmentierung des Speichers kann zu einem Problem werden. Grob
gesagt: Du hast zwar in Summe zb 200 Bytes frei, aber diese 200 Bytes
sind nirgend in einem Rutsch verfügbar, sondern alle Speicherlöcher
zusammen ergeben 200 Bytes. Da wird es dann schwierig ein Array, welches
200 Bytes benötigt anzulegen.
Das ist das eine. Dem könnte man noch mit einer Liste anstelle von
Arrays zumindest teilweise entgegen wirken.
Aber das rettet dich nicht vor dem anderen Problem: Abzuschätzen wann
der Speicher eigentlich voll ist.
Und um dem entgegen zu wirken, ist die einfachste Variante die, die
überlegst dir sinnvolle Obergrenzen. Zb. Dass es nicht mehr als 20
Eingänge geben kann und nicht mehr als 30 Ausgänge. Dann hilft dir das
System zumindest insofern weiter, als dir die WinAvr ToolChain Bescheid
gibt, ob diese Menge an Devices überhaupt noch in den Speicher passen
wird.
1
#include<stdio.h>
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
//struktur für variablen
9
structvariable_memory
10
{
11
int*value;//Zeiger auf Values
12
unsignedbit:4;//bit 0-15 für Bool-Variablen
13
unsignedis_bool:1;//variable bool?
14
unsignedhas_low_part:1;//variable größer als 16 bit?
15
};
16
17
//Port in
18
structport_in_devices
19
{
20
structvariable_memory*mem;//Zeiger auf Variable
21
volatileunsignedchar*virt_port;//Zeiger auf Virtuellen Port
22
unsignedpin:3;//Pin
23
};
24
25
//Port out
26
structport_out_devices
27
{
28
structvariable_memory*mem;//Zeiger auf Variable
29
volatileunsignedchar*virt_port;//Zeiger auf Virtuellen Port
30
unsignedpin:3;//Pin
31
unsignedis_member_of_lamptest:1;//Soll bei Lamptest angehen
32
unsignedis_member_of_dim:1;//Soll bei Dim gedimmt werden
33
};
34
35
36
//Encoder
37
structencoder_devices
38
{
39
structvariable_memory*mem;//Zeiger auf Variable
40
volatileunsignedchar*channel_a_port;//Zeiger auf Port Kanal a
41
unsignedchannel_a_pin:3;//Pin Kanal a
42
volatileunsignedchar*channel_b_port;//Zeiger auf Port Kanal b
So blöd es auch klingt. Mit so einer Strategie wirst du wahrscheinlich
mehr Devices anlegen können, als wie wenn du das ganze dynamisch mit
realloc aufbaust. Speicherfragmentierung lässt grüßen.
Klingt wie immer super vernünftig und logisch, vielen Dank erstmal. Ich
werd mir das heute Abend mal in Ruhe zu Gemüte führen. Soweit ich das
bisher verstanden habe sehe ich für alle Geräte eine maximal Anzahl vor
(die dann unter maximaler Ausnutzung aller den Speicher nicht überlaufen
lässt) und fülle sie einfach eine nach der Anderen mit Daten, wobei die
"nr_of_..."-Variable mir angibt, bis wohin überhaupt gefüllt ist.
Das Setup soll sowieso von einem PC-Program aus gesteuert werden, daher
kann ich dort schon vorab eine Menge abfangen und könnte auch mehr
speicher vorsehen als maximal reinpassen würde.
(Denn ich weiß nicht, ob der Benutzer alle 144 "virtuellen Pins" für
Schalter, oder für LEDs oder in mischform benutzen will, oder 48 davon
außen vor lässt um 8 ADCs zu verwenden).
In dieser Flexibilität gegenüber dem Benutzer liegt ja meine
Hauptschwierigkeit.
Werde das ganze aber mal nach deiner Idee angehen, die MAX_NUMBER_OF
können ja auch im EEPROM stehen und vom Setup so eingestellt werden, daß
die Summe wieder passt...
So, hab das ganze mal angeschaut bin dabei es einzubauen, das sieht
wirklich nach der vernünftigsten Lösung aus.
Ich habe mal noch eine Frage zum Thema Speicherverbrauch: Wieviel Platz
braucht eigentlich ein Zeiger? Wird der als Variable angelegt oder
intern nur verlinkt?
Wenn ich jetzt 144 port_in_devices habe (soviel kann die hardware),
würde es dann sinn machen, die True und False-Eigenschaften nicht als
Byte reinzuschreiben, sondern einmal True und einmal False zu speichern
und dann nur noch darauf zu zeigen? Sind immerhin 18 Byte pro
Eigentschaft...
Statt Arrays fuer die verschiedenen Typen bereit zu halten, duerfte es
besser sein ein Array fuer alle Typen gemeinsam zu haben. Am einfachsten
geht das indem du eine Union fuer alle Typen erstellst und um die
nochmal eine struct legst, die einen Identifier hat, welcher Typ
enthalten ist.
So ungefaehr:
union AllTypesUnion
{
struct TypeA a;
struct TypeB b;
struct TypeC c;
};
struct AllTypesStruct
{
uint8_t type;
union AllTypesUnion content;
};
Auf die Pointer wuerde ich nach Moeglichkeit verzichten, die Ports
kannst du ja auch einfach durchnummerieren.
Bringt das Speicherplatz? Die Ports sind durchnummeriert und ich greife
ja sowieso mit verschiedenen Funktionen darauf zu wodurch die
verschiedenen typen schon vorteilhaft sind.
Ich bin grade am rumrechnen, wieviele Variablen und bytes im
Zentralspeicher ich wirklich brauche, und daher eben meine frage wie ich
noch Speicherplatz sparen kann...
Phillip Hommel schrieb:
> Bringt das Speicherplatz?
In deinem Fall leider nicht, weil die diversen structs unterschiedliche
Speicheranforderungen haben.
> Ich bin grade am rumrechnen, wieviele Variablen und bytes im> Zentralspeicher ich wirklich brauche, und daher eben meine frage wie ich> noch Speicherplatz sparen kann...
Viel wird da nicht mehr gehen.
Eines würde noch gehen. Anstatt der Pointer könntest du Indizes
verwenden. Wenn du zb nur max. 200 values haben kannst, die noch dazu
alle in einem Array sind.
Anstelle von
1
structvariable_memory
2
{
3
int*value;//Zeiger auf Values
4
unsignedbit:4;//bit 0-15 für Bool-Variablen
5
unsignedis_bool:1;//variable bool?
6
unsignedhas_low_part:1;//variable größer als 16 bit?
7
};
hast du dann
1
structvariable_memory
2
{
3
unsignedcharvalueNr;
4
unsignedbit:4;//bit 0-15 für Bool-Variablen
5
unsignedis_bool:1;//variable bool?
6
unsignedhas_low_part:1;//variable größer als 16 bit?
7
};
was dir bei jedem struct variable_memory Objekt 1 Byte einbringt. Für
das einzelne Objekt ist das nicht viel, aber in Summe läppert es sich.
Für die anderen Structs dann sinngemäss genauso: Überall dort wo du
einen Pointer in ein Array hast UND du weißt das das Array weniger als
255 Elemente haben wird, kannst du den Pointer durch den Index ersetzen.
255 deshalb, weil du dann den Indexwert 0xFF für 'kein Eintrag' (also
das was vorher der NULL Pointer war) reservierst.
(Auf der anderen Seite erhebt sich die Frage, warum du überhaupt einen
Verweis aus einem variable_memory Objekt auf einen value hast. Warum
nicht den value direkt in die struct variable_memory Objekt einbauen?)
>>Auf der anderen Seite erhebt sich die Frage, warum du überhaupt einen>>Verweis aus einem variable_memory Objekt auf einen value hast. Warum>>nicht den value direkt in die struct variable_memory Objekt einbauen?
Das hat den Hintergrund, daß ich teilweise nur Bits (state von
Port-Pins) speichere, und dann einfach 16 Pins auf den selben Value
zeigen lasse plus der Bit-Nummer.
Auf der anderen Seite sind manche Variablen länger als 16 Bit, denen
gebe ich dann einen Low-Part und nehme den Valuie an der "bezeigten"
Adresse und den an der nächsten. Damit habe ich dann 32bit.
Das mit den Indizes wäre eine Idee, also anstatt dem Pointer auf Int
einfach die nummer des Values im Array reinschreiben? Ich dachte immer
Pointer wären schön klein bzw würde sowieso angelegt werden,
aberanscheinend fressen die auch ganz schön Speicher...
So lange ich selbst die Hardware in meinem eigenen Projekt konfiguriere
kann ich das alles wunderbar "Hard-Coded" in den Quelltext aufnehmen und
brauche die ganze Initialisierung nicht, sobald das aber jemand anderes,
im besten Fall später ein Kunde nach seinen Wünschen zuweisen soll bin
ich z.Zt. einfach am Ende mit meinem Wissen.
Ich sollte
Eventuell könnte man noch so etwas wie beim Paparazzi-Projekt (bekannt?)
machen, wo ein Compiler im Paparazzi-Center mit dabei ist und man darin
per Code-Generator die Zuweisung macht.
Phillip Hommel schrieb:
> Das mit den Indizes wäre eine Idee, also anstatt dem Pointer auf Int> einfach die nummer des Values im Array reinschreiben? Ich dachte immer> Pointer wären schön klein
Neben double und long sind Pointer normalerweise einer der längsten
Dtaentypen, die es in C gibt. Schliesslich muss in einem Pointer ja auch
die auf dem System größte möglcihe Speicheradresse in Zahlenform
gespeichert werden können. Kann eine Architektur mehr als 256 Bytes
adressieren, muss ein Pointer schon mal mindestens 2 Bytes groß sein.
> bzw würde sowieso angelegt werden,
Nö, warum sollen sie?
Eine Pointervariable ist auch nur eine Variable in der ein Zahlenwert
gespeichert wird. Definierst du keine Variable, wird sie auch nicht
angelegt.
Jedes Objekt hat zwar eine Adresse im Speicher, so wie jedes Haus in
einer Strasse eine Hausnummer hat. Aber wenn niemand da ist, der sich
die Hausnummer notiert, muss man auch keinen Zettel reservieren, auf dem
man sich die Hausnummer aufschreiben kann.