problema de Endian en STM32

10

Estoy usando arm gcc (CooCox) para programar un descubrimiento STM32F4, y he estado luchando con un problema endiano

Estoy muestreando con un ADC de 24 bits a través de SPI. Ya que ingresan tres bytes, MSB primero tuve la idea de cargarlos en una unión para hacerlos (¡espero que de todos modos!) Un poco más fácil de usar.

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

Carga los datos usando lecturas spi en analogin0.spibytes [0] - [2], con [0] como MSB, luego los escupo a través de USART a un megabaud, 8 bits a la vez. No hay problemas.

Los problemas comenzaron cuando intenté pasar los datos a un DAC de 12 bits. Este SPI DAC quiere palabras de 16 bits, que consisten en un prefijo de 4 bits que comienza en el MSB, seguido de 12 bits de datos.

Los intentos iniciales fueron convertir los dos complementos que el ADC me proporcionó para compensar el binario, mediante la analogía analógica0.spihalfwords [0] con 0x8000, cambiando el resultado a los 12 bits inferiores y luego agregando el prefijo aritméticamente. / p>

Increíblemente frustrante, hasta que me di cuenta de que para analogin0.spibytes [0] = 0xFF y y analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] era igual a 0xB5FF y no 0xFFB5 !!!!!

Después de darme cuenta de esto, dejé de usar las operaciones aritméticas y la media palabra, y me quedé con la lógica bitwise y los 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)

... y esto funcionó bien. Cuando miro a temp después de la primera línea de código, su 0xFFB5, y no 0xB5FF, todo está bien

Entonces, para preguntas ...

  • Cortex es nuevo para mí. No puedo recordar el intercambio de bytes de PIC en int16, a pesar de que ambas plataformas son poco endian. ¿Es correcto?

  • ¿Hay una manera más elegante de manejar esto? Sería genial si pudiera poner el ARM7 en modo big-endian. Estoy viendo muchas referencias a que Cortex M4 es bi-endian, pero todas las fuentes parecen no poder realmente decirme cómo . Más específicamente, cómo coloco el STM32f407 en modo big-endian , incluso mejor si se puede hacer en gcc. ¿Es solo una cuestión de establecer el bit apropiado en el registro AIRCR? ¿Hay alguna ramificación, como tener que configurar el compilador para que coincida, o errores de matemáticas más adelante con bibliotecas inconsistentes?

pregunta Scott Seidman

4 respuestas

6

Los sistemas integrados siempre tendrán el problema big-endian / little-endian. Mi enfoque personal ha sido codificar siempre la memoria interna con la endianidad nativa y hacer los cambios correctos cuando los datos entran o salen.

  

Carga los datos utilizando lecturas spi en analogin0.spibytes [0] - [2], con [0] como MSB

Al cargar [0] como MSB, estás codificando el valor como big-endian.

  

analogin0.spibytes [0] = 0xFF y analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] era igual a 0xB5FF

Esto indica que el procesador es little-endian.

Si, en cambio, carga el primer valor en [2] y vuelve a trabajar en [0], entonces ha codificado el número entrante como little-endian, esencialmente haciendo el intercambio cuando el número ingresa. Una vez que esté trabajando con la representación nativa, puede volver a su enfoque original de usar operaciones aritméticas. Solo asegúrese de darle la vuelta a big-endian cuando transmita el valor.

    
respondido por el DrRobotNinja
4

En cuanto a la recompensa "Realmente quiero saber sobre el modo big endian srm32f4", no hay modo big endian en este chip. STM32F4 hace todo el acceso a la memoria en little endian.

El manual del usuario enlace menciona esto en la página 25. Pero hay más. En la página 93 puede ver que hay instrucciones de intercambio de endian. REV y REVB para bit inverso y reverso. REV cambiará la endianess para 32 bits y REV16 lo hará para datos de 16 bits.

    
respondido por el C. Towne Springer
2

Aquí hay un fragmento de código para una corteza M4, compilada 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

Desde C, la llamada puede ser:

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

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

No sé cómo hacerlo más rápido que esto :-)

    
respondido por el Olivier Monnom
1

Para CooCox STM32F429 esto está bien:

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);
    
respondido por el Argebir Laboratuvar

Lea otras preguntas en las etiquetas