Forum: Mikrocontroller und Digitale Elektronik STM32: Adresse mit Rest als uint32_tinterpretieren


von Arno (Gast)


Lesenswert?

Hallo,

ich habe gehört, dass ARM es nicht mögen und mit einem Fehler reagieren, 
wenn man eine Adresse als Pointer auf eine 32Bit Adresse interpretieren 
möchte, diese jedoch kein vielfaches von 4 ist.

Beispielsweise habe ich ein Array aus uint8_t und möchte Element 3 bis 6 
als 32 Bit Wert interpretieren.

Dann kriege ich hier leider einen infinite_loop. Mache ich den folgenden 
Zugriff auf eine Variable mit dem Startelement 0,4,8,16,... geht das 
wunderbar.
1
uint8_t Array[100];
2
uint8_t x;
3
4
*((uint32_t *)&Array[3]) = (uint32_t)&x; // geht nicht
5
*((uint32_t *)&Array[4]) = (uint32_t)&x; // geht

Wie löse ich dieses Dilemma?

von Hannes (Gast)


Lesenswert?

Du nimmst nen 8 Bit datentype und willst den als 32 bit interpretieren? 
Nimm doch gleich nen 32er typ.

von Arno (Gast)


Lesenswert?

Und was mache ich, wenn ich diese wahllos unvorhersehbar gemischt in 
einem Array speichern möchte? Mal 16,32,8 bit.

von Zerleger (Gast)


Lesenswert?

Arno schrieb:
> Und was mache ich, wenn ich diese wahllos unvorhersehbar gemischt in
> einem Array speichern möchte? Mal 16,32,8 bit.

Vorher zerlegen und die Teile dann richtig aligned speichern.

von Jim M. (turboj)


Lesenswert?

Arno schrieb:
> ich habe gehört, dass ARM es nicht mögen

Welche ARM? Von den ARM µCs haben nur die Cortex-M0 das Problem, weil 
sie als Armv6-m tatsächlich nur alinged Addressen ansteuern können.

Cortex-M3 ff. sind aber Armv7-m und können das sehr wohl (allerdings mit 
1-2 Straftakten).

von Dr. Sommer (Gast)


Lesenswert?

Cortex-M3/4 können das, man kann es aber abschalten, um solche Fehler zu 
finden (gibt dann eine Exception). Solcher Code ist nämlich in C und C++ 
verboten.

Details und wie man es korrekt macht findet sich unter 
Serialisierung.

von A. S. (Gast)


Lesenswert?

memcpy (geht immer und ist genau dafür gemacht!)

unsigned long kartoffeln;
unsigned char uca[1000];
int idx = 17+4;

/* rin inne Kartoffeln*/
memcpy(kartoffeln, uca+idx, sizeof(kartoffeln));
/* aus ausse Kartoffeln*/
memcpy(uca+idx+200, kartoffeln, sizeof(kartoffeln));

von Dr. Sommer (Gast)


Lesenswert?

A. S. schrieb:
> memcpy (geht immer und ist genau dafür gemacht!)

Aber das Ergebnis ist Plattform abhängig und der Code daher nicht 
portabel...

von Bernd K. (prof7bit)


Lesenswert?

Dr. Sommer schrieb:
> A. S. schrieb:
>> memcpy (geht immer und ist genau dafür gemacht!)
>
> Aber das Ergebnis ist Plattform abhängig und der Code daher nicht
> portabel...

OP hat im Titel die Problemdomäne auf STM32 eingeschränkt. Grundsätzlich 
ist die Methode mit memcpy portabel zu allen Architekturen der selben 
Endianness. Er soll die 4 Bytes einfach in den Speicher eines uint32 
kopieren und fertig.

Lösung 2:

