Endian-Problem auf STM32

11

Ich benutze arm gcc (CooCox), um eine STM32F4-Entdeckung zu programmieren, und ich habe mit einem Endian-Problem gerungen

Ich probiere mit einem 24-Bit-ADC über SPI. Da drei Bytes eingehen, hatte MSB zuerst die Idee, sie in eine Union zu laden, um sie (ich hoffte jedenfalls!) Ein wenig benutzerfreundlicher zu machen.

typedef union
{
int32_t spilong;
uint8_t spibytes [4];
uint16_t spihalfwords [2];} spidata;
spidata analogin0;

Ich lade die Daten mit spi-Lesevorgängen in analogin0.spibytes [0] - [2] mit [0] als MSB und spucke sie dann über USART mit einer Megabaud aus, jeweils 8 Bit. Keine Probleme.

Die Probleme begannen, als ich versuchte, die Daten an einen 12-Bit-DAC weiterzuleiten. Dieser SPI-DAC möchte 16-Bit-Wörter, die aus einem 4-Bit-Präfix bestehen, das beim MSB beginnt, gefolgt von 12 Datenbits.

Anfängliche Versuche waren, das Zweierkomplement zu konvertieren, das der ADC mir gab, um Binär zu versetzen, indem analogin0.spihalfwords [0] mit 0x8000 xoriert, das Ergebnis auf die unteren 12 Bits verschoben und dann das Präfix arithmetisch hinzugefügt wurde.

Unglaublich frustrierend, bis ich bemerke, dass für analogin0.spibytes [0] = 0xFF und und analogin0.spibytes [1] = 0xB5 analogin0.halfwords [0] gleich 0xB5FF und nicht 0xFFB5 war !!!!!

Nachdem ich dies bemerkt hatte, hörte ich auf, arithmetische Operationen und das Halbwort zu verwenden, und hielt mich an die bitweise Logik und die Bytes

uint16_t temp=0;
.
.
.


// work on top 16 bits
temp= (uint16_t)(analogin0.spibytes[0])<<8|(uint16_t)(analogin0.spibytes[1]);
temp=temp^0x8000; // convert twos complement to offset binary
temp=(temp>>4) | 0x3000; // shift and prepend with bits to send top 12 bits to DAC A


SPI_I2S_SendData(SPI3,temp); //send to DACa (16 bit SPI words)

... und das hat gut funktioniert. Wenn ich nach der ersten Codezeile auf temp schaue, ist es 0xFFB5 und nicht 0xB5FF, also ist alles gut

Also, für Fragen ...

  • Cortex ist neu für mich. Ich kann mich nicht erinnern, dass PIC jemals in Int16-Bytes ausgetauscht wurde, obwohl beide Plattformen Little Endian sind. Ist das richtig?

  • Gibt es eine elegantere Möglichkeit, damit umzugehen? Es wäre großartig, wenn ich den ARM7 einfach in den Big-Endian-Modus versetzen könnte. Ich sehe viele Hinweise darauf, dass Cortex M4 bi-endian ist, aber alle Quellen scheinen nicht mehr zu sagen, wie . Genauer gesagt, wie versetze ich den STM32f407 in den Big-Endian-Modus , noch besser, wenn dies in gcc möglich ist. Geht es nur darum, das entsprechende Bit im AIRCR-Register zu setzen? Gibt es Konsequenzen, z. B. dass der Compiler so eingestellt werden muss, dass er übereinstimmt, oder dass es später zu mathematischen Problemen mit inkonsistenten Bibliotheken kommt?

