Forum: Mikrocontroller und Digitale Elektronik Diverse Variablen remanent machen


von Dirk F. (dirkf)


Lesenswert?

Hallo,
in meinem C-Projekt möchte ich verschiedene Varriablen beim Erkennen von 
Netzausfall ins Flash speichern und natürlich beim nächsten Einschalten 
wieder aus dem Flash laden.

Beispiel:
1
unsigned int Wert1;
2
float Wert2;
3
char Wert3;
Meine Speicher/Lade Routine kann 256 Bytes in einem Rutsch laden oder 
speichern.
Aber dazu müssen die Varriablen mit ihren verschiedenen Größen 
hintereinander im Speicher liegen.

Jetzt dache ich das irgendwie mit einem "union" zu machen.
Mit nur einer Varriable würde das je gehen:
1
union test {
2
   unsigned int Wert1;
3
   unsigned char zum_speichern[256];
4
   };

Jetzt würde ich mit speichern(zum_speichern); ja den INT Wert1 
abspeichern.

Aber wie kann man ein union basteln, wo mehrere verschiedene Varriablen 
hintereinander im Speicher abgelegt werden ?

: Bearbeitet durch Moderator
von ArnoNym (bergler)


Lesenswert?

Dirk F. schrieb:
> Aber wie kann man ein union basteln, wo mehrere verschiedene Varriablen
> hintereinander im Speicher abgelegt werden ?

Wozu das mit den Unions?

Eine Struktur definieren. Du kannst deine Speicherroutine aufrufen indem 
du ihr die Adresse der Struktur und die Länge übergibst (mit dem 
sizeof-Operator). Natürlich prüft man die Länge nach, vor dem Schreiben.
Dann kümmert sich der compiler darum, wo was steht.

Eventuell habe ich dein Problem aber nicht verstanden, das ist durchaus 
drin.

von Dirk F. (dirkf)


Lesenswert?

Hallo Bergler,
guter Hinweis.
Ist denn bei einer Struktur sicher, dass alle Elemente lückenlos 
hintereiander und in der richtigen Reihenfolge im Speicher liegen ?

: Bearbeitet durch User
von ArnoNym (bergler)


Lesenswert?

Dirk F. schrieb:
> Hallo Bergler,
> guter Hinweis.
> Ist denn bei einer Struktur sicher, dass alle Elemente hintereinander
> lückenlos im Speicher liegen ?

Lückenlos würde ich mich nicht sagen trauen. Z.B. wenn da ein bool drin 
ist, hat das garantiert nicht die Größe 1 Bit. Oder wenn man ein wirres 
Sammelsurium aus 32-Bit und 8-Bit Variablen deklariert, dann hängt auch 
vom Compiler ab, wie der das einsortiert.

Wie groß sie wirklich wird, sollte man darum immer den compiler mit 
sizeof ermitteln lassen. Wird ja nicht zur Laufzeit gemacht, kostet also 
nichts.

Aber die Variablen liegen schon hintereinander im Speicher, im Sinne von 
die Struktur belegt einen durchgehenden Block der größe sizeof(struktur) 
ab der Adresse der Struktur.
Man kann z.B. Dinge wie memcpy oder memset für eine Struktur verwenden. 
Oder sie auch einfach ins Flash schreiben (mit den 
Flash-Spezialfunktionen natürlich).

von Max D. (max_d)


Lesenswert?

gcc kann man anweisen die struktur so eng wie möglich zu packen: 
https://www.gnu.org/software/c-intro-and-ref/manual/html_node/Packed-Structures.html
Aber wenn es dir nur darum geht deine variablen im flash zu lagern 
machen "lücken" (ausser dem verschwendeten Platz) eigtl nicht aus...

von Rainer W. (rawi)


Lesenswert?

Dirk F. schrieb:
> verschiedene Varriablen

> die Varriablen

> mehrere verschiedene Varriablen

Da sträubt sich einem alles.
Das ist wie mit "Sattelit", was mit einem Sattel nun auch wirklich 
nichts zu tun hat, wenn man von den Stabilitätseigenschaften der 
Lagrangepunkte L1, L2 oder L3 absieht.

Vielleicht bringt ein Moderator wenigsten den Titel in eine sprachlich 
nicht ganz so peinliche Form.

von Dirk F. (dirkf)


Lesenswert?

Danke an Max und Bergler für die guten konstruktiven Hinweise, im 
Gegensatz zu anderen hier....

von Wastl (hartundweichware)


Lesenswert?

Dirk F. schrieb:
> Ist denn bei einer Struktur sicher, dass alle Elemente lückenlos
> hintereiander und in der richtigen Reihenfolge im Speicher liegen ?

Ist es denn sicher dass du diese Anforderung brauchst? Was soll
das für einen Sinn machen wenn du sowieso in der Struktur immer
eindeutig auf jedes Element zugreifen kannst? Man kann ja mittels
EEPROM Read/Write auf einzelne Elemente zugreifen, muss nicht
die Struktur als Ganzes im Speicher (RAM) stehen haben.

