Moin,
manchmal oder sogar öfter tauchen hier Fragen auf, wie man ein einmal
mehr oder weniger sorgfältig durchdachtes struct am besten nichtflüchtig
verewigt.
Da kommen dann Antworten, daß man das struct eher als union formulieren
sollte. Dann hat man aber andere Restriktionen.
Ich wollte es nicht glauben: Da hat man eine vermutlich lineare
Anordnung von Bytes im RAM, und dann soll man die Bytes womöglich
entweder ganz zu Fuß byteweise oder noch etwas intelligenter per union
Stück für Stück durch die Gegend schaufeln? Obwohl alle beteiligten
Speicher auch linear arbeiten?
Mir ist es nach harter Arbeit endlich gelungen, das zu lösen, und zwar
mit meinem neuen Superzeiger ;))
Erfahrene C-Zeiger-Leute bitte weghören, die verstehen womöglich sogar,
was dieses Konstrukt genau macht g
Es ging bei meinem Problem darum, ein TFT-Panel mit der Eigenschaft
'Display' und der anderen Eigenschaft 'Touchpanel' miteinander zu
verheiraten. Wenn man mehrere Displays hat, möchte man nicht immer neu
kalibrieren.
So könnte eine ganz, ganz einfache Struktur, die die beiden miteinander
verbindet, aussehen:
1
//TYPENDEKLARATION TFT Panel
2
//Die Struktur wird im SPI-Flash (auf dem Panel vorhanden) gespeichert
uint32_txoff;//Touchpanel calibration value x offset
14
uint32_tyoff;//Touchpanel calibration value y offset
15
uint32_txfac;//Touchpanel calibration value x factor
16
uint32_tyfac;//Touchpanel calibration value y factor
17
}PANEL_t;
18
19
PANEL_tpanel;
Sinn: In main liest man den Spiflash aus und braucht nicht immer neu zu
kalibrieren. Oder wenn ein solcher nicht vorhanden ist, eine SDcard, auf
der z.B. der Druckpunkt des Panels abgespeichert ist (threshold up/do).
Der benötigte Druck (bzw. umgekehrt - der Widerstand) ist bei einem
2.4"- oder einem 7"-Display schon ziemlich unterschiedlich.
Das sind im obigen Beispiel also 64 Bytes in der Struktur "panel", die
jetzt irgendwohin gespeichert werden sollen. Wie kriegt man also die
RAM-Daten einfach ins Flash oder auf SD?
Wenn man einen Pointer definiert, der auf einen uint8_t-Typ zeigen soll,
geht der Rest wie von selbst (*).
1
uint8_t*ptr=(uint8_t*)&panel;
Speichern ins Flash geht so:
1
SpiFlashUnlock();
2
SpiFlashSectorerase(0);
3
SpiFlashWriteByteBlock(0,ptr,sizeof(panel));
Dabei ist 'SpiFlashWriteByteBlock' die Standardroutine für SPIflash, die
als erstes Argument die Adresse im Flash erwartet, als zweites den
ersten Eintrag im Struct (= erstes Byte, was übertragen werden soll),
und als dritten die Größe des Structs. Diese Routinen findet man überall
im Netz, aber die Hin- und Herspeicherei zwischen struct, Flash und SD
eher seltener.
Lesen geht mit dem gleichen Zeiger so:
1
uint8_t*ptr=(uint8_t*)&panel;
2
SpiFlashReadByteBlock(0,ptr,sizeof(panel));
Und zack, kann man auf die einzelnen Elemente der Struktur wieder
zugreifen.
Auf SDCard funktioniert es genauso:
1
uint8_t*ptr=(uint8_t*)&panel;
Schreiben und Lesen eines "Device-Deskriptors" anhand von Elm-Chan-FATFS
(devFlash und devSDCard sind Flags, die vorher schon getestet wurden und
das Vorhandensein des jeweiligen Speichers prüfen):
1
// ########################
2
// Device-Deskriptor lesen, a) von Flash oder falls nicht vorhanden, von SDCard
Trotzdem habe ich noch eine Frage, auch wenn das hier nicht erlaubt ist:
Das funktioniert zwar so, aber ist das für 'C'-Verhältnisse auch korrekt
gemacht, also der Zeiger korrekt definiert? Irgendwelche Fallstricke zu
befürchten?
Bin mit Assembler großgeworden, weiß also, wie man in der Machine
indirekt indiziert adressiert, aber die 'C'-Sprache nervt mit ihrer
Zeigescheisse unglaublich. Das versteht doch kein normaler Mensch.
Ich hab das halt nur solange ausprobiert, bis mir der blöde GCC-Compiler
keine Warnings wegen inkompatiblen Zeigern oder noch Schlimmerem um die
Ohren gehauen hat.
Hallo Markus, danke für die Rückmeldung.
Hätte schwören können, daß da wieder was faul ist ;)
Aber mit Deiner Aussage motiviert es mich eher, nochmal genau darüber
nachzudenken, wie C das handhabt.
Denn was man will, weiß man ja. Irgendwo hängt ein Stückchen Speicher
rum, und man will doch nur erreichen, daß man anderen, freundlichen
Zeitgenossen (Modulen) mitteilt, wo dieser Speicher liegt.
Mir fehlt nicht mehr viel, und ich bekomme das beim STM32F4 in den
Griff, anders als beim AVR. Da macht man halt mit PROGMEM und anderem
rum.
And now for something completely different:
Angesichts der Tatsache, daß ziemlich viele TFT-Panels heutzutage mit
SPI-Flash und SD-Kartenslot ausgestattet sind - ok, zumindest für die
Arduino-Gemeinde - finde ich die Verschiebung ins GCC-Forum aufgrund
eines, nebenbei bemerkt nicht besonders sachdienlichen, statements eines
anderen Users, dessen "Beitrag" zudem gelöscht wurde, ziemlich daneben.
Tip: Im mikrocontroller.net mal nach "struct" googeln. Und dann sich
wundern, in welcher Rubrik die Beiträge meist stehen.
Was für ein Schwachsinn, auf Wunsch eines(!) im Zweifel zweifelhaften
Users den Beitrag zu verschieben.
avus schrieb:> Wenn man einen Pointer definiert, der auf einen uint8_t-Typ zeigen soll,> geht der Rest wie von selbst (*).
1
uint8_t*ptr=(uint8_t*)&panel;
Damit machst Du genau das, was alle anderen mit einer Union machen. Die
Union spart einem dieses ständige Casten ein und erhöht damit die
Sicherheit.
Ein Cast schaltet ja immer viele Fehlertests aus.
Ein Cast sagt dem Compiler: Ich weiß es besser als Du, also sei still.
avus schrieb:> aber die 'C'-Sprache nervt mit ihrer> Zeigescheisse unglaublich. Das versteht doch kein normaler Mensch.
Doch, man braucht nur ne Weile.
Im Prinzip sind Zeiger nur Adressen. Das muß man verinnerlichen.
Im Gegensatz zu Assembler kann man aber dem Compiler ziemlich genau
sagen, von was ein Zeiger die Adresse ist (Funktion, Wert, Array,
Struct, Structmember usw.).
Man muß also nicht umständlich alle Offsets selber ausrechnen, sondern
überläßt das bequem und fehlerfrei dem Compiler.
Insbesondere bei IO-Registern sind Structs sehr nützlich und vor allem
fehlersicher. Der Compiler meckert einen sofort an, wenn man ein Bit in
einem falschen Register setzen will oder es garnicht existiert.
Wenn man dagegen nur schreibt:
1
*Adresse=Wert;
kann der Compiler nicht überprüfen, ob das zulässig ist, d.h. diese
IO-Ressource in dem gewünschten Target existiert.
Leider wurden die IO-Structs bei den standard AVR noch nicht definiert,
sondern erst bei den XMEGA und den ARM.
Peter Dannegger schrieb:> Damit machst Du genau das, was alle anderen mit einer Union machen. Die> Union spart einem dieses ständige Casten ein und erhöht damit die> Sicherheit.
Ich habe ein struct gewählt, um möglichst frei in der Handhabung zu sein
(32 char am Anfang etc.). Das Ding ist nur 'zufällig' 64Byte breit.
In der Union könnte man natürlich auch auf die chars herunterdividieren,
aber mein Problem war doch: Wie speicher ich das Ding einfach komplett
weg, ohne mir noch über die einzelnen Bytes Gedanken zu machen.
Zu Deiner zweiten Antwort:
> Im Gegensatz zu Assembler kann man aber dem Compiler ziemlich genau sagen, von > was ein Zeiger die Adresse ist (Funktion, Wert, Array, Struct, Structmember
usw.).
> Man muß also nicht umständlich alle Offsets selber ausrechnen, sondern> überläßt das bequem und fehlerfrei dem Compiler.
Absolut ein Vorteil, den ich noch nicht wirklich zu schätzen
weiß/schätzen kann. Aber genau diese Zielrichtung ist sinnvoll, danke
Dir ;)