Problem Endian na STM32

11

Używam arm gcc (CooCox) do programowania odkrycia STM32F4 i walczyłem z problemem endian

Próbuję z 24-bitowym ADC przez SPI. Ponieważ nadchodzą trzy bajty, MSB najpierw wpadłem na pomysł załadowania ich do unii, aby uczynić je (w każdym razie mam nadzieję!) Nieco łatwiejszymi w użyciu.

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

Ładuję dane przy użyciu odczytów SPI do analogin0.spibytes [0] - [2], z [0] jako MSB, a następnie wypluwam je za pośrednictwem USART z megabudem, 8 bitów na raz. Bez problemów.

Problemy zaczęły się, gdy próbowałem przekazać dane do 12-bitowego przetwornika cyfrowo-analogowego. Ten SPI DAC chce 16-bitowych słów, które składają się z 4-bitowego przedrostka rozpoczynającego się od MSB, po którym następuje 12 bitów danych.

Początkowe próby polegały na przekształceniu dwójki uzupełnień, które ADC dał mi, aby skompensować dane binarne, poprzez xor-analogię0. Słowa-klucze [0] z 0x8000, przesunięcie wyniku do dolnych 12 bitów, a następnie dodanie przedrostka arytmetycznie.

Niesamowicie frustrujące, dopóki nie zauważę, że dla analogin0.spibytes [0] = 0xFF i i analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] było równe 0xB5FF, a nie 0xFFB5 !!!!!

Po zauważeniu tego przestałem używać operacji arytmetycznych i półsłówka, i utknąłem w logice bitowej i bajtach

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)

... i to działało dobrze. Kiedy zerkam w temp po pierwszym wierszu kodu, to 0xFFB5, a nie 0xB5FF, więc wszystko jest dobrze

W przypadku pytań ...

  • Kora jest dla mnie nowa. Nie pamiętam, żeby PIC zamieniał bajty w int16, mimo że obie platformy są małymi endianami. Czy to jest poprawne?

  • Czy istnieje bardziej elegancki sposób, aby sobie z tym poradzić? Byłoby wspaniale, gdybym mógł po prostu ustawić ARM7 w tryb big-endian. Widzę wiele odniesień do tego, że Cortex M4 jest bi-endianem, ale wszystkie źródła wydają się przestać mówić o tym, jak to zrobić . Mówiąc dokładniej, jak ustawić STM32f407 w trybie big-endian , nawet lepiej, jeśli można to zrobić w gcc. Czy to po prostu kwestia ustawienia odpowiedniego bitu w rejestrze AIRCR? Czy są jakieś konsekwencje, takie jak konieczność ustawienia kompilatora w celu dopasowania lub matematyczne błędy później z niespójnymi bibliotekami?

Scott Seidman
źródło
2
„Ponieważ nadchodzą trzy bajty, najpierw MSB” - to big-endian, podczas gdy ty procesor jest little-endian, więc tam zaczyna się twój problem. Poszukiwałbym makr kompilatora / standardowych funkcji biblioteki do wymiany 16/32-bitowych bajtów, zwykle są one implementowane w najbardziej wydajny sposób dla konkretnej platformy CPU. Oczywiście używanie bit-shift / ANDing / ORing jest również w porządku.
Laszlo Valko
Przypuszczam, że mógłbym upychać analogin0.spibytes w dowolnej kolejności, którą chcę, ale to też wydaje się trochę oszustwem, ponieważ musiałbym pamiętać kolejność rozpakowania go, aby przejść przez usart. Myślę, że 3-bajtowy format sprawia, że ​​rzeczy są nieco niestandardowe. Gdyby to było c ++, mógłbym rozważyć klasę.
Scott Seidman
3
CMSIS ma __REV()i __REV16()do cofania bajtów.
Turbo J
3
To wcale nie jest oszustwo - kiedy robisz operacje wejścia / wyjścia na tym poziomie, musisz zdawać sobie sprawę z relacji między zewnętrzną kolejnością bajtów i wewnętrzną kolejnością bajtów i radzić sobie z nimi. Moje własne preferencje to konwersja zewnętrznych reprezentacji na / z „natywnych” (wewnętrznych) reprezentacji na najniższym poziomie hierarchii oprogramowania, co ma sens, i niech wszystkie oprogramowanie wyższego poziomu radzi sobie tylko z rodzimym formatem.
Dave Tweed
Chociaż rdzeń zaprojektowany przez ARM Corp. jest w stanie działać z dowolną endianowością, implementacja rdzenia ARM przez STM w STM32F407 jest tylko little-endian. Patrz Instrukcja obsługi RM0090 strona 64. AIRCR.ENDIANNESS jest bitem tylko do odczytu.
Laszlo Valko

Odpowiedzi:

6

W systemach wbudowanych zawsze występuje problem big-endian / little-endian. Moim osobistym podejściem było zawsze kodowanie pamięci wewnętrznej z natywnym endianizmem i dokonywanie wszelkich wymian w momencie, gdy dane wchodzą lub wychodzą.

Ładuję dane przy użyciu odczytów SPI do analogin0. Spibytes [0] - [2], z [0] jako MSB

Ładując [0] jako MSB, kodujesz wartość jako big-endian.

analogin0.spibytes [0] = 0xFF i i analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] był równy 0xB5FF

Oznacza to, że procesor jest little-endian.

Jeśli zamiast tego załadujesz pierwszą wartość do [2] i wrócisz do [0], to zakodowałeś przychodzącą liczbę jako little-endian, zasadniczo wykonując zamianę przy wprowadzaniu liczby. Po pracy z natywną reprezentacją możesz powrócić do pierwotnego podejścia do korzystania z operacji arytmetycznych. Po prostu przekaż wartość z powrotem do big-endian.

DrRobotNinja
źródło
5

Jeśli chodzi o nagrodę „Naprawdę chcesz wiedzieć o trybie srm32f4 big endian”, nie ma w tym układzie dużego trybu endian. STM32F4 ma dostęp do całej pamięci w małym endianie.

Instrukcja obsługi http://www.st.com/web/en/resource/technical/document/programming_manual/DM00046982.pdf wspomina o tym na stronie 25. Ale jest więcej. Na stronie 93 można zobaczyć instrukcje wymiany Endian. REV i REVB dla bitów do tyłu i do tyłu. REV zmieni endianessę dla 32 bitów, a REV16 zrobi to dla danych 16-bitowych.

C. Towne Springer
źródło
3

Oto fragment kodu dla kory M4, skompilowany z 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

Od C połączenie może być:

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

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

Nie wiem, jak to zrobić szybciej :-)

Olivier Monnom
źródło
możesz użyć makra lub funkcji wbudowanej, aby przyspieszyć ten kod
pro
co z danymi 24-bitowymi?
pengemizt
1

W przypadku CooCox STM32F429 jest to prawidłowe:

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
źródło