Und was soll es bringen die Variablen "in der richtigen
Reihenfolge im Speicher" zu haben?

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

Ok, ich hab mich dahingehend geirrt dass ich vom EEPROM
gesprochen habe während die Daten ja im Flash abgespeichert
werden sollen.

Im Flash speichern wird aber unter Umständen schwierig werden
wenn die Flash-Sektoren "gross" sind oder aber der freie Flash-
Bereich so klein ist dass ein Teil des Programms betroffen ist.

Bei meinen Schaltungen gibts es da immer ein extra EEPROM
sofern nicht schon eines im Controller (AVR) enthalten ist.
Kost' fast nix und ist weitaus komfortabler als Flash.

von Udo S. (urschmitt)


Lesenswert?

Die Frage ist doch erst mal was deine Speicher/Ladefunktion

Dirk F. schrieb:
> Meine Speicher/Lade Routine kann 256 Bytes in einem Rutsch laden oder
> speichern.

als Parameter verarbeiten kann.

Ansonsten ist eine Struktur aller benötigten Variablen der richtige Weg.
Ob da jetzt irgendwo eine Lücke ist oder nicht ist völlig egal (außer 
dir wird der Platz zu knapp) Die Lücke wird sowohl beim Speichern als 
auch beim Laden gleich sein solange du da die gleiche Struktur benutzt.


p.s. Man sollte lernbereit sein, auch wenn es die Rechtschreibung 
betrifft.

von Rolf M. (rmagnus)


Lesenswert?

Max D. schrieb:
> Aber wenn es dir nur darum geht deine variablen im flash zu lagern
> machen "lücken" (ausser dem verschwendeten Platz) eigtl nicht aus...

Außerdem kann es je nach µC sein, dass ein Zugriff mit unpassendem 
Alignment nicht geht und es dann zu einem Fehler kommt.

Wastl schrieb:
> Dirk F. schrieb:
>> Ist denn bei einer Struktur sicher, dass alle Elemente lückenlos
>> hintereiander und in der richtigen Reihenfolge im Speicher liegen ?
>
> Ist es denn sicher dass du diese Anforderung brauchst?

Sehr wahrscheinlich braucht er die nicht. Warum sollte es die 
Speicherfunktion jucken, ob da Lücken zwischen den Variablen sind? Die 
weiß ja nicht mal was von Variablen, sondern kopiert einfach nur einen 
Block aus Bytes 1:1 von A nach B. Das wäre zumindest das, was ich 
erwarten würde. Und wenn ich diesen Block später wieder Byte für Byte 
zurück nach A kopiere, ist dort natürlich alles wieder exakt gleich wie 
vorher. Reihenfolgen oder Lücken sind dafür völlig unerheblich.

Wastl schrieb:
> Ok, ich hab mich dahingehend geirrt dass ich vom EEPROM
> gesprochen habe während die Daten ja im Flash abgespeichert
> werden sollen.

Bisher wissen wir nicht einmal um was für eine µC-Familie es überhaupt 
geht und ob mit "Flash" der eingebaute Speicher oder ein externes 
Flash-Modul gemeint ist.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Dirk F. schrieb:
> Ist denn bei einer Struktur sicher, dass alle Elemente lückenlos
> hintereiander ... im Speicher liegen ?
Nein. Es kommt aufs Alignment an. Du solltest es für diesen Struct auf 1 
Byte stellen.

BTW @TO: bitte nicht Plenken! Ein Satzzeichen hat keine eigene eigene 
Zeile verdient.

: Bearbeitet durch Moderator
von Wastl (hartundweichware)


Lesenswert?

Rolf M. schrieb:
> Bisher wissen wir nicht einmal um was für eine µC-Familie es überhaupt
> geht und ob mit "Flash" der eingebaute Speicher oder ein externes
> Flash-Modul gemeint ist.

Wie Recht du doch hast.

Dirk F. schrieb:
> Ist denn bei einer Struktur sicher, dass alle Elemente lückenlos
> hintereiander und in der richtigen Reihenfolge im Speicher liegen ?

Eine stichhaltige Begründung für diese Anforderung wäre sehr
hilfreich. Oft werden solche Dinge einfach mal "ins Blaue"
gefordert ohne genau zu wissen warum.

von Dirk F. (dirkf)


Lesenswert?

Wastl schrieb:
> Ist es denn sicher dass du diese Anforderung brauchst? Was soll
> das für einen Sinn machen wenn du sowieso in der Struktur immer
> eindeutig auf jedes Element zugreifen kannst? Man kann ja mittels
> EEPROM Read/Write auf einzelne Elemente zugreifen, muss nicht
> die Struktur als Ganzes im Speicher (RAM) stehen haben.
Da muss ich bei jeder neuen remanenten Variable (dieses mal richtig 
geschrieben) meine Speicherroutine anpassen. Ist mir zu aufwendig und 
fehleranfällig.


> Und was soll es bringen die Variablen "in der richtigen
> Reihenfolge im Speicher" zu haben?
Wenn in einigen Jahren ein Firmware Update kommt, welche mit einer 
anderen Version des Compilers oder ein andere Compiler erstellt wurde, 
und dann die Anordnung der alten Daten im Flash nicht mit der u.U. neuen 
Anordnung übereinstimmt.

