Hallo,
vermutlich für euch total trivial, für mich aber schwer. In einem AVR
mit avr-gcc möchte ich aus einem Puffer, der als Array implementiert
ist, mehrere ints herauskopieren.
Die einzelnen Bytes im Array sind dabei in umgekehrter Reihenfolge zum
int, weil sie so vom A/D-Wandler über eine I2C-Schnittstelle
hereinkommen.
Ungefähr so habe ich das implementiert:
1
uint8_tpuffer[100];
2
uint32_tgrossezahl[16];
1
grossezahl[n]=(uint32_t)puffer[25]<<24|
2
(uint32_t)puffer[24]<<16|
3
(uint32_t)puffer[23]<<8|
4
(uint32_t)puffer[22];
Der avr-gcc macht daraus eine riesen Geschichte:
1
632: fb 01 movw r30, r22
2
634: 27 81 ldd r18, Z+7 ; 0x07
3
636: e6 80 ldd r14, Z+6 ; 0x06
4
638: a5 80 ldd r10, Z+5 ; 0x05
5
63a: 64 80 ldd r6, Z+4 ; 0x04
6
63c: 90 e0 ldi r25, 0x00 ; 0
7
63e: 66 ea ldi r22, 0xA6 ; 166
8
640: 72 e0 ldi r23, 0x02 ; 2
9
642: 86 9f mul r24, r22
10
644: f0 01 movw r30, r0
11
646: 87 9f mul r24, r23
12
648: f0 0d add r31, r0
13
64a: 96 9f mul r25, r22
14
64c: f0 0d add r31, r0
15
64e: 11 24 eor r1, r1
16
650: e5 5c subi r30, 0xC5 ; 197
17
652: fd 4f sbci r31, 0xFD ; 253
18
654: 30 e0 ldi r19, 0x00 ; 0
19
656: 40 e0 ldi r20, 0x00 ; 0
20
658: 50 e0 ldi r21, 0x00 ; 0
21
65a: 52 2f mov r21, r18
22
65c: 44 27 eor r20, r20
23
65e: 33 27 eor r19, r19
24
660: 22 27 eor r18, r18
25
662: ff 24 eor r15, r15
26
664: 00 e0 ldi r16, 0x00 ; 0
27
666: 10 e0 ldi r17, 0x00 ; 0
28
668: 87 01 movw r16, r14
29
66a: ff 24 eor r15, r15
30
66c: ee 24 eor r14, r14
31
66e: 2e 29 or r18, r14
32
670: 3f 29 or r19, r15
33
672: 40 2b or r20, r16
34
674: 51 2b or r21, r17
35
676: 77 24 eor r7, r7
36
678: 88 24 eor r8, r8
37
67a: 99 24 eor r9, r9
38
67c: 26 29 or r18, r6
39
67e: 37 29 or r19, r7
40
680: 48 29 or r20, r8
41
682: 59 29 or r21, r9
42
684: bb 24 eor r11, r11
43
686: cc 24 eor r12, r12
44
688: dd 24 eor r13, r13
45
68a: dc 2c mov r13, r12
46
68c: cb 2c mov r12, r11
47
68e: ba 2c mov r11, r10
48
690: aa 24 eor r10, r10
49
692: 2a 29 or r18, r10
50
694: 3b 29 or r19, r11
51
696: 4c 29 or r20, r12
52
698: 5d 29 or r21, r13
53
69a: 20 83 st Z, r18
54
69c: 31 83 std Z+1, r19 ; 0x01
55
69e: 42 83 std Z+2, r20 ; 0x02
56
6a0: 53 83 std Z+3, r21 ; 0x03
Kann man das in C irgendwie geschickter formulieren, so dass schnellerer
Code erzeugt wird? Letztendlich müssen ja nur 4 Bytes kopiert werden,
wobei die Adresse der rechten Hälfte zur Compilezeit feststeht.
Da ich viele Daten kopieren muss und die Samplerate des A/D-Wandlers
hoch ist, muss ich das schneller hinbekommen.
Das einfachste wäre vermutlich, die Bytes vom Wandler gleich in der
richtigen Reihenfolge abzulegen. Dass du sie vom SPI in einer anderen
Reihenfolge bekommst, ist ja kein Argument, du kennst ja die Reihenfolge
in der sie in den Speicher müssen.
Danach einen uint32_t Pointer auf den Anfang jeder Zahl hinlegen und
einfach zugreifen.
Du könntest auch eine union einrichten, bestehend aus einem 4 Byte Array
und einem uint32_t
1
unionconvert
2
{
3
uint8_tBytes[4];
4
uint32tValue;
5
}
Auf die Bytes weist du zu und über Value holst du dir das Ergebnis. Das
ist zwar eigentlich nicht standardkonform (über eine Variable schreiben
und über eine andere holen), aber alle bekannten Compiler implementieren
das richtig. Also so was wie ein De-facto Standard, der aus formalen
Gründen nicht im C-Standard mit aufgenommen werden kann.
So gesehen ist übrigens auch die Casting-Variante nicht ganz 'astrein'.
Bei deiner Lösung erhebt sich die Frage, wozu du eigentlich puffer[23]
und puffer[24] zuerst auf einen uint32_t hochcastest, nur um sie
verschieben zu können. Braucht kein Mensch, da dieses Zwischenergebnis
sowieso nicht überlaufen kann. Allerdings verwundert mich dein
Codeumfang dann doch ein wenig. Hast du den Optimizer eingeschaltet?
Ja, der Optimizer steht auf -Os
Ok, ich habe noch nicht alles gepostet, was evtl. für die Frage relevant
ist. In den eingehenden Daten sind auch Checksummen und Paritätsbits,
die vorher gecheckt werden. Die Daten sind eigentlich nur 24 bit groß
(A/D-Wandler mit 24 bit). Wenn ich also den Puffer umgekehrt laufen
lasse, das heißt von oben nach unten einlese, damit das Array für den
Zugriff "richtig herum" ist, dann müsste ich an anderer Stelle wieder
tierisch frickeln, um die Paritätsbits und Checksummen zu prüfen.
Ok, wenn es keine einfache Lösung für mein Problem gibt, werde ich mir
das mal anschauen. Ich dachte nur intuitiv, dass das Kopieren der Daten
ganz einfach geht.
Kann ich eigentlich nicht einfach über einen Pointer die Daten kopieren?
Also so:
Inhalt von (Adresse von (grossezahl[n])) = puffer[25];
Inhalt von (Adresse von (grossezahl[n]) + 1) = puffer[24];
usw.?
Martin schrieb:> Karl Heinz Buchegger schrieb:>> der aus formalen>> Gründen nicht im C-Standard mit aufgenommen werden kann.>> Was für "formale Gründe"?
Der C-Standard drückt sich um fast alles was in Richtung technische
Realisierbarkeit geht. Es ist nicht (oder nur sehr schwammig)
festgelegt, was eigentlich ein Byte im C-Sinne sein soll. Der Compiler
hat die Freiheit zwischen Struct Elementen Padding Bytes reinzupacken,
so dass diese Variante
1
unionconvert{
2
struct{
3
unsignedcharByte1;
4
unsignedcharByte2;
5
unsignedcharByte3;
6
unsignedcharByte4;
7
}
8
unsignedlongValue;
9
};
schon aus mehreren Gründen so nicht definiert werden kann:
* zwischen den Einzelbytes darf der Compiler Padding Bytes unterbringen
* es gibt keine Forderung, dass ein unsigned long tatsächlich aus 4
Bytes besteht.
* über die Reihenfolge der Bytes, die dann den unsigned long bilden
sollen ist nichts ausgesagt. Bei signed Typen ist es dann noch
schlimmer, weil kein Mensch sagt, dass 2-er Komplement benutzt werden
soll (drum ist ja auch Overflow bei signed Typen nicht definiert)
Aus ähnlichen Gründen, ist auch die Variante mit dem Array nicht
definierbar.
Aus ähnlichen Gründen wird im C-Standard ja auch gesagt, dass das
Umcasten eines Pointers sofort zu undefiniertem Verhalten führt. D.h.
der C-Standard kann nicht mehr garantieren, was dann eigentlich
passieren soll. Und genau darum geht es: Der C-Standard kann dir nur
Dinge zusagen, die dann tatsächlich auf allen Computern genau so
realisiert sind. Alles was darüber hinaus geht ist grundsätzlich
undefiniert.
Karl Heinz Buchegger schrieb:> Martin schrieb:>> Karl Heinz Buchegger schrieb:>>> der aus formalen>>> Gründen nicht im C-Standard mit aufgenommen werden kann.>>>> Was für "formale Gründe"?>> Der C-Standard drückt sich um fast alles was in Richtung technische> Realisierbarkeit geht. Es ist nicht (oder nur sehr schwammig)> festgelegt, was eigentlich ein Byte im C-Sinne sein soll. Der Compiler> hat die Freiheit zwischen Struct Elementen Padding Bytes reinzupacken,
Und wo sind jetzt die formalen Gründe?
Martin schrieb:> Karl Heinz Buchegger schrieb:>> Martin schrieb:>>> Karl Heinz Buchegger schrieb:>>>> der aus formalen>>>> Gründen nicht im C-Standard mit aufgenommen werden kann.>>>>>> Was für "formale Gründe"?>>>> Der C-Standard drückt sich um fast alles was in Richtung technische>> Realisierbarkeit geht. Es ist nicht (oder nur sehr schwammig)>> festgelegt, was eigentlich ein Byte im C-Sinne sein soll. Der Compiler>> hat die Freiheit zwischen Struct Elementen Padding Bytes reinzupacken,>> Und wo sind jetzt die formalen Gründe?
Warum er sich hier auf das Abenteuer "Wir definieren exakt auf Byteebene
was in solchen Fällen passieren soll" einlassen soll, wenn er es an
anderen Stellen nicht tut.
Von mir aus streiche den begriff "formale" und ersetze ihn durch "gute"