Er soll sich vorher Gedanken machen so daß die Daten nicht wie Kraut und 
Rüben zu liegen kommen sondern jedes Wort immer schön an einem 
vielfachen seiner eigenen Länge ausgerichtet sein wird. Dann kann er 
einfach ein union aus nem byte-array und nem struct nehmen und direkt 
drauf zugreifen, das wäre die eleganteste Lösung, geht aber eben auch 
nur innerhalb der selben Endianness.

Strategien um das struct zu entwerfen: entweder stur nach Größe 
sortieren, die größten zuerst, dann die nächst kleineren, usw. Oder wenn 
zusammengehöriges zusammen bleiben soll dann eben von Hand genau so 
anordnen daß jedes Element an einem ganzzahligen Vielfachen seiner 
eigenen Länge zu liegen kommt, erforderlichenfalls an entsprechnder 
Stelle explizit Dummy-Bytes als Platzhalter einfügen wo sonst Padding 
entstehen würde. Das geht eigentlich recht fix, die Regeln sind simpel 
und alle ABIs der letzten zig Jahre und auch alle zukünftigen haben die 
selben Alignment-Regeln.

Zur Sicherheit vielleicht noch ein static assert auf das erwartete 
sizeof des structs machen um einen eventuellen Bruch der Regeln in der 
Zukunft einfach detektieren zu können (natürlich darf er es NICHT als 
packed deklarieren sonst ist es Essig mit detektieren einer 
Regeländerung mittels sizeof).

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Bernd K. schrieb:
> OP hat im Titel die Problemdomäne auf STM32 eingeschränkt.

Es wäre aber denkbar dass man den Code irgendwann mal woanders hin 
portieren könnte.

Bernd K. schrieb:
> Grundsätzlich ist die Methode mit memcpy portabel zu allen Architekturen
> der selben Endianness.

Und dem selben Padding und dem selben Vorzeichen Format (teilweise), 
falls man auch vorzeichenbehaftete Integer verwendet.

Bernd K. schrieb:
> Dann kann er einfach ein union aus nem byte-array und nem struct nehmen
> und direkt drauf zugreifen, das wäre die eleganteste Lösung

Dies ist in C Implementation defined (nicht portabel) und in C++ ganz 
verboten. Also nicht sehr elegant.

Bernd K. schrieb:
> alle ABIs der letzten zig Jahre und auch alle zukünftigen haben die
> selben Alignment-Regeln.

Sicher? AVR z.B. hat gar keine Alignment Anforderungen, ARM schon.

Die sicherste Lösung ist die Verwendung von Bitshifts. Die funktionieren 
auf vorzeichenlosen Integern garantiert immer überall gleich. Steht auch 
alles im verlinkten Artikel...

von W.S. (Gast)


Lesenswert?

Arno schrieb:
> *((uint32_t *)&Array[4]) = (uint32_t)&x; // geht
>
> Wie löse ich dieses Dilemma?

Wirklich am besten fährt man in C, wenn man seine Algorithmen so 
schreibt, daß man dazu möglichst keine Cast's benötigt.

Bedenke mal eines: mit einem Cast zwingt man den Compiler zu etwas, 
gegen das er sich eigentlich sträubt - und das allerhöchstwahrscheinlich 
aus gutem Grunde.

Also halte dich besser daran:

Bernd K. schrieb:
> Lösung 2:
>
> Er soll sich vorher Gedanken machen so daß die Daten nicht wie Kraut und
> Rüben zu liegen kommen

Sehe ich ganz genauso. Wenn man merkt, daß man mit Casts um sich 
schmeißen muß wie ein Anarchist die Bomben, dann sollte man seinen 
ganzen Algorithmus in die Tonne werfen und sich einen besseren 
ausdenken.

W.S.

von D. H. (Gast)


Lesenswert?

Dr. Sommer schrieb:

> Sicher? AVR z.B. hat gar keine Alignment Anforderungen, ARM schon.

Seit wann kann AVR z.b. 5 Bit schreiben?
Ein Alignment auf Bytes ist völlig willkürlich.