von Dirk F. (dirkf)


Lesenswert?

Rolf M. schrieb:
> Bisher wissen wir nicht einmal um was für eine µC-Familie es überhaupt
> geht und ob mit "Flash" der eingebaute Speicher oder ein externes
> Flash-Modul gemeint ist.

PIC32MZ  + SST26 Flash

von Wastl (hartundweichware)


Lesenswert?

Dirk F. schrieb:
> Wenn in einigen Jahren ein Firmware Update kommt, welche mit einer
> anderen Version des Compilers oder ein andere Compiler erstellt wurde,
> und dann die Anordnung der alten Daten im Flash nicht mit der u.U. neuen
> Anordnung übereinstimmt.

Deswegen die Verwendung einer Struktur. Und die Anweisung an
den Compiler die Struktur so zu "packen" wie man es braucht
und für richtig hält. Alignments gibt es auch noch als Option.

von Rolf M. (rmagnus)


Lesenswert?

Lothar M. schrieb:
> Dirk F. schrieb:
>> Ist denn bei einer Struktur sicher, dass alle Elemente lückenlos
>> hintereiander ... im Speicher liegen ?
> Nein. Es kommt aufs Alignment an. Du solltest es für diesen Struct auf 1
> Byte stellen.

Warum?

Dirk F. schrieb:
>> Und was soll es bringen die Variablen "in der richtigen
>> Reihenfolge im Speicher" zu haben?
> Wenn in einigen Jahren ein Firmware Update kommt, welche mit einer
> anderen Version des Compilers oder ein andere Compiler erstellt wurde,
> und dann die Anordnung der alten Daten im Flash nicht mit der u.U. neuen
> Anordnung übereinstimmt.

Die Reihenfolge der Elemente einer struct ist in C immer die, in der sie 
im Code definiert wurden. Ein Compiler, der die anders sortiert, würde 
an der Stelle massiv gegen den Standard verstoßen. Für einzelne 
Variablen, die nicht in einer struct sind, gilt das übrigens nicht.
Ich sehe zwei Dinge, die dir bei einem Compilerwechsel theoretisch (aber 
auch eher unwahrscheinlich) passieren können:
- Die Größen von Datentypen ändern sich - in dem Fall bringt dir ein 
Packen der Struktur aber nichts.
- Das Alignment ändert sich - dagegen kann das Packen helfen, falls der 
µC damit umgehen kann. Ich würde dann eher mit Füll-Elementen arbeiten, 
um damit das Alignment manuell zu erzielen.

Generell würde ich das (da eben eher unwahrscheinlich) nicht weiter 
bedenken, sondern einfach im Code ein paar static-asserts einbauen, die 
mir - falls so ein Fall doch mal nach einem Compilerwechsel auftritt - 
einen Fehler ausgeben. Dann kann man sich immer noch darum kümmern.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Dirk F. schrieb:
> Ist denn bei einer Struktur sicher, dass alle Elemente lückenlos
> hintereiander und in der richtigen Reihenfolge im Speicher liegen ?

Sie liegen exakt in der Reihenfolge, wie Du sie in der Struct deklariert 
hast. Das ist der Sinn einer Struct.
Es können allerdings Lücken auftreten, wenn der Compiler ein Alignment 
auf CPU-Datenbreite vorsieht. Daher sollte man vorzugsweise in der 
Struct die Variablen nach absteigendem Format anordnen (64Bit zuerst ... 
8Bit am Ende).

von Dirk F. (dirkf)


Lesenswert?

Peter D. schrieb:
> Daher sollte man vorzugsweise in der
> Struct die Variablen nach absteigendem Format anordnen (64Bit zuerst ...
> 8Bit am Ende).

Ja, mache ich so.  Danke an alle.

von Udo S. (urschmitt)


Lesenswert?

Dirk F. schrieb:
> Wenn in einigen Jahren ein Firmware Update kommt, welche mit einer
> anderen Version des Compilers oder ein andere Compiler erstellt wurde,
> und dann die Anordnung der alten Daten im Flash nicht mit der u.U. neuen
> Anordnung übereinstimmt.

Wenn das wichtig ist oder vielleicht sogar eine Übertragung der Daten zu 
einem anderen System, dann schreibt man sich dafür explizite Funktionen 
zur Serialisierung und Deserialisierung der Daten in/von einem Byte 
Array. Das Byte Array kann man dann speichern, kopieren, verschicken.

Dann liegt es komplett in deiner Hand.

von Flunder (flunder)


Lesenswert?

Rolf M. schrieb:
> Die Reihenfolge der Elemente einer struct ist in C immer die, in der sie
> im Code definiert wurden. Ein Compiler, der die anders sortiert, würde

