Vấn đề về endian trên STM32


11

Tôi đang sử dụng arm gcc (CooCox) để lập trình khám phá STM32F4 và tôi đã vật lộn với một vấn đề về endian

Tôi đang lấy mẫu với ADC 24 bit thông qua SPI. Vì ba byte đang đến, MSB trước tiên tôi có ý tưởng tải chúng vào một liên minh để làm cho chúng (tôi hy vọng, dù sao đi nữa!) Dễ sử dụng hơn một chút.

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

Tôi tải dữ liệu bằng cách sử dụng spi đọc vào analogin0.spibytes [0] - [2], với [0] là MSB, sau đó tôi nhổ chúng qua USART ở tốc độ megabaud, 8 bit mỗi lần. Không vấn đề gì.

Các vấn đề bắt đầu khi tôi cố gắng truyền dữ liệu vào một bộ xử lý 12 bit. SPI DAC này muốn các từ 16 bit, bao gồm tiền tố 4 bit bắt đầu từ MSB, theo sau là 12 bit dữ liệu.

Những nỗ lực ban đầu là để chuyển đổi twos bổ sung mà ADC đã cho tôi để bù nhị phân, bằng xor-ing analogin0.spihalfwords [0] với 0x8000, chuyển kết quả xuống 12 bit dưới cùng, sau đó thêm tiền tố vào một cách hợp lý.

Vô cùng bực bội, cho đến khi tôi nhận thấy rằng đối với analogin0.spibytes [0] = 0xFF và analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] bằng 0xB5FF chứ không phải 0xFFB5 !!!!!

Sau khi nhận thấy điều này, tôi đã ngừng sử dụng các phép toán số học và nửa câu, và bị mắc kẹt với logic bitwise và các 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)

... Và điều này đã làm việc tốt. Khi tôi nhìn trộm tạm thời sau dòng mã đầu tiên, 0xFFB5 của nó chứ không phải 0xB5FF, vì vậy tất cả đều tốt

Vì vậy, đối với câu hỏi ...

  • Cortex là mới đối với tôi. Tôi không thể nhớ PIC từng hoán đổi byte trong int16, mặc dù cả hai nền tảng đều là endian nhỏ. Điều này có đúng không?

  • Có cách nào thanh lịch hơn để xử lý việc này? Sẽ thật tuyệt nếu tôi có thể đưa ARM7 vào chế độ lớn. Tôi đang thấy nhiều tài liệu tham khảo về Cortex M4 là bi-endian, nhưng tất cả các nguồn dường như không còn thực sự cho tôi biết làm thế nào . Cụ thể hơn, làm cách nào để đặt STM32f407 vào chế độ lớn , thậm chí còn tốt hơn nếu nó có thể được thực hiện trong gcc. Đây có phải là vấn đề thiết lập bit thích hợp trong thanh ghi AIRCR không? Có bất kỳ sự phân nhánh nào, chẳng hạn như phải đặt trình biên dịch khớp, hoặc các lỗi toán học sau này với các thư viện không nhất quán ??


2
"Vì ba byte đang đến, MSB đầu tiên" - đó là endian lớn, trong khi CPU của bạn là endian nhỏ, vì vậy đó là vấn đề của bạn bắt đầu. Tôi sẽ tìm các macro trình biên dịch / các hàm thư viện chuẩn để thực hiện hoán đổi byte 16/32-bit, thông thường chúng được triển khai theo cách hiệu quả nhất cho nền tảng CPU cụ thể. Tất nhiên, sử dụng dịch chuyển / ANDing / ORing bit-khôn ngoan cũng không sao.
Laszlo Valko

Tôi cho rằng tôi có thể nhét analogin0.spibytes theo bất kỳ thứ tự nào tôi muốn, nhưng điều đó cũng có vẻ hơi gian lận, vì tôi phải nhớ thứ tự hủy bỏ nó để chuyển nó qua usart. Tôi nghĩ định dạng 3 byte làm cho mọi thứ hơi không chuẩn. Nếu đây là c ++, tôi có thể xem xét một lớp.
Scott Seidman

3
CMSIS__REV()__REV16()để đảo ngược byte.
Turbo J

3
Hoàn toàn không phải là một trò gian lận - khi bạn đang thực hiện I / O ở cấp độ này, bạn phải nhận thức và xử lý mối quan hệ giữa thứ tự byte ngoài và thứ tự byte bên trong. Sở thích của tôi là chuyển đổi các biểu diễn bên ngoài thành / từ các biểu diễn "gốc" (bên trong) ở mức thấp nhất trong hệ thống phân cấp phần mềm có ý nghĩa và để tất cả phần mềm cấp cao hơn xử lý chỉ với định dạng gốc.
Dave Tweed

Mặc dù lõi được thiết kế bởi ARM Corp có thể hoạt động với cả độ bền, nhưng việc triển khai lõi ARM của STM trong STM32F407 chỉ mang tính kết thúc. Xem Tài liệu tham khảo RM0090 trang 64. AIRCR.ENDIANNESS là một bit chỉ đọc.
Laszlo Valko

Câu trả lời:


6

Các hệ thống nhúng sẽ luôn có vấn đề lớn về cuối / cuối nhỏ. Cách tiếp cận cá nhân của tôi là luôn luôn mã hóa bộ nhớ trong với độ bền bản địa và thực hiện bất kỳ giao dịch hoán đổi nào ngay khi dữ liệu đi vào hoặc rời đi.

Tôi tải dữ liệu bằng cách đọc spi vào analogin0.spibytes [0] - [2], với [0] là MSB

Bằng cách tải [0] dưới dạng MSB, bạn đang mã hóa giá trị dưới dạng endian lớn.

analogin0.spibytes [0] = 0xFF và analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] bằng 0xB5FF

Điều này chỉ ra rằng bộ xử lý là ít endian.

Nếu thay vào đó, bạn tải giá trị đầu tiên vào [2] và hoạt động trở lại [0], thì bạn đã mã hóa số đến dưới dạng endian nhỏ, về cơ bản thực hiện trao đổi khi số được nhập. Khi bạn đang làm việc với biểu diễn gốc, bạn có thể quay lại phương pháp ban đầu của mình bằng cách sử dụng các phép toán số học. Chỉ cần đảm bảo lật nó trở lại big endian khi bạn truyền giá trị.


5

Về tiền thưởng "Thực sự muốn biết về chế độ endian lớn srm32f4", không có chế độ endian lớn trên chip này. STM32F4 thực hiện tất cả các truy cập bộ nhớ trong ít endian.

Hướng dẫn sử dụng http://www.st.com/web/en/resource/technical/document/programming_manual/DM00046982.pdf đề cập đến điều này trên trang 25. Nhưng còn nhiều hơn thế. Trên trang 93 bạn có thể thấy có các hướng dẫn hoán đổi endian. REV và REVB cho bit đảo ngược và đảo ngược. REV sẽ thay đổi endianess cho 32 bit và REV16 sẽ làm điều đó cho dữ liệu 16 bit.


3

Đây là đoạn mã cho một vỏ não M4, được biên dịch bằng 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

Từ C, cuộc gọi có thể là:

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

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

Không biết cách làm nhanh hơn thế này :-)


bạn có thể sử dụng một macro hoặc một hàm nội tuyến để làm cho mã này nhanh hơn
pro

Dữ liệu 24 bit thì sao?
pengemizt

1

Đối với CooCox STM32F429, điều này là ổn:

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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.