Scott Seidman
quelle
2
"Da drei Bytes eingehen, MSB zuerst" - das ist Big-Endian, während Ihre CPU Little-Endian ist. Hier beginnt also Ihr Problem. Ich würde nach Compilermakros / Standardbibliotheksfunktionen suchen, um 16/32-Bit-Byte-Swapping durchzuführen. Normalerweise werden sie für die jeweilige CPU-Plattform auf die effizienteste Weise implementiert. Natürlich ist es auch in Ordnung, bitweises Verschieben / ANDing / ORing zu verwenden.
Laszlo Valko
Ich nehme an, ich könnte analogin0.spibytes in beliebiger Reihenfolge stopfen, aber das scheint auch ein bisschen betrügerisch zu sein, da ich mich an die Reihenfolge erinnern müsste, um es zu entfernen, um es über usart zu übergeben. Ich denke, das 3-Byte-Format macht die Dinge etwas unüblich. Wenn dies c ++ wäre, könnte ich eine Klasse in Betracht ziehen.
Scott Seidman
3
CMSIS hat __REV()und __REV16()zum Umkehren von Bytes.
Turbo J
3
Es ist überhaupt kein Cheat - wenn Sie E / A auf dieser Ebene ausführen , müssen Sie die Beziehung zwischen der externen Bytereihenfolge und der internen Bytereihenfolge kennen und damit umgehen. Ich bevorzuge es, externe Darstellungen in / von "nativen" (internen) Darstellungen auf der untersten Ebene der Softwarehierarchie zu konvertieren, die sinnvoll ist, und alle übergeordneten Softwareprogramme nur das native Format behandeln zu lassen.
Dave Tweed
Obwohl der von ARM Corp. entwickelte Kern mit beiden Endianness arbeiten kann, ist die Implementierung des ARM-Kerns in STM32F407 durch STM nur Little-Endian. Siehe Referenzhandbuch RM0090 Seite 64. AIRCR.ENDIANNESS ist ein schreibgeschütztes Bit.
Laszlo Valko

Antworten:

6

Eingebettete Systeme haben immer das Big-Endian / Little-Endian-Problem. Mein persönlicher Ansatz war es, den internen Speicher immer mit der nativen Endianität zu codieren und alle Swaps richtig durchzuführen, wenn Daten ein- oder ausgehen.

Ich lade die Daten mit SPI-Reads in analogin0.spibytes [0] - [2], wobei [0] das MSB ist

Durch Laden von [0] als MSB codieren Sie den Wert als Big-Endian.

analogin0.spibytes [0] = 0xFF und und analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] war gleich 0xB5FF

Dies zeigt an, dass der Prozessor Little-Endian ist.

Wenn Sie stattdessen den ersten Wert in [2] laden und auf [0] zurückarbeiten, haben Sie die eingehende Nummer als Little-Endian codiert und den Swap im Wesentlichen beim Eingeben der Nummer durchgeführt. Sobald Sie mit der nativen Darstellung arbeiten, können Sie zu Ihrem ursprünglichen Ansatz zurückkehren, arithmetische Operationen zu verwenden. Stellen Sie einfach sicher, dass Sie es zurück auf Big-Endian drehen, wenn Sie den Wert übertragen.

DrRobotNinja
quelle
5

In Bezug auf Bounty "Ich möchte wirklich etwas über den Big Endian-Modus von srm32f4 wissen" gibt es auf diesem Chip keinen Big Endian-Modus. STM32F4 führt den gesamten Speicherzugriff in Little Endian durch.

Das Benutzerhandbuch http://www.st.com/web/en/resource/technical/document/programming_manual/DM00046982.pdf erwähnt dies auf Seite 25. Aber es gibt noch mehr. Auf Seite 93 sehen Sie, dass es Anweisungen zum Austauschen von Endian gibt. REV und REVB für Rückwärts- und Rückwärtsbit. REV ändert die Endianess für 32 Bit und REV16 für 16 Bit.

C. Towne Springer
quelle
3

Hier ist ein Code-Snippet für einen Cortex M4, kompiliert mit gcc

/*
 * asmLib.s
 *
 *  Created on: 13 mai 2016
 */
    .syntax unified
    .cpu cortex-m4
    .thumb
    .align
    .global big2little32
    .global big2little16
    .thumb
    .thumb_func
 big2little32:
    rev r0, r0
    bx  lr
 big2little16:
    rev16   r0, r0
    bx  lr

Ab C kann der Anruf sein:

 extern uint32_t big2little32(uint32_t x);
 extern uint16_t big2little16(uint16_t x);

 myVar32bit = big2little32( myVar32bit );
 myVar16bit = big2little16( myVar16bit );

Ich weiß nicht, wie ich es schneller machen soll :-)

Olivier Monnom
quelle
Sie können ein Makro oder eine Inline-Funktion verwenden, um diesen Code schneller zu machen
Pro
Was ist mit 24-Bit-Daten?
Pengemizt
1

Für CooCox STM32F429 ist dies in Ordnung:

typedef union {
  uint8_t  c[4];
  uint16_t   i[2];
  uint32_t  l[1];
}adc;

adc     adcx[8];

...

// first channel ...
    adcx[0].c[3] = 0;
    adcx[0].c[2] = UB_SPI1_SendByte(0x00);
    adcx[0].c[1] = UB_SPI1_SendByte(0x00);
    adcx[0].c[0] = UB_SPI1_SendByte(0x00);
Argebir Laboratuvar
quelle