ROTFL da glaubt noch einer an das Gute im Programmierer. Nein, das ist 
laut Standard "implementation defined", also so wie es den Erstellern 
des Compilers in den Kram gepasst hat. Wenn Software, die mit haargenau 
dem selben Compiler übersetzt wurde, die Daten schreibt und auch liest, 
ist man ziemlich auf der sicheren Seite, dass alles wieder da raus 
kommt, wo es rein ging. Ich kann mich aber ernsthaft an ein 
Compilerupdate (also selber Compilerhersteller, selber Produktname) 
erinnern, nach dem die Reihenfolge der Elemente in den Strukturen 
umgedreht war.

Und da sind wir wieder bei den Anforderungen an die Software des 
Fragestellers. Ist denn überhaupt verlangt, dass nach einem Update (noch 
schwieriger : nach einem Downgrade) seiner Software, die alten Daten 
noch aus dem NVM gelesen werden können ? Dabei könnten ja Daten 
hinzukommen oder wegfallen.

Wenn das nicht gefordert ist : blockiere die Übernahme von Daten in 
einem anderen (unbekannten) Format durch eine Versionsnummer, die vor 
den Daten abgelegt wird und vertraue auf eine Struktur.

Wenn es denn sein muss, mach Dir ganz viele Gedanken, wie Du mit allen 
Möglichen Sonderfällen umgehst und schreibe die Routinen zur 
Serialisierung und Deserialisierung komplett selbst.

von Wastl (hartundweichware)


Lesenswert?

"Foolproof" wäre es, per Linker Description Definition jeder
Variable einen eigenen Flash-Speicherraum (soweit der Speicher-
Platz im Controller sein soll) zu reservieren. Für jede neu
hinzugekommene Variable dann einen neuen Speicherraum definieren.

Viel Spass ....

von Oliver R. (superberti)


Lesenswert?

Hi,

bei erhöhten Anforderungen an die "Mutationsrate" der abzuspeichernden 
Variablen und sogar bei eventuellen Architekturänderungen bietet sich 
CBOR (z.B. tinycbor von Intel) als Speicherformat für Deine Werte an.
Über irgendwelche Strukturformate Deines Compilers musst Du Dir dann 
jedenfalls keine Gedanken mehr machen, das ist alles in CBOR definiert.
In 256 Bytes lässt sich mit CBOR so Einiges unterbringen.

Gruß,
Oliver

von Rahul D. (rahul)


Lesenswert?

Peter D. schrieb:
> Es können allerdings Lücken auftreten, wenn der Compiler ein Alignment
> auf CPU-Datenbreite vorsieht. Daher sollte man vorzugsweise in der
> Struct die Variablen nach absteigendem Format anordnen (64Bit zuerst ...
> 8Bit am Ende).

Dagegen hilft doch ein
1
#pragma pack(1)
2
struct foo
3
{
4
  ...
5
}

Führt zwar zu (etwas) erhöhtem Ausführungsaufwand, funktionert aber in 
Bezug auf linearen Speicher einwandfrei.

Flunder schrieb:
> Und da sind wir wieder bei den Anforderungen an die Software des
> Fragestellers. Ist denn überhaupt verlangt, dass nach einem Update (noch
> schwieriger : nach einem Downgrade) seiner Software, die alten Daten
> noch aus dem NVM gelesen werden können ? Dabei könnten ja Daten
> hinzukommen oder wegfallen.

Wenn das Problem auftritt, kann man auch sagen: "Pech gehabt!"
PS: Du plenkst.

Wir arbeiten hier mit mindestens zwei verschiedenen Compilern (arm und 
AVR) und verwenden ohne Probleme systemübergreifende structs.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> Lothar M. schrieb:
>> Dirk F. schrieb:
>>> Ist denn bei einer Struktur sicher, dass alle Elemente lückenlos
>>> hintereiander ... im Speicher liegen ?
>> Nein. Es kommt aufs Alignment an. Du solltest es für diesen Struct auf 1
>> Byte stellen.
> Warum?
Sonst werden u.U. bei einem 32-Bit-Prozessor hinter jedes einzelne Byte 
3 "Füllbytes" gelegt, damit wieder eine D-Wort-Adresse herauskommt und 
der Prozessor optimal schnell auf den Speicher zugreifen kann.

Ein Struct aus 65 Bytes passt dann auf einmal überraschenderwiese nicht 
mher in die 256-Byte-Page...

- 
https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html

Rahul D. schrieb:
> Wir arbeiten hier mit mindestens zwei verschiedenen Compilern (arm und
> AVR) und verwenden ohne Probleme systemübergreifende structs.
Wenn man dann aber mal mit Litte- und Big-Endian zu tun bekommt 
(ARM<->x86), und diese structs dazwischen austauschen muss, dann 
erreicht man einen neuen Level...  ;-)

von Dirk F. (dirkf)


Lesenswert?

Lothar M. schrieb:
> Du solltest es für diesen Struct auf 1 Byte stellen.

Und wie macht man das ?
1
#pragma pack(1)
2
3
typedef struct                 
4
    {
5
    //    Variable                 Adresse    Beschreibung
6
    unsigned int ds;            // 0...3 Gewählter Datensatz 
7
    unsigned char Taktzaehler;  // 4 Zykluszähler  
8
    }
9
    REMANENT;
