Hallo,
ich versuche grad zu verstehen, wie das 32 Bit System sich auf die
variablen auswirkt, die ich benutze. Bin sehr erfahren mit der
Programmierung des Cortex aber eben nur auf einer höheren Ebene unter
Benutzung von RTOS wie ChibiOS usw, wo man sich darum nicht kümmern
muss.
Wie liegt das RAM vor? Mein kleiner F103 hat 20 KB internes RAM. 20 KB =
20 x 1024 Bytes. Aber bei 32 Bit müssten das also 5120 32 Bit Wörter
sein.
Oder ist das noch hardwaremässig unterteilt in 8 Bit Bytes?
Wenn ich einen Ausdruck wie uint8_t vor eine Variable schreibe,
verbraucht der Compiler dann trotzdem 32 Bit für diese Variable und
lässt die oberen 24 Bit ungenutzt? Oder erzeugt er Code um 4 uint8_t
Variablen in einem 32 Bit RAM Zelle unter zu bringen?
Kann man den GCC Compiler veranlassen "sparsam" mit dem internen Ram
umzugehen, auch wenn das mehr Code erzeugt?
Sorry, vielleicht ist das ja alles ein alter Hut aber ich möchte es
trotzdem mal verstehen...
Gruss,
Christian
Chris J. schrieb:> Aber bei 32 Bit müssten das also 5120 32 Bit Wörter> sein.> Oder ist das noch hardwaremässig unterteilt in 8 Bit Bytes?
Letzteres.
Wenn man den AdressZähler um eins hochzählt, dann zählt man ein Byte
weiter. Der Cortex-M3 kann also als 32-Bitter die Bytes von 4
aufeinanderfolgenen Adressen gleichzeitig in ein Register laden.
Chris J. schrieb:> Wenn ich einen Ausdruck wie uint8_t vor eine Variable schreibe,> verbraucht der Compiler dann trotzdem 32 Bit für diese Variable
Nein.
Allerdings kann der Code dadurch langsamer werden, denn die Reduktion
32->8 Bits kann zusätzliche Instruktionen kosten. Nicht jede Variable
landet zwingend im RAM, Schleifenzähler z.B. kann er oft im Register
halten.
Allerdings muss er für "static" und globale Variablen zwingen RAM
reservieren.
Chris J. schrieb:> Sorry, vielleicht ist das ja alles ein alter Hut aber ich möchte es> trotzdem mal verstehen...
Man sollte denken, dass sich mittlerweile rumgesprochen hat, wozu die
Herstellerdokumentation da ist...
Also hier mein Tip:
ARM hat sehr gute Literatur zu seinen Controllern, da findest Du
Antworten auf Deine Fragen. Klar, ist lästig, ein paar Tausend Seiten
durchzugehen. Aber wenn Du es nicht machst, wirst Du immer wieder
Probleme bekommen.
Hi,
danke für die Info. "Geahnt" habe ich es, allerdings lassen sich auch 32
Bit wie 8 Bit softwaretechnisch verwalten, wenn man nur genügend Aufwand
betreibt. Ich schreibe Programme, vielleicht als Relikt aus den 80igern
(1kb beim Sinclair ZX) immer noch "platzsparend", obwohl ich bei Code
inzwischen mehr auf Lesbarkeit achte, denn das 1BM Flash beim F427
kriege ich eh nie voll, grad mal 80kb und das bei über 10.000 Zeilen....
Das native Datenformat eines C-Compilers ist int.
Wenn er mit Bruchstücken davon hantieren muss, resultiert
in der Regel Mehraufwand.
Und int sind beim ARM 32 bit.
Cschlumpf schrieb:> Wenn er mit Bruchstücken davon hantieren muss, resultiert> in der Regel Mehraufwand.>> Und int sind beim ARM 32 bit.
Schon klar. Da ich Daten eines Sensors (8 Bit PIC, compiliert mit CCS
Compiler) zu einem ARM (erzeugt mit GCC) per Funk übertrage musste ich
die auch packed machen, damit das Datenpaket (ein struct) richtig
einsortiert wurde im Zielsystem. Da nützten auch uint8-t nichts, ohne
packed_array wurde das nichts.
Chris J. schrieb:> übertrage musste ich> die auch packed machen, damit das Datenpaket (ein struct)
Versuche packed in Zukunft zu vermeiden, lies stattdessen mal das
folgende und plane die Anordnung der einzelnen Elemente entsprechend:
http://www.catb.org/esr/structure-packing/
Bernd K. schrieb:> Versuche packed in Zukunft zu vermeiden, lies stattdessen mal das> folgende und plane die Anordnung der einzelnen Elemente entsprechend:
Wenn etwas einfaches funktioniert.. warum es dann kompliziert machen?
Ich lese es heute abend mal durch, sieht interessant aus...
1
/* An den STM32F103 Display Controller adressierter Datensatz */
Chris J. schrieb:> /* An den STM32F103 Display Controller adressierter Datensatz */> typedef struct {> uint8_t command; // 1: 0x10 = Datensatz> folgend , 0x20 = .....> uint8_t nrDataSet; // 1: Datensatznummer> (rotierend)> uint8_t id_data; // 1: Aktuelle Satznummer in> History> uint8_t Feuchte_Now; // 1: Aktuelle Feuchte> uint16_t Pressure_Now; // 2: Aktueller Druck> int8_t IntTemp_Now; // 1: Innentemperatur> int8_t ExtTemperature_Now; // 2: Aktuelle> Aussentemperatur (99 = ungültig)> float h_Pressure; // 4: History Druck[id_data]> uint8_t h_ExtSensor_Feuchte; // 1: Feuchte außen> int8_t h_ExtSensor_Temperature; // 1: Temperatur außen> time_t unix, // 4 Bytes> timestamp; // 4: Zeitstempel der> Messung>> /* -------------------------------> 31 Bytes */> } __attribute__((packed)) stm32f103_data_t;
Ist alles aligned bis auf den Zeitstempel am Ende, der ist 4 byte lang
und würde deshalb von sich aus gerne 2 Byte weiter hinten liegen auf dem
nächsten 4er Offset, durch das packed wird er aber nach vorne auf einen
krummen Offset gezwungen wodurch der Compiler mehr umständlichen Code
beim Zugriff darauf generieren muss.
Du könntest den Zeitstempel direkt hinter das float legen (dort wäre er
aligned) und die beiden restliche bytes dann dahinter. Dann könntest Du
das packed weglassen.
PS: Deine byte-Zählung in den Kommentaren entspricht nicht (mehr) den
wahren Gegebenheiten.
PPS: Um auf alle Fälle sicher zu gehen könntest Du ein statisches assert
auf die von Hand ausgerechnete zu erwartete sizeof() des ungepacken
structs mit einbauen, so knallt es beim Kompilieren sofort wenn
versehentlich was verrutscht ist oder sich auf einem neuen Prozessor
andere Alignment-Regeln ergeben (unwahrscheinlich weil strenger als
self-aligned so ziemlich nirgends anzutreffen ist und weniger strenge
Regeln bei einem lückenlosen struct keine Änderung bewirken würden).
PPPS: auch wenn der Timestamp nach vorne gezogen wird hat das struct 2
bytes padding ganz am Ende weil die Gesamtgröße immer durch die Größe
des größten Members teilbar sein muß. Bei unvermeidbarem Padding füge
ich gerne selbst explizit Paddingbytes ein und benenne sie "reserved_1",
"reserved_2", etc., manchmal sind die bei späteren Erweiterungen dann
noch nützlich.