von Dr. Sommer (Gast)


Lesenswert?

D. H. schrieb:
> Ein Alignment auf Bytes ist völlig willkürlich.

Nein, Speicheradressen sind immer in Bytes. Ein Byte kann aber mehr als 
8 Bit haben.

von A. S. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Aber das Ergebnis ist Plattform abhängig und der Code daher nicht
> portabel...

Nein. Zur Aufgabenstellung ist memcpy immer portabel und richtig: die 4 
folgenden Bytes sollen ein 32bit wert sein. Dass man sie ggf nochmals 
umformen muss, weil sie anders kodiert wurden, ist davon unabhängig. 
Hier ging es um das alignment, nicht um endian oder Repräsentation.

von D. H. (Gast)


Lesenswert?


von Dr. Sommer (Gast)


Lesenswert?

D. H. schrieb:
> Dr. Sommer schrieb:
> Nein, Speicheradressen sind immer in Bytes. Ein Byte kann aber mehr als
> 8 Bit haben.
>
> https://en.wikipedia.org/wiki/12-bit
> https://en.wikipedia.org/wiki/18-bit
> https://en.wikipedia.org/wiki/4-bit
> https://en.wikipedia.org/wiki/36-bit

Ohne ausformulierte Sätze habe ich keine Ahnung, was du mir sagen 
willst. Vermutlich willst du nur trollen und langwierige Erklärungen 
provozieren.

A. S. schrieb:
> Hier ging es um das alignment, nicht um endian oder Repräsentation

Okay, streng genommen richtig. Ich wollte halt ggf. später auftretenden 
Problemen vorgreifen und eine komplett-Lösung vorschlagen. Gerade in C 
ist es eben so, dass das, was man machen will, nicht immer das richtige 
ist, und alternative Herangehensweisen sinnvoll sein können.

Außerdem ist erst memcpy und dann tauschen auch nicht kürzer als 
Bitshifts.

von A. S. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Außerdem ist erst memcpy und dann tauschen auch nicht kürzer als
> Bitshifts.

Der TO wollte ab Adresse a n Bytes. Die Aufgabe ergibt sich auch, wenn 
man structs kopieren will.

Bitshiften vermeidet das Problem implizit, weil es nur Bytes kopiert. 
Die Aufgabe ist aber eine andere: von einer bekannten KODIERUNG in eine 
andere zu übersetzen. Wir wissen aber darüber nichts. Und ansehen kann 
man es den Daten auch nicht.

Und ja, ich halte es für ein echtes Problem, wenn Leute sich dann selber 
Byte-zugriffs-mechanismen bauen, weil sie denken, memcpy ist zu langsam.

Da wird dann gecastet statt stur &x, sizeof(x)

von Dr. Sommer (Gast)


Lesenswert?

A. S. schrieb:
> Die Aufgabe ist aber eine andere: von einer bekannten KODIERUNG in eine
> andere zu übersetzen.

Und genau das geht mit memcpy nicht, weil es die Bytes nicht tauscht und 
kein Padding beachtet.

A. S. schrieb:
> Und ja, ich halte es für ein echtes Problem, wenn Leute sich dann selber
> Byte-zugriffs-mechanismen bauen, weil sie denken, memcpy ist zu langsam.

Das ist nochmal ein ganz anderes Problem. memcpy kann ja auch ggf. 
wegoptimiert werden.

von Arno (Gast)


Lesenswert?

Danke alle, ich habe jetzt erstmal wider Willen alles auf 32bit aligned.

von Dr. Sommer (Gast)


Lesenswert?

Hier mal die korrekte Lösung:
1
uint8_t Array[100];
2
uint32_t x = ...;
3
4
Array[3] = (uint8_t) (x & 0xFF);
5
Array[4] = (uint8_t) ((x>>8) & 0xFF);
6
Array[5] = (uint8_t) ((x>>16) & 0xFF);
7
Array[6] = (uint8_t) ((x>>24) & 0xFF);