10
11
// Globale Variable r der Struktur REMANENT erstellen
12
REMANENT r;     
13
14
r.ds = 1;           // Gewählter Datensatz initialisieren
15
r.Taktzaehler = 3;  // Anzahl Zyklen

: Bearbeitet durch User
von Rahul D. (rahul)


Lesenswert?

Lothar M. schrieb:
> Wenn man dann aber mal mit Litte- und Big-Endian zu tun bekommt
> (ARM<->x86), und diese structs dazwischen austauschen muss, dann
> erreicht man einen neuen Level...  ;-)

Wir verwenden keine X86 oder PowerPC als Senke.
Die Structs bzw. .h-Dateien werden von einem PC-Programm generiert - 
dabei sind die Endians aber egal, da am Ende nur Text herauskommt.

Wenn ich mich recht erinnere kann man beim arm auch zwischen BigEndian 
und LittleEndian umschalten (nie benutzt, kann auch woanders 
herstammen).

Auf solche Probleme kann man natürlich auch vorher testen...

von Udo S. (urschmitt)


Lesenswert?

Lothar M. schrieb:
> man einen neuen Level

Den Level hatte man schon mit einfachen TCP Verbindungen.

Rahul D. schrieb:
> Auf solche Probleme kann man natürlich auch vorher testen...

Wenn man zwischen mehreren Systemen Daten austauscht kann man das auch 
einfach gleich richtig machen.
Stichwort Serialisieren/deserialisieren wurde genannt.
Nächste Stufe wäre dafür ein definiertes Format zu nehmen, wurde auch 
schon genannt:

Oliver R. schrieb:
> bietet sich
> CBOR (z.B. tinycbor von Intel) als Speicherformat für Deine Werte an.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Dirk F. schrieb:
> Lothar M. schrieb:
>> Du solltest es für diesen Struct auf 1 Byte stellen.
> Und wie macht man das ?
Genau so wie aufgezeigt:
#pragma pack(1)

Rahul D. schrieb:
> beim arm auch zwischen BigEndian und LittleEndian umschalten
Kann man, sollte man aber nicht, weil man dann wieder aus der Reihe 
tanzt.

Udo S. schrieb:
> Lothar M. schrieb:
>> man einen neuen Level
> Den Level hatte man schon mit einfachen TCP Verbindungen.
Du und ich hatten den, aber viele andere noch nicht... ;-)

: Bearbeitet durch Moderator
Beitrag #7603106 wurde vom Autor gelöscht.
von Rolf M. (rmagnus)


Lesenswert?

Lothar M. schrieb:
>>> Nein. Es kommt aufs Alignment an. Du solltest es für diesen Struct auf 1
>>> Byte stellen.
>> Warum?
> Sonst werden u.U. bei einem 32-Bit-Prozessor hinter jedes einzelne Byte
> 3 "Füllbytes" gelegt, damit wieder eine D-Wort-Adresse herauskommt und
> der Prozessor optimal schnell auf den Speicher zugreifen kann.

Ich habe noch nicht gehört, dass auf einem Prozessor ein 8-Bit-Typ ein 
32-Bit-Alignment bekäme - oder überhaupt, dass Typen ein Alignment 
bräuchten, das größer ist als der Typ selbst. Hast du mal ein Beispiel 
für sowas? Ich halte das eher für ungewöhnlich, gerade bei µCs, wo das 
ja übelste Platzverschwendung wäre.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Flunder schrieb:
> Rolf M. schrieb:
>> Die Reihenfolge der Elemente einer struct ist in C immer die, in der sie
>> im Code definiert wurden. Ein Compiler, der die anders sortiert, würde
>
> ROTFL da glaubt noch einer an das Gute im Programmierer. Nein, das ist
> laut Standard "implementation defined", also so wie es den Erstellern
> des Compilers in den Kram gepasst hat.

Die Reihenfolge ist garantiert:

C99 TC3 §6.7.2.1p13 "structure and union specifiers":

"Within a structure object, the non-bit-field members and the units in 
which bit-fields reside have addresses that increase in the order in 
which they are declared."

Alignment ist da ein anderes Thema.

von Rahul D. (rahul)


Lesenswert?

Udo S. schrieb:
> Den Level hatte man schon mit einfachen TCP Verbindungen.

Probleme nit denen man sich beschäftigt, wenn sie auftreten...
Das wäre in meinem Fall eine Erweiterung, die bis jetzt noch nicht 
benötigt wurde. Aber auch sowas sollte man gelöst bekommen...

Danke für den Hinweis.

Lothar M. schrieb:
> Kann man, sollte man aber nicht, weil man dann wieder aus der Reihe
> tanzt.

Bei den "Experten", die ich schon kennenlernen durfte, wäre sowas 
definitiv möglich, wenn sie dadurch einfacher zum Ziel kommen.
Die andere Seite ist natürlich das Problem der andere...

von Dirk F. (dirkf)


Lesenswert?

Lothar M. schrieb:
> Genau so wie aufgezeigt:
> #pragma pack(1)

