Problema Endian su STM32


11

Sto usando arm gcc (CooCox) per programmare una scoperta STM32F4, e ho lottato con un problema di endian

Sto campionando con un ADC a 24 bit tramite SPI. Dato che arrivano tre byte, MSB per prima cosa mi è venuta l'idea di caricarli in un sindacato per renderli (speravo comunque!) Un po 'più facili da usare.

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

Carico i dati usando spi read in analogin0.spibytes [0] - [2], con [0] come MSB, quindi li sputo via USART a un megabaud, 8 bit alla volta. Nessun problema.

I problemi sono iniziati quando ho provato a passare i dati a un DAC a 12 bit. Questo DAC SPI richiede parole a 16 bit, che consistono in un prefisso a 4 bit a partire dall'MSB, seguito da 12 bit di dati.

I primi tentativi sono stati di convertire il complemento a due che l'ADC mi ha dato per compensare binario, xoring analogin0.spihalfwords [0] con 0x8000, spostando il risultato sui 12 bit inferiori e quindi aggiungendo il prefisso su aritmetica.

Incredibilmente frustrante, fino a quando non noto che per analogin0.spibytes [0] = 0xFF ee analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] era uguale a 0xB5FF e non 0xFFB5 !!!!!

Dopo aver notato questo, ho smesso di usare le operazioni aritmetiche e la mezza parola e mi sono bloccato sulla logica bit a bit e sui byte

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)

... e questo ha funzionato bene. Quando sbircio a temp dopo la prima riga di codice, è 0xFFB5 e non 0xB5FF, quindi tutto va bene

Quindi, per domande ...

  • Cortex è nuovo per me. Non riesco a ricordare che PIC abbia mai scambiato byte in int16, anche se entrambe le piattaforme sono poco endian. È corretto?

  • Esiste un modo più elegante per gestirlo? Sarebbe bello se potessi semplicemente mettere l'ARM7 in modalità big-endian. Sto vedendo molti riferimenti a Cortex M4 come bi-endian, ma tutte le fonti sembrano smettere di dirmi davvero come . Più specificamente, come posso mettere STM32f407 in modalità big-endian , ancora meglio se può essere fatto in gcc. È solo questione di impostare il bit appropriato nel registro AIRCR? Ci sono delle ramificazioni, come la necessità di impostare il compilatore affinché corrisponda, o problemi di matematica in seguito con librerie incoerenti?


2
"Dato che arrivano tre byte, MSB per primo" - questo è big-endian, mentre la tua CPU è little-endian, quindi è qui che inizia il tuo problema. Cercherei macro di compilatore / funzioni di libreria standard per lo scambio di byte a 16/32 bit, di solito sono implementate nel modo più efficiente per la particolare piattaforma CPU. Ovviamente, anche l'uso del cambio bit / saggio / ANDing / ORing va bene.
Laszlo Valko,

Suppongo che potrei riempire analogin0.spibytes in qualsiasi ordine che voglio, ma sembra anche un po 'un imbroglio, poiché dovrei ricordare l'ordine di disimpegnarlo per passarlo via usart. Penso che il formato a 3 byte renda le cose un po 'non standard. Se questo fosse c ++, potrei prendere in considerazione una classe.
Scott Seidman,

3
CMSIS ha __REV()e __REV16()per invertire i byte.
Turbo J,

3
Non è affatto un trucco - quando si esegue l'I / O a questo livello, è necessario conoscere e gestire la relazione tra l'ordine di byte esterno e l'ordine di byte interno. La mia preferenza è quella di convertire rappresentazioni esterne in / da rappresentazioni "native" (interne) al livello più basso nella gerarchia del software che ha senso e lasciare che tutti i software di livello superiore si occupino solo del formato nativo.
Dave Tweed

Anche se il core progettato da ARM Corp. è in grado di funzionare con entrambe le endianness, l'implementazione del core ARM da parte di STM in STM32F407 è solo little-endian. Vedere il Manuale di riferimento RM0090 pagina 64. AIRCR.ENDIANNESS è un bit di sola lettura.
Laszlo Valko,

Risposte:


6

I sistemi integrati avranno sempre il problema big-endian / little-endian. Il mio approccio personale è stato quello di codificare sempre la memoria interna con l'endianità nativa e fare qualsiasi scambio giusto quando i dati entrano o escono.

Carico i dati usando spi read in analogin0.spibytes [0] - [2], con [0] come MSB

Caricando [0] come MSB, stai codificando il valore come big-endian.

analogin0.spibytes [0] = 0xFF ee analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] era uguale a 0xB5FF

Ciò indica che il processore è little-endian.

Se invece carichi il primo valore in [2] e lavori di nuovo su [0], allora hai codificato il numero in arrivo come little-endian, essenzialmente facendo lo swap quando il numero entra. Una volta che lavori con la rappresentazione nativa, puoi tornare al tuo approccio originale di utilizzo delle operazioni aritmetiche. Assicurati di riportarlo a big-endian quando trasmetti il ​​valore.


5

Per quanto riguarda la taglia "Voglio davvero conoscere la modalità big endian srm32f4", non esiste una modalità big endian su questo chip. STM32F4 esegue tutto l'accesso alla memoria in little endian.

Il manuale dell'utente http://www.st.com/web/en/resource/technical/document/programming_manual/DM00046982.pdf menziona questo a pagina 25. Ma c'è di più. A pagina 93 puoi vedere le istruzioni per lo scambio di endian. REV e REVB per bit di inversione e inversione. REV cambierà endianess per 32 bit e REV16 lo farà per dati a 16 bit.


3

Ecco uno snippet di codice per una corteccia M4, compilato con 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

Da C, la chiamata può essere:

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

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

Non so come fare più velocemente di così :-)


puoi utilizzare una macro o una funzione incorporata per rendere questo codice più veloce
pro

che dire dei dati a 24 bit?
pengemizt,

1

Per CooCox STM32F429 va bene:

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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.