So werden die 4 Bytes in Little-Endian-Reihenfolge (leicht änderbar) in 
das Array geschrieben. Dieses Verhalten bleibt auch auf anderen 
Plattformen gleich. Die Frage ist aber noch, warum eigentlich eine 
Adresse so in ein Array gepackt werden soll? Das Array wird doch 
bestimmt per UART oder so verschickt, was soll ein anderes Bauteil mit 
einer internen Adresse anfangen?

von Bernd K. (prof7bit)


Lesenswert?

Dr. Sommer schrieb:
> Und genau das geht mit memcpy nicht, weil es die Bytes nicht tauscht und
> kein Padding beachtet.

Was hast Du immerzu mit Deinem Padding? Er kennt doch den Quelloffset 
genau und die Zieladresse ist eine normale Variable und die ist per 
Definition immer richtig aligned.

von Dr. Sommer (Gast)


Lesenswert?

Bernd K. schrieb:
> Er kennt doch den Quelloffset
> genau und die Zieladresse ist eine normale Variable und die ist per
> Definition immer richtig aligned.

Ja, bei einem einzelnen Integer ist das kein Problem. Wenn man aber, wie 
hier nahe gelegt, ein ganzes struct per memcpy in ein Array kopiert um 
dieses zu verschicken, hat man ein Plattform-abhängiges Padding drin:

A. S. schrieb:
> Der TO wollte ab Adresse a n Bytes. Die Aufgabe ergibt sich auch, wenn
> man structs kopieren will.

von Bernd K. (prof7bit)


Lesenswert?

Dr. Sommer schrieb:
> Wenn man aber, wie
> hier nahe gelegt, ein ganzes struct per memcpy in ein Array kopiert um
> dieses zu verschicken, hat man ein Plattform-abhängiges Padding drin

Dann entwirft man das Struct vorher so (und das hab ich unübersehbar 
dazugeschrieben) daß auch unter strengst möglichen Alignmentregeln kein 
Padding entstehen wird! Das ist ganz einfach, man muß mur seinen Geist 
öffnen und die Logik einströmen lassen.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Bernd K. schrieb:
> Dann entwirft man das Struct vorher so (und das hab ich unübersehbar
> dazugeschrieben) daß auch unter strengst möglichen Alignmentregeln kein
> Padding entstehen wird!

Und du bist absolut sicher dass das immer unter allen Plattformen 
garantiert wirkt? Auch wenn man Datentypen verwendet, welche z.B. 3 
Bytes groß sind?
Welchen Vorteil bietet das gegenüber manuellem Kopieren der Bytes?

von Bernd K. (prof7bit)


Lesenswert?

Dr. Sommer schrieb:
> Auch wenn man Datentypen verwendet, welche z.B. 3
> Bytes groß sind?

Zeig mir mal einen nativen Datentyp der 3 Bytes groß ist. Wenn das ein 
struct aus 3 Bytes sein soll dann hat das auf strengen Platformen ein 
sizeof() von 4. Dann fügt man halt noch ein explizites Dummy-Byte in 
sein struct ein dann hat es überall 4, selbst auf Plattformen die 
überhaupt keine Regeln erzwingen!

von Dr. Sommer (Gast)


Lesenswert?

Bernd K. schrieb:
> Zeig mir mal einen nativen Datentyp der 3 Bytes groß ist

Es gibt Systeme (PIC?) mit int24_t.

Bernd K. schrieb:
> Dann fügt man halt noch ein explizites Dummy-Byte in
> sein struct ein dann hat es überall 4, selbst auf Plattformen die
> überhaupt keine Regeln erzwingen!

Das heißt man frickelt herum, bis es auf Biegen und Brechen "ganz 
sicher" auf allen Plattformen funktioniert, statt einfach die vom 
C-Standard garantiert korrekte Lösung zu benutzen?

von Bernd K. (prof7bit)