Habe es eben ausprobiert  (PIC32MZ + XC32 Compiler):
Bei  #pragma pack(1) oder  #pragma pack(2) stürzt der Prozessor ab.
#pragma pack(4) geht, bringt dann aber nichts mehr.

von Bruno V. (bruno_v)


Lesenswert?

Dirk F. schrieb:
> Habe es eben ausprobiert  (PIC32MZ + XC32 Compiler):
> Bei  #pragma pack(1) oder  #pragma pack(2) stürzt der Prozessor ab.
> #pragma pack(4) geht, bringt dann aber nichts mehr.

Es gibt Prozessoren, da dürfen variablen nicht über 2 oder 4 
byte-Grenzen liegen. Also ein uint32 darf nur an Adresse 0, 4, 8, 12 ... 
anfangen, nicht 1,2 oder 3.

Das Problem löst Peters Anordnung im struct automatisch, egal welches 
pragma.

Warum erlaubt der Compiler es dann, wenn der Prozessor abstürzt? Naja, 
der Trick ist dann, mit memcpy darauf zuzugreifen. Das geht immer.

von Rahul D. (rahul)



Lesenswert?

Dirk F. schrieb:
> Bei  #pragma pack(1) oder  #pragma pack(2) stürzt der Prozessor ab.

Der Prozessor stürzt ab? Dem sollte das #pragma egal sein: Es ist eine 
Compiler-Anweisung, die der Compiler dazu verwendet Programmcode nach 
bestimmten Regeln zu übersetzen.
Wenn der das nicht kann (, weil er es nicht kennt), dann muss man Peters 
Weg gehen.
Ansonsten sollte die Compiler-Doku helfen (siehe Anhang)...
https://ww1.microchip.com/downloads/aemDocuments/documents/DEV/ProductDocuments/UserGuides/MPLAB-XC32-Compiler-UG-PIC32M-DS-50002799.pdf

Edit: Bild und Datenblattlink hinzugefügt.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Dirk F. schrieb:
> Lothar M. schrieb:
>> Genau so wie aufgezeigt:
>> #pragma pack(1)
>
> Habe es eben ausprobiert  (PIC32MZ + XC32 Compiler):
> Bei  #pragma pack(1) oder  #pragma pack(2) stürzt der Prozessor ab.
> #pragma pack(4) geht, bringt dann aber nichts mehr.

Das ist exakt das, was ich anfangs damit meinte:

Rolf M. schrieb:
> Außerdem kann es je nach µC sein, dass ein Zugriff mit unpassendem
> Alignment nicht geht und es dann zu einem Fehler kommt.

Rahul D. schrieb:
> Der Prozessor stürzt ab? Dem sollte das #pragma egal sein:

Er stürzt ja genau genommen nicht beim Pragma selbst ab, sondern bei dem 
Code, der unter dessen Verwendung generiert wurde.

> Es ist eine Compiler-Anweisung, die der Compiler dazu verwendet
> Programmcode nach bestimmten Regeln zu übersetzen.

Ja, nach einer Weise, die in diesem Fall zum verwendeten Prozessor 
inkompatibel ist.

von Rahul D. (rahul)


Lesenswert?

Rolf M. schrieb:
> Ja, nach einer Weise, die in diesem Fall zum verwendeten Prozessor
> inkompatibel ist.

Microchip mal wieder...
Compiler-Bug-Report schreiben! (oder auf Controller und Compiler 
umsteigen, die das können.)

von 900ss (900ss)


Lesenswert?

Rolf M. schrieb:
> Dirk F. schrieb:
>> Lothar M. schrieb:
>>> Genau so wie aufgezeigt:
>>> #pragma pack(1)
>>
>> Habe es eben ausprobiert  (PIC32MZ + XC32 Compiler):
>> Bei  #pragma pack(1) oder  #pragma pack(2) stürzt der Prozessor ab.
>> #pragma pack(4) geht, bringt dann aber nichts mehr.
>
> Das ist exakt das, was ich anfangs damit meinte:
>


Solange man nur über die struct oder Zeiger auf die struct auf die 
Member zugreift, sollte es auch mit pack(1) funktionieren. Der Compiler 
generiert dann bei einem unaligned access für z.B. uint32 automatisch 4 
Bytezugriffe und setzt die Werte richtig zusammen. Wenigstens GCC macht 
das. Ob das im Standard definiert ist, weiß ich gerade nicht.
Wenn der Compiler irgendwann das Wissen über die unaligned Adresse 
verliert, weil man z.B mit uint32 *p = &str.counter arbeitet und dann p 
an eine Funktion übergibt, die uint32 * erwartet und da dann so 
zugreift, dann wird es zum Absturz führen. Hier hat der Compiler kein 
Wissen mehr über die Struktur und generiert einen 32 Bit Wortzugriff der 
dann unter Umständen unaligned ist. Dann fällt die Kuh um.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Rahul D. schrieb:
> Rolf M. schrieb:
>> Ja, nach einer Weise, die in diesem Fall zum verwendeten Prozessor
>> inkompatibel ist.
>
> Microchip mal wieder...
> Compiler-Bug-Report schreiben! (oder auf Controller und Compiler
> umsteigen, die das können.)

