Hallo zusammen,
ich habe ein großes Struct, bei dem in einem Element ein Zeiger steht,
und zwar entweder auf
- Eine Variable eines bestimmten Typs oder
- Auf eine Funktion.
Auf was dieser Zeiger zeigt, sagt mir ein enum, das sich ebenfalls in
dem Struct befindet und definiert ist als:
{MT_NULL,none,(void*)headmainmenu,0,0},// Nullzeile markiert Ende des structs
Ich vermute, daß ich irgendetwas nicht bedacht habe - denn beim Cast des
Funktionsnamens in einen void-Pointer gibt mir der AVR-GCC eine Warnung
aus:
1
ISO C forbids conversion of function pointer to object pointer type
Habe ich bei diesem Konstrukt einen grundsätzlichen Denkfehler gemacht
oder ist nur eine Kleinigkeit falsch?
Viele Grüße
W.T.
[Edit:]
Funktionieren tut es - nur machen mich Warnings immer misstrauisch.
Walter Tarpan schrieb:> Ich vermute, daß ich irgendetwas nicht bedacht habe - denn beim Cast des> Funktionsnamens in einen void-Pointer gibt mir der AVR-GCC eine Warnung> aus:> ISO C forbids conversion of function pointer to object pointer type>> Habe ich bei diesem Konstrukt einen grundsätzlichen Denkfehler gemacht> oder ist nur eine Kleinigkeit falsch?
Die Warnung ist doch eigentlich recht eindeutig. In ISO C darf man nicht
zwischen Zeigern auf Funktionen und Zeigern auf Objekten casten. Ein
void* gilt als Zeiger auf ein Objekt.
Rolf Magnus schrieb:> Die Warnung ist doch eigentlich recht eindeutig. In ISO C darf man nicht> zwischen Zeigern auf Funktionen und Zeigern auf Objekten casten. Ein> void* gilt als Zeiger auf ein Objekt.
Daß mich der Compiler nicht anlügt, glaube ich. Aber was ist eine
regelkonforme Vorgehensweise?
Walter Tarpan schrieb:> Daß mich der Compiler nicht anlügt, glaube ich. Aber was ist eine> regelkonforme Vorgehensweise?
Eine uninon nehmen. Die ist exakt für sowas gedacht.
Rolf Magnus schrieb:> Eine uninon nehmen. Die ist exakt für sowas gedacht.
ganz bestimmt nicht.
Eine zuweise zwischen einen funktionzeiger und einen normalen Zeiger ist
überhaupt nicht zulässig.
Peter II schrieb:> Rolf Magnus schrieb:>> Eine uninon nehmen. Die ist exakt für sowas gedacht.>> ganz bestimmt nicht.>>> Eine zuweise zwischen einen funktionzeiger und einen normalen Zeiger ist> überhaupt nicht zulässig.
Ist es bei einer Union nicht, die beiden Zeiger teilen sich nur den
Speicherplatz.
Hans-Georg Lehnard schrieb:> Ist es bei einer Union nicht, die beiden Zeiger teilen sich nur den> Speicherplatz.
ach so habt ihr das gemeint. naja da kann man auch 2 variablen einlegen.
Peter II schrieb:> Hans-Georg Lehnard schrieb:>> Ist es bei einer Union nicht, die beiden Zeiger teilen sich nur den>> Speicherplatz.>> ach so habt ihr das gemeint. naja da kann man auch 2 variablen einlegen.
Die brauchen dann aber auch Platz für zwei Variablen. Der Witz bei der
Union ist ja gerade, daß das dort eben nicht so ist.
Es ist nicht zulässig, weil nicht auf jeder Prozessorarchitektur ein
Datenpointer genausogroß ist wie ein Funktionspointer.
Wenn man sich dessen bewusst ist, und nicht Wert auf 150%ige
Portabilität legt, kann man allerdings auch "Schwamm drüber" sagen, und
die Compilerwarnung ignorieren/abklemmen.
Mit dem sizeof-Operator lässt sich das ja immerhin überprüfen.
Naja, das Ganze findet auf einem ARM oder AVR statt, beidesmal mit dem
GCC.
Bei beiden sind die Zeigerbreiten gleich groß. Und es ist durchaus auch
noch mehr plattformabhängiger Code dabei.
Allerdings finde ich auch nicht, wie ich die Warnung einzeln mit -WnoXXX
abklemmen könnte. Bis jetzt waren auch alle "pedantic"-Warnungen
zumindest ein guter Grund, über ein Konstrukt noch einmal in Ruhe
nachzudenken, deswegen will ich mit dem globalen Warn-Level nicht
herunter.
Aber die Union müsste ja trotzdem klappen.
Wenn der Funktionspointer größer sein sollte als der Datenpointer wird
eben die Union größer, so groß eben wie der größte Member.
Preisfrage:
Was passiert wenn ich den Datenpointer schreibe und den Funktionspointer
lese, respektive "ausführe"?
bal schrieb:> Aber die Union müsste ja trotzdem klappen.
Wie initialisierst Du ein Union innerhalb eines struct-Arrays im Flash?
Der Umweg über einen Char-Zeiger bringt übrigens in Bezug auf die
Warnung auch nichts.
Walter Tarpan schrieb:> Wie initialisierst Du ein Union innerhalb eines struct-Arrays im Flash?
In Standard-C und mit avr-gcc überhaupt nicht :-)
Initialisierung gemäß C99 sieht z.B. so aus:
Johann L. schrieb:> Walter Tarpan schrieb:>> Wie initialisierst Du ein Union innerhalb eines struct-Arrays im Flash?>> In Standard-C und mit avr-gcc überhaupt nicht :-)
Wahrscheinlich ein Tippfehler: Der AVR-GCC hat mit dem Konstrukt
keinerlei Problem.
Der MSVC (C89) kriegt es allerdings nicht hin.
Ehrlich gesagt reicht mir auch die Lösung mit dem void-Zeiger von ganz
oben. Ich würde nur gerne die Warnung einzeln unterdrücken.
Walter Tarpan schrieb:> Johann L. schrieb:>> Walter Tarpan schrieb:>>> Wie initialisierst Du ein Union innerhalb eines struct-Arrays im Flash?>>>> In Standard-C und mit avr-gcc überhaupt nicht :-)>> Wahrscheinlich ein Tippfehler: Der AVR-GCC hat mit dem Konstrukt> keinerlei Problem.
Das Ding liegt aber nicht im Flash, dazu brauchst du __flash
oder __progmem__.
> Der MSVC (C89) kriegt es allerdings nicht hin.
Es braucht, wie gesagt, mindestens C99. Das kann Winzigweich nun mal
nicht.
Johann L. schrieb:>> Wahrscheinlich ein Tippfehler: Der AVR-GCC hat mit dem Konstrukt>> keinerlei Problem.>> Das Ding liegt aber nicht im Flash, dazu brauchst du __flash> oder __progmem__.
Momentan sind zwei Varianten vorhanden:
a) Die Definition ganz oben mit:
enumdatatype_edatatype;// Datentyp des Zeigers "pointer"
4
void*pointer;// Funktionszeiger
5
//char * pointer; // Funktionszeiger
6
constint16_tvarMin;// Untere Grenze numerische Variable
7
constint16_tvarMax;// Oberere Grenze numerische Variable
8
}menudata_t;// Struct wird terminiert mit einer
9
// Zeile, wo in .menutext ein
10
// Nulltext ({'\0'}) steht.
11
12
13
staticconstflashmenudata_tMenudat[]={
14
{MT_RETURN,voidFcn,NULL,0,0},
15
{MT_MESSAGE_TEST,voidFcn,(void*)menu_test,0,0},
16
...
liegt im Flash, da "flash" für den AVR zu "__flash" expandiert wird und
die Header eingebunden sind. Läge es nicht im Flash, würde nicht
funktionieren, da das Struct in voller Größe größer als der freie SRAM
ist.
b) Für die Variante mit dem anonymen Union in "menudata_t" liegt auch
alles im Flash. Sie sah so aus:
und funktioniert ebenfalls auf dem AVR.
Getestet habe ich alles mit drei Compilern:
1. AVR-GCC
2. ARM-GCC
3. MSVC 2010
Jetzt das Problem der Varianten:
1a: funktioniert, gibt eine lästige Warnung aus. Wenn ich die Warnung
unterdrückt bekomme, ohne daß andere Warnungen darunter leiden, bin
ich zufrieden.
Da die Structs verhältnismäßig groß sind, wird die Warnung nämlich
sehr oft ausgegeben, daß andere Warnungen dann untergehen können.
2a: Siehe 1a.
3a: funktioniert.
1b: funktioniert.
2b: funktioniert.
3c: funktioniert nicht, da der MSVC nur C89 kann. Sollte ich das
vernünftig initialisiert bekommen, daß es bei den anderen beiden im
Flash steht, bin ich auch zufrieden.
Für 3c (gemeint ist wohl 3b):
Gerüchten zufolge soll der C-Compiler, den MS in den letzten Jahren
sträflich vernachlässigt hat, in der neusten Inkarnation des Visual
Studio (VS2013) etwas weiter in Richtung C11 entwickelt worden sein.
Ob das hier hilft, entzieht sich meiner Kenntnis.
Für 1a:
gcc kennt ein #pragma, mit dem sich spezifische Warnungen abschalten
lassen:
#pragma GCC diagnostic ignored "-Wxxxx"
und wieder einschalten:
#pragma GCC diagnostic warning "-Wxxxx"
Praktischerweise gibt es bei gcc keine die spezfische Warnung
spezifizierende Nummer, was die Suche nach der abzuklemmenden Warnung
erschwert - auf die Schnelle habe ich "ISO C forbids conversion of
function pointer to object pointer type" nicht in
http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html finden können.
Daß die Warnung prinzipiell sinnvoll ist, steht außer Frage, aber sie zu
ignorieren, weil man weiß, daß auf der Zielmaschine Daten- und
Funktionspointer die gleiche Größe haben, ist ein durchaus legitimer
Wunsch.
Und es ist ja nicht so, daß alle Betriebssystem-APIs jeweils neu
geschrieben werden, nur weil die Akademiker, die den jeweiligen
C-Standard kredenzen, sich mal wieder was neues ausgedacht haben.
Rufus Τ. Firefly schrieb:> Praktischerweise gibt es bei gcc keine die spezfische Warnung> spezifizierende Nummer, was die Suche nach der abzuklemmenden Warnung> erschwert - auf die Schnelle habe ich "ISO C forbids conversion of> function pointer to object pointer type" nicht in> http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html finden können.>
Hallo Rufus,
genau auf der Seite habe ich auch gesucht. Die Warnung wird als
"-Wpedantic" geführt, sie einzeln abzuschalten finde ich nichts.
> Daß die Warnung prinzipiell sinnvoll ist, steht außer Frage, aber sie zu> ignorieren, weil man weiß, daß auf der Zielmaschine Daten- und> Funktionspointer die gleiche Größe haben, ist ein durchaus legitimer> Wunsch.
Zumal man es mit einer Zeile
Rufus Τ. Firefly schrieb:> Praktischerweise gibt es bei gcc keine die spezfische Warnung> spezifizierende Nummer, was die Suche nach der abzuklemmenden Warnung> erschwert
Nummer nicht, aber einen Schalter:
1
warning: ISO C forbids initialization between function pointer and 'void *' [-Wpedantic]
Johann L. schrieb:> Nummer nicht, aber einen Schalter:
Der aber klemmt noch diverse andere Warnungen ab, die man vielleicht
doch haben möchte.
Gut, bei Anwendung mit #pragma ist das nicht besonders relevant.
Also:
#pragma GCC diagnostic ignored "-Wpedantic"
... Pointerzuweisung ...
#pragma GCC diagnostic warning "-Wpedantic"
Oder, um nicht versehentlich pedantic einzuschalten, falls es vorher
nicht eingeschaltet war:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
... Pointerzuweisung ...
#pragma GCC diagnostic pop
Das dürfte der "sauberste" Weg sein, und um ganz sicher zu gehen,
kommt noch das assert von Walter dazu.
Rufus Τ. Firefly schrieb:> #pragma GCC diagnostic push> #pragma GCC diagnostic ignored "-Wpedantic">> ... Pointerzuweisung ...>> #pragma GCC diagnostic pop
Super! Vielen Dank! Das ist ein workaround, mit dem ich gut leben
kann.
Rufus Τ. Firefly schrieb:> #pragma GCC diagnostic ignored "-Wpedantic"
Augen zu, damit man das Elend nicht sehen muß?
Funktioniert, aber mich graust es bei sowas.
Walter Tarpan schrieb:> ch habe ein großes Struct, bei dem in einem Element ein Zeiger steht,> und zwar entweder auf> - Eine Variable eines bestimmten Typs oder> - Auf eine Funktion.
Wozu schreibst du SOWAS bloß? Igittigitt.
Wenn du schon ein "großes" struct hast, dann spendiere diesem struct
doch einfach noch einen oder mehrere weitere Zeiger, so daß du all diese
Klimmzüge garnicht erst heraufbeschwören mußt.
Oder überdenke deinen gesamten Ansatz nochmal. Das Ganze soll für ein
Menü herhalten, ja? Dann nimm nur EINEN Zeiger, nämlich einen
Funktionszeiger, der auf eine jeweils zum Menü-Element passende Funktion
zeigt. Damit wird deine ganze switch/case-Latte überflüssig und die
jeweiligen Bearbeitungsfunktionen werden klein, weil speziell. Dein enum
kannst du dir dann auch sparen, denn die jeweilige Funktion weiß schon
selber, was sie tun muß.
W.S.
bal schrieb:> Preisfrage:> Was passiert wenn ich den Datenpointer schreibe und den Funktionspointer> lese, respektive "ausführe"?
Nach ISO C ist das Verhalten undefiniert. In der Praxis wird halt
irgendein unsinniger Zeigerwert rauskommen.
Rufus Τ. Firefly schrieb:>> Augen zu, damit man das Elend nicht sehen muß?>> Nein. Augen zu, um eine akademische Warnung abzuklemmen.
Hmm, jetzt verwunderst du mich doch. Bei SDHC-Karten sollen Leute auf
eine für sie funktionierende Lösung verzichten, um standardkonform zu
sein, bei C ist dir Konformität dann aber wurscht, solang's irgendwie
tut.
O doch. Die allermeisten Leute hier in diesem Forum versuchen, fertige
C-Quellstücke in ihre Hardware zu stopfen OHNE NACHZUDENKEN und sie
erwarten daß alles funktioniert (neudeutsch FUNZT).
Da halte ich nen pedantischen Compiler durchaus für hilfreich, um diesen
Leuten wenigstens ein wenig auf den Zeh zu treten. Mag ja sein daß der
eine oder andere aus dieser Gilde sowas zum Anlaß nimmt, über sein
verkorkstes Gesamtkonzept nochmal nachzudenken - und schon ist etwas zur
Besserung der Allgemeinheit getan. Nicht viel, aber immerhin etwas.
W.S.
Nun, dem "funzt"-Ansatz beim "proggen" (so nennt man das heutzutage
wohl) lässt sich hier immerhin so:
Walter Tarpan schrieb:> Zumal man es mit einer Zeile> static_assert((sizeof(voidFcn_t)==sizeof(uint8_t*)),\> "Error: Pointers to functions and objects differ in size."\> "Implementation won't work.");> problemlos automatisch im Auge behalten kann.
mit einer gepflegten Fehlermeldung zwar nicht gerade auf die Sprünge
helfen, aber immerhin auf die Finger klopfen, sollte es nötig werden.
W.S. schrieb:> Oder überdenke deinen gesamten Ansatz nochmal. Das Ganze soll für ein> Menü herhalten, ja? Dann nimm nur EINEN Zeiger, nämlich einen> Funktionszeiger, der auf eine jeweils zum Menü-Element passende Funktion> zeigt. Damit wird deine ganze switch/case-Latte überflüssig und die> jeweiligen Bearbeitungsfunktionen werden klein, weil speziell. Dein enum> kannst du dir dann auch sparen, denn die jeweilige Funktion weiß schon> selber, was sie tun muß.W.S. schrieb:> O doch. Die allermeisten Leute hier in diesem Forum versuchen, fertige> C-Quellstücke in ihre Hardware zu stopfen OHNE NACHZUDENKEN und sie> erwarten daß alles funktioniert (neudeutsch FUNZT).W.S. schrieb:> Mag ja sein daß der> eine oder andere aus dieser Gilde sowas zum Anlaß nimmt, über sein> verkorkstes Gesamtkonzept nochmal nachzudenken - und schon ist etwas zur> Besserung der Allgemeinheit getan.
Danke, daß Du Dir die Zeit genommen hast, den aktuellen Stand meines
Projekts von meiner Website zu laden und das verkorkste Gesamtkonzept
einmal durchzusehen. Wie Du schon richtig erkannt hast, ist dieser
Quelltext zum Wohle der Menschheit gedacht. Obwohl ich kein
Softwareentwickler bin, bin ich natürlich der Meinung, daß es genau das
Bastelprojekt ist, was der Menschheit bislang gefehlt hat.
Bislang dachte ich, eine einzige Warnung bei "-pedantic" bei einem
Projekt mit etwas mehr als 7kZeilen (Header+Quelltext) wäre gar nicht so
schlecht. Zumal wenn die Warnung für eine Stelle im Quelltext eines
Menüs ist, also dort, wo ein Fehler -sollte er auftreten- sofort für den
Tester sichtbar ist.
Bislang war ich natürlich auch der Meinung, daß ein
switch/case-Konstrukt mit weniger als 40 Zeilen deutlich übersichtlicher
als sechs einzelne Funktionen ist, die auch noch alle einander sehr
ähnlich sein müßten. Mea Culpa.
Also bitte zeige mir Dein nicht-verkorkstes Menü, damit ich lernen kann!
Daniel A. schrieb:> Wie wäre es damit: Einen Funktionspointer in einer variable speichern,> von welcher man einen Objektpointer bekommen kann.
Hallo Daniel,
Zweck der ganzen Aktion war es ja, ein Menü-Strukt etwas übersichtlicher
darzustellen. Sozusagen eine läppische Spalte (=Struct-Feld) einsparen,
um die Verwechselbarkeit zwischen Variablenzeiger und Funktionszeiger
auszuschließen.
Für jeden Eintrag eine extra-Funktion abzulegen, konterkariert dieses
Ziel etwas.
Aber wie ich schon oben schrieb: Eigentlich bin ich mit der jetzigen
Lösung auch zufrieden. Hätte dieses Forum eine "gelöst"-Markierung, wäre
sie jetzt gesetzt.
Viele Grüße
W.T.
Walter Tarpan schrieb:> Also bitte zeige mir Dein nicht-verkorkstes Menü, damit ich lernen kann!
Ich kann mich des Eindruckes nicht erwehren, daß du beleidigt reagierst
anstatt ein bissel nachzudenken.
Also ich erkläre dir das:
Walter Tarpan schrieb:> ich habe ein großes Struct, bei dem in einem Element ein Zeiger steht,> und zwarWalter Tarpan schrieb:> Ziel der ganzen Aktion ist es, eine zum Datentyp passende Aktion> auszuführen:
Eben. Du denkst prozedural und du schreibst deinen Code dann auch
prozedural, also etwa so, wie man es auf Papier in nem PAP macht. Sowas
geht ja für ganz kleine Projekte, da braucht man nur ein Array aus
Struct's, wo eine Bezeichnung und ein Aktionscode drinsteht. Das Ganze
artet dann in ein Programmstück mit einem umfänglichen switch case aus.
Wie gesagt, für ganz kleine Projekte reicht sowas.
Aber sobald ein Projekt und damit ein Menüsystem etwas größer wird, ist
dieser prozedurale Ansatz nur noch Mist. Zu Anfang geht es, dann wird#s
umfänglicher und man verheddert sich (wie du) in der Behandlung von
Sonderfällen - und wenn man irgendwas später ändern will, muß man an
1000 Stellen herumdoktern.
Sowas macht man anders, nämlich objektorientiert und ereignisgesteuert.
Ein solches Menüsystem besteht aus einem Sack von Objekten, logisch
hierarchisch je nach Bedarf angeordnet und physisch im ROM angeordnet.
Ja, im ROM, ohne malloc und Konsorten zu benötigen.
Jedes Objekt hat Daten, also quasi Properties, Zeiger zu seinen Peers,
nen Zeiger zu seinen Members, nen Zeiger zu seinen variablen Daten im
RAM und Methoden.
Jaja, C kennt keine Objekte, weswegen man - sofern man in C schreiben
will - sowas eben zu Fuß erledigen muß. Dazu braucht es eine struct
Definition, die für alle Menüelemente gilt. Lediglich die Methoden sind
je nach Objekttyp unterschiedlich. Eigentlich braucht man genaugenommen
nur 3 Methoden:
1. eine Methode, mit der sich das Objekt selbst darstellt
2. eine Methode, mit der das Objekt auf allgemeine Ereignisse reagiert
3. eine Methode, mit der das gerade fokussierte Objekt auf Usereingaben
reagiert.
Ein so aufgebautes Menü ist wirklich universell, die Menüelemente können
alles Mögliche sein (statischer Text, ne Eingabezeile, Buttons,
Checkboxes usw.) und sie können Members haben, also ein Button mit nem
Text drauf oder mit nem Icon drauf oder mit beidem usw.
Das Angenehme an so einem Menü ist, daß die verschiedenen Objekte
voneinander getrennt ihre Arbeit verrichten, OHNE daß das übergeordnete
Element sich um deren Details kümmern muß.
Lade dir mal die Lernbetty aus der Codesammlung herunter, dort hatte ich
damals ein derart aufgebautes Menü vorgeturnt. Du kannst dir also dort
deine Anregungen holen.
W.S.
W.S. schrieb:> Ich kann mich des Eindruckes nicht erwehren, daß du beleidigt reagierst> anstatt ein bissel nachzudenken.
Der Eindruck mag entstehen, allerdings neige ich dazu, das Angenehme mit
dem Nützlichen zu verbinden und einfach beides gleichzeitig zu machen
:-).
W.S. schrieb:> Eben. Du denkst prozedural und du schreibst deinen Code dann auch> prozedural,>> [...]>> Wie gesagt, für ganz kleine Projekte reicht sowas.
Siehst Du. Genau der Meinung bin ich auch. Ich habe maximal 5
Datentypen, von denen 4 fast identisch behandelt werden müssen. Und die
"Behandlung" aller Datentypen zusammen ist nur wenige Zeilen lang.
Von den Prozeduren bestehen 80% einfach daraus, das Eingabe- und das
Ausgabegerät zur Verfügung zu stellen.
Wenn ich das Menü-Struct übersichtlich auf einer Bildschirmseite
dargestellt bekomme, ist mir das deutlich mehr wert, als wenn das
bischen Quelltext, das darüberläuft, schöner ist.
Ich bin ein Freund objektorientierter Arbeitsweise. Aber nicht für ein
popeliges Menü auf einem 128x64-Zeichen-Textdisplay.
Na, dann war das Problem ja schon von Anfang an gelöst und der ganze
Aufriß hier völlig umsonst. Und dann brauschst du ja das "voidFcn" auch
bloß nicht, weil du garantiert massenweise Platz in deinem "datatype"
hast, um alle vorkommenden Fälle abzudecken, ohne die Adresse einer
Funktion mitzuliefern.
Schreib das beim nächsten Mal doch gleich hin, das spart ne Menge
Aufregung.
W.S.
W.S. schrieb:> Schreib das beim nächsten Mal doch gleich hin, das spart ne Menge> Aufregung.
Hm. Ich könnte mich nicht daran erinnern, aufgeregt gewesen zu sein.
Seit dem Beitrag mit dem "unnamed"-union habe ich eine sinnvolle und
schöne Lösung für C99, und seit diesem Beitrag
Beitrag "Re: Einmal void-Pointer und zurück" sogar eine Lösung,
die unter C89 gut funktioniert.
Danach kam ein Beitrag mit "Igittigitt". Aufregend ist der Thread
seitdem auch noch nicht. Aber interessant genug, wenn man etwas
streitlustig ist und gleichzeitig etwas Langeweile hat.
W.S. schrieb:> Na, dann war das Problem ja schon von Anfang an gelöst und der ganze> Aufriß hier völlig umsonst. Und dann brauschst du ja das "voidFcn" auch> bloß nicht, weil du garantiert massenweise Platz in deinem "datatype"> hast, um alle vorkommenden Fälle abzudecken, ohne die Adresse einer> Funktion mitzuliefern.
Warum ich einen objektorientierten Ansatz brauche, wenn ich schlappe 5
Datentypen habe, von denen einer ein function handle ist und die anderen
4 fast identisch sind, geht mir trotzdem nicht in den Kopf. Und warum
das plötzlich massiv Platz braucht, wenn ich einen enum (1 byte) und
einen Zeiger (2 byte) in ein Struct packe, ist mir auch rätselhaft. Aber
egal. Ich hab's ja. Der Flash meines ATmega32 ist ja gerade mal zu 74%
voll. Da passen noch viele Struct-Zeilen hinein.
Viele Grüße
W.T.
Nanana.. DU hast ganz am Anfang von deinem "großen struct" geschrieben
- alle anderen haben lediglich genau darauf ihre Vermutungen aufgebaut.
Und warum du 5 Integertypen und einen Funktionszeiger denn überhaupt
brauchst, hast du noch immer nicht erklärt. Wenn du z.B. alle Variablen
als 'long' deklarierst, brauchst du überhaupt keine Fallunterscheidung,
sondern bloß einen long* Zeiger.
Ich hab allerdings in deinen anderen Beiträgen gesehen, daß du teilweise
sehr seltsame Denkansätze verfolgst, die ich so nie und nimmer andenken
würde, beispielsweise dein Versuch mit vereinheitlichtem PortA, PortB
usw. um textuelle Gleichheit zwischen ARM und AVR herzustellen.
Normalerweise schreibt man sich die Initialisierung des µC genau passend
zum konkreten Chip ganz individuell. Da ist kein Platz für irgendwelche
Vereinheitlichungen mit ganz anderen µC-Familien.
W.S.
OK, diskutieren wir die Sinnhaftigkeit meines Vorgehens hier. Thematisch
ist der Thread ja abgeschlossen. Und neben meiner natürlichen Streitlust
hat das für mich den Vorteil, daß man dadurch, daß man ein Vorhaben
erklärt, selbst merkt, wenn man eine Dummheit vor hat :-).
W.S. schrieb:> Und warum du 5 Integertypen und einen Funktionszeiger denn überhaupt> brauchst, hast du noch immer nicht erklärt. Wenn du z.B. alle Variablen> als 'long' deklarierst, brauchst du überhaupt keine Fallunterscheidung,> sondern bloß einen long* Zeiger.
Oben habe ich die Deklaration meines "großen Structs" hingeschrieben.
Aber ich paste es noch einmal in der aktuellen Form:
enumdatatype_edatatype;// Datentyp des Zeigers "pointer"
4
// Bei function pointer wird die Funktion
5
// gestartet, bei Zeigern auf normale Daten
6
// der Datentyp im Menue bearbeitet.
7
union{
8
voidFcn_tpvoidFcn;// Funktionszeiger
9
uint8_t*puint8;// Zeiger auf Variablentypen
10
uint16_t*puint16;
11
int8_t*pint8;
12
int16_t*pint16;
13
void*pvoid;
14
};
15
constint16_tvarMin;// Untere Grenze numerische Variable
16
constint16_tvarMax;// Oberere Grenze numerische Variable
17
}menudata_t;// Struct wird terminiert mit einer Zeile,
18
// wo in .menutext ein Nulltext
19
// ({'\0'}) steht.
Auf dem AVR (einem ATmega32) ist jedes Struct dieses Typs im Flash
angesiedelt und stellt den Inhalt eines Menüs dar.
Und so sieht die Speicherbelegung aus:
1
Program Memory Usage : 27728 bytes 84,6 % Full
2
Data Memory Usage : 1519 bytes 74,2 % Full
Wie man sieht, ist im Flash noch großzügig Platz vorhanden, während ich
im SRAM aufpassen muß.
Das Struct mit den Konfigurationsdaten muß im SRAM Platz finden. Sogar
zweimal (Eine Sicherheitskopie während des EEPROM-Auslesens und in
Menüphasen, um geänderte Einstellungen verwerfen zu können). Also mag
ich hier Datensparsamkeit. Was in ein uint8 paßt, kommt in ein uint8.
Und was in ein int8 paßt in ein int8. Die Zeiger im struct sind immer 16
Bit groß. Macht aber nichts. Ist ja im Flash.
Das Menü hat zwei "Kernfunktionalitäten":
- Einstellungen (Integer-Werte) bearbeiten
- "Programme" (void-void-Funktionen) starten - die meisten davon sind
Diagnosefunktionen oder andere Menüs.
Für diese Kernfunktionalitäten wird extrem wenig Quelltext gebraucht. Es
paßt alles auf eine Bildschirmseite. Der Aufwand im Menü steckt in den
Nebenfunktionalitäten:
- Bildschirm-Update
- Drehgeber- und Taster-abfragen
- Hausaufgaben machen (Scrollpositionen ausrechnen, dafür sorgen, daß
Bedienelemente, die vom Menü ständig abgegeben und wiedergeholt werden,
das machen, was man von ihnen erwartet usw.)
Es gibt keinerlei Grund, auf diese Nebenfunktionalitäten einen weiteren
Abstraktionslevel aufzusetzen, um diese Handvoll Hauptfunktionalitäten
zu trennen. Dazu ist das Menü zu einfach gestrickt.
Und natürlich ist das Menü selbst mehrere Abstraktionsebenen über dem
Grafik-Display, dem Drehgeber und dem Taster.
-----
W.S. schrieb:> ch hab allerdings in deinen anderen Beiträgen gesehen, daß du teilweise> sehr seltsame Denkansätze verfolgst, die ich so nie und nimmer andenken> würde, beispielsweise dein Versuch mit vereinheitlichtem PortA, PortB> usw. um textuelle Gleichheit zwischen ARM und AVR herzustellen.
Der Denkansatz mag seltsam erscheinen - ich halte ihn aber für sinnvoll.
Mit dem AVR habe ich eine funktionierende Plattform (=Quelltext +
Entwicklungsumgebung + Steuerungsleiterplatte + Gerätehardware, in die
das Ganze eingebettet ist). Für den ARM sind drei dieser Punkte
neu/nicht vorhanden.
Also verfolge ich den Weg:
1. AVR-Quelltext allgemeiner (=plattformunabhängiger) machen.
2. Plattformabhängige Komponenten kapseln
3. Testen (mit AVR-Hardware)
(4). Für den AVR einen Bitbanging-"Treiber" schreiben (umschaltbar)
(5). Testen (mit AVR-Hardware)
(6). Bitbanging-"Treiber" plattformunabhängig schreiben.
(7). Testen (mit AVR-Hardware)
8. Auf ARM portieren
9. Modultest mit ARM-Hardware
10. Für den ARM einen "nativen Treiber" schreiben (umschaltbar).
11. Modultest mit ARM-Hardware
12. Verbesserungen in AVR-Quelltext einfließen lassen
13. goto 1;
Die Punkte in Klammern habe ich nur beim I2C gemacht- den fand ich ein
wenig knifflig. Und für diese Punkte war die Austauschbarkeit einer
funktionierenden Low-Level-Routine eine echte Hilfe. Auch wenn ich (zum
Glück) noch keine ernsthaften Hardwarefehler finden mußte.
Vielleicht wäre ein "schmeiß alles weg und machs neu"-Ansatz schneller
gegangen. Vielleicht hätte ich mich damit aber auch mit drei neuen
Faktoren neben dem Quelltext (Entwicklungsumgebung, MCU, Hardware) von
der grünen Wiese auch so ins Abseits manövriert, daß ich noch nicht so
weit wäre.
W.S. schrieb:> Normalerweise schreibt man sich die Initialisierung des µC genau passend> zum konkreten Chip ganz individuell. Da ist kein Platz für irgendwelche> Vereinheitlichungen mit ganz anderen µC-Familien.
"Normalerweise" findet vermutlich auch selten Sprung eines Projekts von
einer MCU mit 8 Bit 16 MHz zu 32 Bit 72MHz statt. Schon allein aus
Kostengründen würde ein knapp zu kleiner Prozessor selten durch etwas
anderes mit soooo viel Reserve ersetzt. Oder wäre aus ebendiesen Gründen
schon lange durch etwas Moderneres ersetzt worden.
-----
Zu guter Letzt: Durch die Migration auf andere Hardware sind eben neue
Anforderungen an das Menü entstanden- denn jetzt muß einfach viel mehr
einstellbar sein. Und deswegen hat "alles mit int16" den verbleibenden
RAM unkomfortabel klein und die separate Spalte für Funktionszeiger hat
den Quelltext schlechter lesbar gemacht.
So schließt sich der Kreis.