Lesenswert?

Arno schrieb:
> Danke alle, ich habe jetzt erstmal wider Willen alles auf 32bit aligned.

Du musst jeden Member nur auf seine eigene Länge alignen, also uint16_t 
auf 2 Byte und uint8_t auf beliebige. Du musst sie also einfach so 
umsortieren daß sie alle automatisch auf solche Offsets fallen, zum 
Beispiel 2 einzelne Byte, dann ein uint16, dann ein uint32, die fügen 
sich in der Reihenfolge auch bei strengstem Alignment lückenlos. Ist 
eigentlich ganz einfach.

von Bernd K. (prof7bit)


Lesenswert?

Dr. Sommer schrieb:
> Das heißt man frickelt herum, bis es auf Biegen und Brechen

Nein, man befolgt einfach die Alignment-Regeln. Da muss man gar nichts 
frickeln, das kann man fast blind hinschreiben weil die Regeln absolut 
simpel sind.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Bernd K. schrieb:
> Da muss man gar nichts
> frickeln, das kann man fast blind hinschreiben weil die Regeln absolut
> simpel sind.

Kennst du eine Quelle, die diese Regeln allgemeingültig für alle 
Plattformen, auf denen Standard-C läuft, konsistent darlegt? Im 
C-Standard stehen sie nämlich nicht, und das ist das Dokument, nach dem 
man programmieren sollte.

von Bernd K. (prof7bit)


Lesenswert?

Dr. Sommer schrieb:
> Es gibt Systeme (PIC?) mit int24_t.

Wie gesagt, wenn man das portabel machen will fügt man noch ein 
explizites Padding-Byte am Ende ein und dann funktioniert das auch auf 
strengeren Plattformen (also auf allen).

von Bernd K. (prof7bit)


Lesenswert?

Dr. Sommer schrieb:
> Kennst du eine Quelle, die diese Regeln allgemeingültig für alle
> Plattformen, auf denen Standard-C läuft, konsistent darlegt?

Du schaust Dir die ABIs aller in Frage kommenden Prozessoren an und 
nimmst die mit dem strengsten Alignment. Daran orientierst Du Dich. Bei 
allen die in den letzten zig Jahren gebaut wurden bedeutet das:

~~~ Alignment an einem Vielfachen der eigenen Länge. ~~~

Ganz einfach.

von Dr. Sommer (Gast)


Lesenswert?

Bernd K. schrieb:
> Du schaust Dir die ABIs aller in Frage kommenden Prozessoren an und
> nimmst die mit dem strengsten Alignment. Daran orientierst Du Dich.

Ist mir zu aufwendig. Wer weiß wohin der Code irgendwann mal portiert 
werden soll. Ich schreibe einfach die bereits genannte Lösung hin und 
weiß ganz sicher, dass sie immer funktioniert:

Beitrag "Re: STM32: Adresse mit Rest als uint32_tinterpretieren"

Bernd K. schrieb:
> Ganz einfach.

Ich hätte da halt gerne einen etwas besseren Beleg als "prof7bit hat das 
mal geschrieben".

von Bernd K. (prof7bit)


Lesenswert?

Dr. Sommer schrieb:

> Ist mir zu aufwendig.

Na Du bist ja extrem einfach zu überfordern wenn sowas simples wie 
Variablen nach der Größe zu sortieren oder unterschiedliche Bauklötzchen 
in die Slots passender Größe einzufügen schon an Deine Grenzen stößt.

> Ich hätte da halt gerne einen etwas besseren Beleg als "prof7bit hat das
> mal geschrieben".

Glaub wem Du willst und mach was Du willst. Ich für meinen Teil wollte 
eigentlich nur dem Fragesteller einen gangbaren und sicheren Weg 
aufzeigen.

Hier, wenn Du noch mehr dazu lesen willst: 
http://www.catb.org/esr/structure-packing/

: Bearbeitet durch User
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.