Das ist kein Compiler-Bug. Du hast einfach ein Feature falsch benutzt. 
Und wenn du nur mit µCs umgehen kannst, die keine 
Alignment-Anforderungen haben, dann machst du was falsch.

900ss schrieb:
> Solange man nur über die struct oder Zeiger auf die struct auf die
> Member zugreift, sollte es auch mit pack(1) funktionieren. Der Compiler
> generiert dann bei einem unaligned access für z.B. uint32 automatisch 4
> Bytezugriffe und setzt die Werte richtig zusammen.

Ist dann halt im Vergleich zu einem normalen Zugriff elends langsam.

> Wenigstens GCC macht das. Ob das im Standard definiert ist, weiß ich
> gerade nicht.

Ist es nicht. Im Standard ist auch das Pragma nicht definiert. Dort ist 
nur definiert, dass du die Alignment-Anforderungen einhalten musst. Wenn 
du den Compiler über eine non-Standard-Erweiterung explizit anweist, das 
zu missachten, musst du eben genau wissen, was du tust, sonst kann es 
knallen.

> Wenn der Compiler irgendwann das Wissen über die unaligned Adresse
> verliert, weil man z.B mit uint32 *p = &str.counter arbeitet und dann p
> an eine Funktion übergibt, die uint32 * erwartet und da dann so
> zugreift, dann wird es zum Absturz führen. Hier hat der Compiler kein
> Wissen mehr über die Struktur und generiert einen 32 Bit Wortzugriff der
> dann unter Umständen unaligned ist. Dann fällt die Kuh um.

Jo, Packing ist und bleibt halt eine Frickellösung.

: Bearbeitet durch User
von 900ss (900ss)


Lesenswert?

Rolf M. schrieb:
> Jo, Packing ist und bleibt halt eine Frickellösung.

Manchmal hat man kaum eine Wahl. Ich mach dann "intern" alles ohne 
packing und für die Interfaces (hier das Flash) explizit 
Zugriffsfunktionen, die das dann richtig regeln. Damit ist dann das 
packing sauber gekapselt.

In diesen Interface-Funktionen kann man auch ohne struct und pack 
arbeiten. Damit ist man nicht compilerabhängig.

: Bearbeitet durch User
von Rahul D. (rahul)


Lesenswert?

Rolf M. schrieb:
> Jo, Packing ist und bleibt halt eine Frickellöstung.
Die das Leben erleichtert...

Rolf M. schrieb:
> Das ist kein Compiler-Bug. Du hast einfach ein Feature falsch benutzt.
Für mich ist es ein Bug, wenn andere Compiler es umsetzen.
Es ist kein Fehler-Bug; eher ein Missing-Feature-Bug.
Es wäre für mich ein Ausschlusskriterium den Controller / Compiler zu 
verwenden.

> Und wenn du nur mit µCs umgehen kannst, die keine
> Alignment-Anforderungen haben, dann machst du was falsch.
Was hat das eine mit dem anderen zu tun?
Natürlich ist es eine Software-Lösung, die dazu führt, dass die 
Ausführungsgeschwindigkeit sich verlangsamt.
Nach deiner Aussage darf man also für Modbus-Geschichten maximal 
16-Bit-Controller verwenden?! (oder auf einem 32-Bit-System 
Speicherplatz verwendet).

von 900ss (900ss)


Lesenswert?

Rolf M. schrieb:
> Ist dann halt im Vergleich zu einem normalen Zugriff elends langsam.

Einen Tod musst du halt sterben.

Es gibt nicht immer "normale" Zugriffe für unaligned Daten. Siehe als 
Beispiel Ethernetpakete. Ob du die Zugriffe jetzt per packed struct oder 
selber einzelne Bytezugriffe machst, ist dann egal.
Bei packed struct nimmt einem der Compiler die Arbeit ab. Wenigstens 
GCC.

von 900ss (900ss)


Lesenswert?

Rahul D. schrieb:
> Es wäre für mich ein Ausschlusskriterium den Controller / Compiler zu
> verwenden.

Wenn du(!) diese Entscheidung treffen darfst, dann ist es ok. In der 
Regel wird das kaum der Fall sein.

von Rolf M. (rmagnus)


Lesenswert?

Rahul D. schrieb:
> Rolf M. schrieb:
>> Jo, Packing ist und bleibt halt eine Frickellöstung.
> Die das Leben erleichtert...

… oder erschwert, je nach dem.

> Rolf M. schrieb:
>> Das ist kein Compiler-Bug. Du hast einfach ein Feature falsch benutzt.
> Für mich ist es ein Bug, wenn andere Compiler es umsetzen.
> Es ist kein Fehler-Bug; eher ein Missing-Feature-Bug.

Wie 900ss schon schrieb, kann es aber gar nicht überall funktionieren. 
Man hat also ein Feature, das nur so halb funktioniert.

>> Und wenn du nur mit µCs umgehen kannst, die keine
>> Alignment-Anforderungen haben, dann machst du was falsch.
> Was hat das eine mit dem anderen zu tun?
> Natürlich ist es eine Software-Lösung, die dazu führt, dass die
> Ausführungsgeschwindigkeit sich verlangsamt.

