Problema Endian no STM32


11

Estou usando arm gcc (CooCox) para programar uma descoberta STM32F4, e estou lutando com um problema endian

Estou amostrando com um ADC de 24 bits via SPI. Como três bytes estão chegando, primeiro o MSB teve a idéia de carregá-los em uma união para torná-los (eu esperava!) Um pouco mais fácil de usar.

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

Carrego os dados usando leituras spi em analogin0.spibytes [0] - [2], com [0] como MSB, depois os cuspo via USART em um megabaud, 8 bits por vez. Sem problemas.

Os problemas começaram quando tentei passar os dados para um DAC de 12 bits. Esse DAC SPI deseja palavras de 16 bits, que consistem em um prefixo de 4 bits começando no MSB, seguido por 12 bits de dados.

As tentativas iniciais foram converter o complemento duplo que o ADC me deu para compensar o binário, xor-analogog0.spihalfwords [0] com 0x8000, deslocando o resultado para os 12 bits inferiores e adicionando o prefixo aritmeticamente.

Incrivelmente frustrante, até eu notar que para analogin0.spibytes [0] = 0xFF e analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] era igual a 0xB5FF e não 0xFFB5 !!!!!

Depois de perceber isso, parei de usar operações aritméticas e a meia palavra e permaneci na lógica bit a bit e nos 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)

... e isso funcionou bem. Quando eu espio temp após a primeira linha do código, é 0xFFB5, e não 0xB5FF, então tudo está bem

Então, para perguntas ...

  • O córtex é novo para mim. Não me lembro da PIC de troca de bytes em int16, embora ambas as plataformas sejam pouco endian. Isso está correto?

  • Existe uma maneira mais elegante de lidar com isso? Seria ótimo se eu pudesse colocar o ARM7 no modo big endian. Estou vendo muitas referências ao Cortex M4 sendo bi-endiano, mas todas as fontes parecem não me dizer como . Mais especificamente, como coloco o STM32f407 no modo big-endian , ainda melhor se isso puder ser feito no gcc. É APENAS uma questão de definir o bit apropriado no registro do AIRCR? Existem ramificações, como a necessidade de definir o compilador para corresponder ou erros matemáticos mais tarde com bibliotecas inconsistentes?


2
"Como três bytes estão chegando, o MSB primeiro" - isso é big-endian, enquanto sua CPU é little-endian, então é aí que o seu problema começa. Eu procuraria por macros do compilador / funções de biblioteca padrão para fazer a troca de bytes de 16/32 bits, geralmente elas são implementadas da maneira mais eficiente para a plataforma de CPU específica. Obviamente, usar shift / ANDing / ORing em bits também é bom.
Laszlo Valko

Suponho que eu poderia colocar analogin0.spibytes em qualquer ordem que eu quisesse, mas isso também parece meio trapaceiro, pois eu teria que lembrar da ordem de descompactá-lo para transmiti-lo via usart. Eu acho que o formato de 3 bytes torna as coisas um pouco fora do padrão. Se fosse c ++, eu poderia considerar uma classe.
21813 Scott Seidman

3
O CMSIS possui __REV()e __REV16()para reverter bytes.
Turbo J

3
Não é uma trapaça - quando você estiver executando E / S nesse nível, você deve estar ciente e lidar com o relacionamento entre a ordem de bytes externos e a ordem de bytes interna. Minha preferência é converter representações externas para / de representações "nativas" (internas) no nível mais baixo da hierarquia de software que faça sentido, e permitir que todos os softwares de nível superior lidem apenas com o formato nativo.
Dave Tweed

Embora o núcleo projetado pela ARM Corp. possa operar com qualquer endianness, a implementação do STM do núcleo do ARM no STM32F407 é apenas little-endian. Consulte o Manual de Referência RM0090 página 64. AIRCR.ENDIANNESS é um bit somente leitura.
Laszlo Valko

Respostas:


6

Os sistemas embarcados sempre terão o problema de big endian / little-endian. Minha abordagem pessoal foi sempre codificar a memória interna com a capacidade de endereçamento nativa e fazer qualquer troca correta quando os dados entram ou saem.

Carrego os dados usando leituras spi em analogin0.spibytes [0] - [2], com [0] como MSB

Ao carregar [0] como MSB, você codifica o valor como big endian.

analogin0.spibytes [0] = 0xFF e analogicin0.spibytes [1] = 0xB5, analogin0.halfwords [0] foi igual a 0xB5FF

Isso indica que o processador é little-endian.

Se, em vez disso, você carregar o primeiro valor em [2] e retornar a [0], codificará o número recebido como little-endian, essencialmente fazendo a troca à medida que o número entra. Depois de trabalhar com a representação nativa, você pode retornar à sua abordagem original de usar operações aritméticas. Apenas certifique-se de devolvê-lo ao big-endian ao transmitir o valor.


5

Em relação à recompensa "Realmente quero saber sobre o modo big endian srm32f4", não há modo big endian neste chip. STM32F4 faz todo o acesso à memória em little endian.

O manual do usuário http://www.st.com/web/en/resource/technical/document/programming_manual/DM00046982.pdf menciona isso na página 25. Mas há mais. Na página 93, você pode ver que existem instruções de troca endian. REV e REVB para bit reverso e reverso. O REV mudará a endianess para 32 bits e o REV16 fará isso para os dados de 16 bits.


3

Aqui está um trecho de código para um córtex M4, compilado com o 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

Em C, a chamada pode ser:

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

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

Não sei como fazer mais rápido do que isso :-)


você pode usar uma macro ou uma função inline tornar o referido código mais rápido
pro

que tal dados de 24 bits?
pengemizt

1

Para o CooCox STM32F429, tudo bem:

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);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.