Forum: Mikrocontroller und Digitale Elektronik Daten von array in long int kopieren, in umgekehrter Reihenfolge


von Robert (Gast)


Lesenswert?

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_t puffer[100];
2
uint32_t grossezahl[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.

von Karl H. (kbuchegg)


Lesenswert?

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
union convert
2
{
3
  uint8_t Bytes[4];
4
  uint32t Value;
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?

von Robert (Gast)


Lesenswert?

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.?

von Martin (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> der aus formalen
> Gründen nicht im C-Standard mit aufgenommen werden kann.

Was für "formale Gründe"?

von Karl H. (kbuchegg)


Lesenswert?

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
union convert {
2
  struct {
3
    unsigned char Byte1;
4
    unsigned char Byte2;
5
    unsigned char Byte3;
6
    unsigned char Byte4;
7
  }
8
  unsigned long Value;
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.

von mio (Gast)


Lesenswert?

Man kann den gcc anweisen keine Padding-Bytes (oder -Bits) zu verwenden:
1
union DMAIdRegister {
2
    struct _DMAIdRegister {
3
        unsigned dma_id         : 16;
4
        unsigned dma_ver        : 16;
5
    } __attribute__((__packed__)) Bits;
6
    volatile uint32_t reg;
7
};

"...This attribute, attached to an enum, struct, or union type 
definition, specified that the minimum required memory be used to 
represent the type..."

(siehe: http://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Type-Attributes.html)

von Martin (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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"

von Martin (Gast)


Lesenswert?

Ja, so paßt es.

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.