brauch noch mal etwas Nachhilfe in C. Möchte eine Tabelle mit Zeigern
auf Variablen machen, so daß ich auf diese in einer Schleife zugreifen
kann obwohl sie der Linker irgendwo verstreut hat.
1
uint16_tVar1;
2
uint16_tVar2;
3
constuint16_tadrtab[]={&Var1,&Var2};
meldet beim Compilieren:
arm-none-eabi-gcc -mcpu=cortex-m0plus -std=gnu11 -g3 -c -Os
-DSTM32L011xx -DUSE_FULL_LL_DRIVER -mlittle-endian -Wall -mthumb
-funsigned-char -mno-tpcs-frame -gdwarf-2 -oo/main.o main.c
main.c:159:28: warning: initialization of 'short unsigned int' from
'uint16_t *' {aka 'short unsigned int *'} makes integer from pointer
without a cast [-Wint-conversion]
159 | const uint16_t adrtab[] = {&Var1,&Var2} ; // Array of
address table
| ^
main.c:159:28: note: (near initialization for 'adrtab[0]')
main.c:159:28: error: initializer element is not computable at load time
main.c:159:28: note: (near initialization for 'adrtab[0]')
main.c:159:34: warning: initialization of 'short unsigned int' from
'uint16_t *' {aka 'short unsigned int *'} makes integer from pointer
without a cast [-Wint-conversion]
159 | const uint16_t adrtab[] = {&Var1,&Var2} ; // Array of
address table
| ^
main.c:159:34: note: (near initialization for 'adrtab[1]')
main.c:159:34: error: initializer element is not computable at load time
main.c:159:34: note: (near initialization for 'adrtab[1]')
makefile:52: recipe for target 'o/main.o' failed
make: *** [o/main.o] Error 1
J. V. schrieb:> brauch noch mal etwas Nachhilfe in C. Möchte eine Tabelle mit Zeigern> auf Variablen machen, so daß ich auf diese in einer Schleife zugreifen> kann obwohl sie der Linker irgendwo verstreut hat.
Versuchs mal so. Zeiger haben immer was mit dem * zu tun.
Ok, es ist ein Typproblem was ich gestern abend aufgrund der noch nie
gesehenen Fehlermeldung nicht mehr geschnallt habe. Beim Variablentyp
selbst muß ich noch mal etwas gründlicher nachschauen. Tatsächlich sind
es vielerlei unterschiedliche Ram Variablen die beim Power-Fail
Interrupt ins Eeprom gerettet werden sollen. Das möchte ich in einer
Schleife über den sizeof Operator der Zeigertabelle machen. Es soll
nicht jedesmal das Save und Restore nachgebessert werden wenn eine
Variable entfällt oder neu erforderlich wird. Ein anderer Ansatz wäre
über den Linker ein seperates .dataX Segment aufzumachen und sich dann
die Anfangs und Endadressen zu holen. Aktuell gibt es aber noch genug
Flash für ein Feld von Zeigern irgendwohin ins .bss.
Die Seitengröße des Eeproms ist 4 Bytes und es macht eigentlich keinen
Sinn hier etwas systemunabhängig portabel machen zu wollen weil eben die
Hardware so ist. Allerdings sind viele Variablen auch nur 2 oder 1 Byte
weshalb ich da ein Union gemacht habe wo dann mehrere kleinere Variablen
in einer Eeprom Seite zusammenfassen kann. Das (allseits knappe) Ram
wird also ganz "normal" benutzt und Save und Restore kann dann auf den 4
Byte Typ passend zur Seitengröße in der Schleife zugreifen. Hat sich
eine Variable in der Seite geändert, wird zwangsweise die gesamte Seite
gesichert.
const uint16_t *adrtab[] = scheint schon mal korrekt zu compilieren
weil jetzt klar ist daß es ein Feld von Zeigern sein soll. Ein
Zeigereintrag in der Tabelle müsste voraussichtlich 4 Byte Flash belegen
weil der Cortex M0+ ein 32 Bitter ist.
was hindert dich, das gleich richtig zu machen?
Anstatt alle Variablen mit einem Hilfskonstrukt einzusammeln, schreib'
deinen Code um, so dass er diese Variablen direkt (in einer Struktur
oder einem Array) hält. Dann kannst Du das in einem Rutsch wegschreiben
oder laden.
was hindert dich, das gleich richtig zu machen?
Das ist eben die Kunst des Programmierens und ich muß erst mal
draufkommen was "richtig" ist. Oftmals bedarf es dazu eben einiger
Iterationen und nicht umsonst ist "agil" in Mode.
Wobei ich es nicht optimal finde, Variablen, die eigentlich nicht viel
miteinander zu tun haben vermutlich, in ein Feld zu nötigen, nur weil
sie als einzige Gemeinsamkeit das Etikett "soll ins EPROM kopiert
werden" tragen.
Erstens weil der Quelltext weder lesbarer noch wartbarer wird, wenn man
statt bspw. "zaehlerBenutzerzugriffe" etwas nichtssagendes wie
"wichtigesArray[23]" nehmen muß.
Zum anderen bleibt es vielleicht nicht dabei, daß alle solchen Variablen
in denselben Typ gezwängt werden können.
Deshalb ist ein Ansatz wie schon erwähnt mit einer eigenen
Linker-section für alles, was so gesichert werden soll, wesentlich
eleganter.
J. V. schrieb:> so daß ich auf diese in einer Schleife zugreifen> kann obwohl sie der Linker irgendwo verstreut hat
Und du erwartest jetzt das der Compiler schon weiß wo das später sein
wird?
Es gibt dabei leider noch ein anderes Problem: Neben Ram Variablen sind
auch einige Register aus der Hardware wie etwa Zählerstände von
Inkrmentalgebern zu sichern und wiederherzustellen. Eine Zeigertabelle
kann da locker draufzeigen und wenn man dort schreiben und lesen kann
funktioniert alles wie auch im Ram. Um die Hardware-Register in den
Struct reinzukriegen müssten man sie dazu zunächst jeweils gesondertes
Umkopieren. Also vielleicht doch wieder zurück auf Anfang...
> Und du erwartest jetzt das der Compiler schon weiß wo das später sein
wird?
Vom Linker möchte ich das schon erwarten daß er selbst weis wohin er die
Variablen legt.
Und wenn du das einfach abstrahierst?
Also:
Falls der Zustand eintrifft, dass alles weggeschrieben werden muss,
rufst du eine Fkt. auf "save_critical_data(...)".
In dieser hast du z.B. eine Struktur die dein Inhalt vom EEPROM
abbildet.
Diese wird mit get() Funktionen auf alle einzelnen Variablen/Register
befüllt. Da es ja sein kann, dass du sonst kein Zugriff auf einige
Variablen hast.
Dann wärst fertig damit.
Ein Ort wo alles zusammen kommt und du brauchst keine Zeiger die
irgendwo hin zeigen und es ist klar strukturiert.
Oder passt dieser Ansatz bei dir nicht?
>Diese wird mit get() Funktionen auf>alle einzelnen Variablen/Register befüllt.
Dann kann ich sie ja gleich ins Eeprom schreiben anstelle zuvor noch in
ein Struct. Ausserdem müsste ich das Befüllen und Wiederherstellen in
beiden Richtungen pflegen und deshalb wollte ich das so nicht. Außerdem
ist RAM immer knapp weshalb man das nicht doppelt will (auch nicht auf
Stack/Heap). Das umkopieren mit get() oder sonstwas genügt auch für die
wenigen HW Register welcher ausser der Reihe liegen.
Janvi schrieb:> Dann kann ich sie ja gleich ins Eeprom schreiben
Wenn du das einzeln machst, brauchst du mehrere Zugriffe (dauert
länger).
Janvi schrieb:> Außerdem> ist RAM immer knapp weshalb man das nicht doppelt will
Aber Zeiger nehmen je nach System manchmal sogar mehr Platz ein als der
Wert ansich, je nach Variablentyp.
Jede Seite muß sowieso einzeln bzw. in 4 Byte Portionen geschrieben
werden sobald ein Bit anders ist. Dauert also genau gleich lang.
Nachdem der Cortex M0+ ein 32 bitter ist, sind es die Zeiger wohl auch
und ebenso die Seitengröße mit den Werten. Es steht also 1:1 aber der
Unterschied ist, daß die Werte im Ram stehen und die Zeiger ins Flash
können wovon es ungleich mehr gibt.
Momentan gibt es noch die Schwierigkeit, daß die Zähler-Register 16 bit
sind und die anderen 2 Byte der Eeprom Seite dann ungenutzt bleiben wenn
ich das in der Schleife mache. Aus Sicht des Wear-Leveling halbiert das
die Lebensdauer weil ich eine weitere Seite für das nächste 16 bit
Register brauche. Also müsste ich pro Seite zwei Zeiger spendieren um
alles zusammenklauben zu können. Die Zeiger würden dann immerhin doppelt
so viel Flash Bytes belegen wie der angezeigte Inhalt im Ram bzw. IO
Bereich.
Janvi schrieb:> Nachdem der Cortex M0+ ein 32 bitter ist, sind es die Zeiger wohl auch> und ebenso die Seitengröße mit den Werten. Es steht also 1:1 aber der> Unterschied ist, daß die Werte im Ram stehen und die Zeiger ins Flash> können wovon es ungleich mehr gibt.
Sorry, das klingt irgendwie alles sehr verwirrend.
Wenn es ein 32Bit ist, dann ist da gar nix 1:1.
Wenn du 4x 16Bit Variablen im RAM hast, nehmen die 64Bit RAM ein.
Wenn du nun 4x Zeiger auf diese Variablen erzeugst, nehmen diese Zeiger
4x 32Bit ein = 128Bit.
Somit nehmen die Zeiger dir viel mehr RAM weg, als wenn du dir eine
Kopie dieser Variablen erstellst.
Und wenn du eine Kopie hast, z.B. in einer struct und du bei dieser noch
das alignment abstellst, dann kannst du die so ins eeprom schreiben und
dabei bleiben auch keine Bytes leer (frei).
[...]
1. Wurde schon geschrieben: Du brauchst kein Feld von uint16_t sondern
ein
Feld von Zeigern auf uint16_t
2. Die Priorität von * (Zeiger) ist höher als [] (Feld).
Also:
const uint16_t *(adrtab[]) = {&Var1,&Var2};
adrtab ist ein Feld von Zeigern auf uint16_t. Spiralenregel.
Da gibt es so eine Webseite "taking c gibberisch to english" oder so.
Weiss den Namen nicht mehr.
Gruß
Gunther