Du hast aber empfohlen, den Controller zu wechseln, um das Problem zu 
umgehen.

> Nach deiner Aussage darf man also für Modbus-Geschichten maximal
> 16-Bit-Controller verwenden?! (oder auf einem 32-Bit-System
> Speicherplatz verwendet).

Was hat jetzt Modbus damit zu tun?

von Rahul D. (rahul)


Lesenswert?

Rolf M. schrieb:
> Was hat jetzt Modbus damit zu tun?
Wir verwenden Modbus-RTU mit packed structs problemlos auf einem 
STM32...

Rolf M. schrieb:
> Du hast aber empfohlen, den Controller zu wechseln, um das Problem zu
> umgehen.
Wenn man es einfacher haben will, und die Möglichkeit hat, würde ich 
umsteigen (Der Spruch bezieht sich auf mich - das ist keine Empfehlung 
für andere; höchstens ein Lösungsvorschlag unter o.g. Bedingungen).

900ss schrieb:
> Wenn du(!) diese Entscheidung treffen darfst, dann ist es ok. In der
> Regel wird das kaum der Fall sein.

Bei privaten Projekten darf ich das und bei Projekten in der Firma muss 
man notfalls etwas diskutieren. Wir sind kein Konzern...

von Dirk F. (dirkf)


Lesenswert?

Rahul D. schrieb:
>> Du hast aber empfohlen, den Controller zu wechseln, um das Problem zu
>> umgehen.
> Wenn man es einfacher haben will, und die Möglichkeit hat, würde ich
> umsteigen

Also wegen so einem Mini-Problem den Controller zu wechseln scheidet 
aus.
Allein wegen der Einarbeitungszeit.

Dann werde ich halt meine structs sauber gliedern und manuell auf die 
4-Byte Grenze achten.

von Rolf M. (rmagnus)


Lesenswert?

Rahul D. schrieb:
> Wenn man es einfacher haben will, und die Möglichkeit hat, würde ich
> umsteigen (Der Spruch bezieht sich auf mich - das ist keine Empfehlung
> für andere; höchstens ein Lösungsvorschlag unter o.g. Bedingungen).

Naja, ob's jetzt einfacher ist, den Compiler und den µC zu wechseln als 
seinen Code so zu schreiben, dass er ohne Packing auskommt, sei mal 
dahingestellt.

900ss schrieb:
> Rolf M. schrieb:
>> Ist dann halt im Vergleich zu einem normalen Zugriff elends langsam.
>
> Einen Tod musst du halt sterben.

Ja, klar. Solange man, wie du schreibst, diesen Teil rein auf die 
Schnittstelle begrenzt und nicht überall im Code auch intern die 
gepackten Strukturen nutzt, fällt der Aufwand so oder so an, auch wenn 
man's von Hand macht.

von Bruno V. (bruno_v)


Lesenswert?

Bei RISC-Prozessoren ist es üblich, dass Variablen in einem Zugriff 
gelesen können werden müssen.

Wer da ein #pack1 will (z.B. für den Austausch von Daten), weiß das und 
berücksichtigt das. Es gibt ja genügend Lösungen (hier schon genannt):

 * Sortieren nach Größe (sie Peter)
 * Zugriff mit memcpy
 * Zugriff byteweise (z.B. Versenden per Uart)

Das kann auch kein GCC richtigen.

von Rahul D. (rahul)


Lesenswert?

Rolf M. schrieb:
> Naja, ob's jetzt einfacher ist, den Compiler und den µC zu wechseln als
> seinen Code so zu schreiben, dass er ohne Packing auskommt, sei mal
> dahingestellt.

Dann darf ich mich einfach glücklich schätzen, dass unsere Controller 
und Compiler das unterstüzen.

von Pit S. (pitschu)


Lesenswert?

Eine bewährte Methode, die ich in allen Projekten mit dem Bedarf an 
reset- resistenten Variablen benutze ist diese: In der RAM section des 
Linkers habe ich eine Section mit dem Namen PS_PARARAM definiert. Alle 
Variablen, die überleben sollen werden wie folgt irgendwo global im 
C-Code definiert (Beispiel):

__attribute__((section(".PS_PARARAM"))) char         softapPassword[32] 
= "1234567890";

Eine Funktion SaveParams() sichert die gesamte Section ins Flash und 
eine entsprechende Funktion reloadParams() lädt die gesamte Section vom 
Flash. Bei Start des Programms wird reloadParams() ausgeführt. Die 
Funktion prüft über einen Hashwert, ob der Flash jemals gefüült wurde. 
Falls nicht, wird auch nichts geladen und die Variablen im RAM behalten 
ihren Initialwert. Die sporadisch aufgerufene Funktion saveParams() 
prüft, ob sich Variablen geändert haben (über einen Hash) und flashed 
falls der Hashwert sich ändert. SaveParams() kann man natürlich auch 
jederzeit aufrufen, um ein Speichern zu erzwingen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.