STM32의 엔디안 문제


11

arm gcc (CooCox)를 사용하여 STM32F4 검색을 프로그래밍하고 있으며 엔디안 문제로 씨름하고 있습니다.

SPI를 통해 24 비트 ADC로 샘플링하고 있습니다. 3 바이트가 들어 오기 때문에 MSB는 먼저 유니언에로드하여 사용하기가 더 쉬워졌습니다 (어쨌든 희망했습니다!).

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

MSB로 [0]을 사용하여 spi 읽기를 사용하여 데이터를 analogin0.spibytes [0]-[2]에로드 한 다음 USART를 통해 한 번에 8 비트의 megabaud로 뱉었습니다. 아무 문제 없습니다.

12 비트 DAC에 데이터를 전달하려고 할 때 문제가 시작되었습니다. 이 SPI DAC는 MSB에서 시작하는 4 비트 접두사와 12 비트의 데이터로 구성된 16 비트 워드를 원합니다.

초기의 시도는 ADC가 나에게 2 진수를 상쇄하도록 변환하는 것이 었습니다. 0x8000으로 analogin0.spihalfwords [0]를 xoring하여 결과를 맨 아래 12 비트로 이동 한 다음 접두사를 산술적으로 추가하여 바이너리를 오프셋합니다.

analogin0.spibytes [0] = 0xFF 및 analogin0.spibytes [1] = 0xB5에 대해 analogin0.halfwords [0]이 0xFFB5가 아닌 0xB5FF와 같았다는 것을 알 때까지 놀랍게도 실망 스럽습니다.

이것을 알아 차린 후에 나는 산술 연산과 하프 워드 사용을 중단하고 비트 논리와 바이트를 고수했습니다.

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)

... 그리고 잘 작동했습니다. 첫 번째 코드 줄, 0xFFB5가 아닌 0xB5FF가 아닌 임시로 들여다 볼 때 모든 것이 좋습니다.

그래서 질문이 있다면 ...

  • 피질은 나에게 새로운 것입니다. 두 플랫폼이 거의 엔디안이지만 PIC가 바이트 교환을 int16에서 기억하는 것을 기억할 수 없습니다. 이 올바른지?

  • 이것을 처리하는 더 우아한 방법이 있습니까? ARM7을 빅 엔디안 모드로 전환하면 좋을 것입니다. 나는 Cortex M4에 대한 많은 참조가 bi-endian 인 것을보고 있지만 모든 출처는 실제로 어떻게 나에게 말하는지 부족한 것처럼 보입니다 . 보다 구체적으로, 어떻게 빅 엔디안 모드로 STM32f407 배치합니까 는 GCC에서 할 수 있으면 더 나은. 이것은 단지 AIRCR 레지스터에서 적절한 비트를 설정하는 문제입니까? 컴파일러를 일치 시키도록 설정해야하거나 나중에 일관성이없는 라이브러리를 사용하여 계산해야하는 등의 영향이 있습니까?


2
"3 바이트가 들어 오면 MSB가 먼저" -빅 엔디안 인 반면 CPU는 리틀 엔디안이므로 문제가 시작됩니다. 16/32 비트 바이트 스와핑을 수행하는 컴파일러 매크로 / 표준 라이브러리 함수를 찾고 싶습니다. 일반적으로 특정 CPU 플랫폼에 가장 효율적인 방식으로 구현됩니다. 물론 비트 단위 쉬프팅 / ANDing / ORing도 괜찮습니다.
Laszlo Valko

내가 원하는 순서대로 analogin0.spibytes를 채울 수 있다고 생각하지만 usart를 통해 전달하기 위해 unstuff 명령을 기억해야하기 때문에 약간의 속임수처럼 보입니다. 3 바이트 형식은 약간 비표준 적이라고 생각합니다. 이것이 C ++ 인 경우 클래스를 고려할 수 있습니다.
Scott Seidman

3
CMSIS는__REV()__REV16()바이트를 반전합니다.
Turbo J

3
이 수준에서 I / O를 수행 할 때는 외부 바이트 순서와 내부 바이트 순서 간의 관계를 알고 처리 해야합니다 . 본인이 선호하는 것은 소프트웨어 계층 구조에서 가장 낮은 수준에서 외부 표현을 "네이티브"(내부) 표현으로 변환하거나 다른 표현으로 변환하는 것입니다.
Dave Tweed 님이

ARM Corp.에서 설계 한 코어가 어느 쪽 엔디안으로도 작동 할 수 있지만 STM32F407에서 STM의 ARM 코어 구현은 리틀 엔디안 일뿐입니다. 64 페이지의 참조 설명서 RM0090을 참조하십시오. AIRCR.ENDIANNESS는 읽기 전용 비트입니다.
Laszlo Valko

답변:


6

임베디드 시스템에는 항상 빅 엔디안 / 리틀 엔디안 문제가 있습니다. 내 개인적인 접근 방식은 항상 내부 메모리를 기본 엔디안으로 인코딩하고 데이터가 들어 오거나 나올 때 바로 스왑하는 것입니다.

spi 읽기를 사용하여 MSB로 [0]을 사용하여 analogin0.spibytes [0]-[2]에 데이터를로드합니다.

[0]을 MSB로로드하면 값이 빅 엔디안으로 인코딩됩니다.

analogin0.spibytes [0] = 0xFF 및 analogin0.spibytes [1] = 0xB5에서 analogin0.halfwords [0]은 0xB5FF와 같습니다.

이는 프로세서가 리틀 엔디안임을 나타냅니다.

대신 첫 번째 값을 [2]에로드하고 [0]으로 다시 작업하면 들어오는 숫자를 리틀 엔디안으로 인코딩하여 숫자가 입력 될 때 스왑을 수행합니다. 기본 표현으로 작업하고 나면 산술 연산을 사용하는 원래 접근 방식으로 돌아갈 수 있습니다. 값을 전송할 때 big-endian으로 다시 되 돌리십시오.


5

현상금과 관련하여 "srm32f4 빅 엔디안 모드에 대해 정말로 알고 싶다"는이 칩에는 빅 엔디안 모드가 없습니다. STM32F4는 리틀 엔디안으로 모든 메모리 액세스를 수행합니다.

사용자 매뉴얼 http://www.st.com/web/en/resource/technical/document/programming_manual/DM00046982.pdf 에이 내용이 25 페이지에 나와 있습니다. 그러나 더 많은 내용이 있습니다. 93 페이지에서 엔디안 스와핑 지침이 있음을 알 수 있습니다. 역방향 및 역방향 비트의 경우 REV 및 REVB REV는 32 비트에 대한 endianess를 변경하고 REV16은 16 비트 데이터에 대해 endianess를 변경합니다.


3

다음은 gcc로 컴파일 된 피질 M4에 대한 코드 스 니펫입니다.

/*
 * 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

C에서 호출은 다음과 같습니다.

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

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

이보다 더 빠른 방법을 모른다 :-)


매크로 나 인라인 함수를 사용하여이 코드를 더 빠르게 만들 수 있습니다
pro

24 비트 데이터는 어떻습니까?
pengemizt

1

CooCox STM32F429의 경우 정상입니다.

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);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.