ปัญหา Endian บน STM32


11

ฉันกำลังใช้ arm gcc (CooCox) เพื่อเขียนโปรแกรม STM32F4 การค้นพบและฉันได้รับการต่อสู้กับปัญหา endian

ฉันสุ่มตัวอย่างด้วย ADC 24 บิตผ่าน SPI เนื่องจากสามไบต์กำลังเข้ามา MSB ครั้งแรกที่ฉันมีความคิดในการโหลดมันลงในกลุ่มเพื่อให้พวกเขา (ฉันหวังว่าต่อไป!) ใช้งานง่ายขึ้นเล็กน้อย

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

ฉันโหลดข้อมูลโดยใช้ spi อ่านเป็น analogin0.spibytes [0] - [2], ด้วย [0] ในฐานะ MSB จากนั้นฉันพ่นมันออกทาง USART ที่ megabaud ครั้งละ 8 บิต ไม่มีปัญหา.

ปัญหาเริ่มต้นขึ้นเมื่อฉันพยายามส่งข้อมูลไปยัง DAC 12 บิต SPI DAC นี้ต้องการคำ 16 บิตซึ่งประกอบด้วยคำนำหน้า 4 บิตเริ่มต้นที่ MSB ตามด้วยข้อมูล 12 บิต

ความพยายามเริ่มแรกคือการแปลง twos ที่ ADC มอบให้ฉันเพื่อชดเชยไบนารีโดย xor-ing analogin0.spihalfwords [0] ด้วย 0x8000 เปลี่ยนผลลัพธ์เป็น 12 บิตด้านล่างจากนั้นเพิ่มคำนำหน้าบนเลขคณิต

น่าผิดหวังอย่างไม่น่าเชื่อจนกระทั่งฉันสังเกตเห็นว่าสำหรับ analogin0.spibytes [0] = 0xFF และและ analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] เท่ากับ 0xB5FF และไม่ใช่ 0xFFB5 !!!!!

หลังจากสังเกตเห็นสิ่งนี้ฉันหยุดใช้การดำเนินการทางคณิตศาสตร์และครึ่งคำและติดอยู่กับตรรกะระดับบิตและไบต์

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)

... และมันก็ใช้ได้ดี เมื่อฉันดูที่ temp หลังจากบรรทัดแรกของรหัส 0xFFB5 ของมันและไม่ใช่ 0xB5FF ดังนั้นทั้งหมดเป็นสิ่งที่ดี

ดังนั้นสำหรับคำถาม ...

  • Cortex นั้นใหม่สำหรับฉัน ฉันจำไม่ได้ว่า PIC ใช้ไบต์สลับกันใน int16 ได้แม้ว่าทั้งสองแพลตฟอร์มจะเป็น endian น้อยก็ตาม ถูกต้องหรือไม่

  • มีวิธีที่สง่างามกว่านี้ในการจัดการกับเรื่องนี้หรือไม่? มันจะดีมากถ้าฉันสามารถใส่ ARM7 เข้าสู่โหมด big-endian ฉันเห็นการอ้างอิงจำนวนมากเพื่อ Cortex M4 เป็นสอง endian แต่แหล่งที่มาทั้งหมดดูเหมือนจะหยุดสั้นของจริงบอกฉันว่า โดยเฉพาะอย่างยิ่งฉันจะวาง STM32f407 ในโหมด big-endianได้ดียิ่งขึ้นถ้าสามารถทำได้ใน gcc นี่เป็นเพียงการตั้งค่าบิตที่เหมาะสมในการลงทะเบียน AIRCR หรือไม่? มี ramifications เช่นต้องตั้งคอมไพเลอร์ให้ตรงกันหรือ math screwups ภายหลังด้วยไลบรารีที่ไม่สอดคล้องกันหรือไม่?


2
"ตั้งแต่สามไบต์กำลังจะมา MSB ก่อน" - นั่นคือ big-endian ในขณะที่ CPU ของคุณเป็น endian เล็ก ๆ น้อย ๆ นั่นคือสิ่งที่ปัญหาของคุณเริ่มต้น ฉันจะมองหาคอมไพเลอร์มาโคร / ฟังก์ชั่นไลบรารีมาตรฐานเพื่อทำการสลับไบต์ 16/32 บิตโดยปกติจะมีการใช้งานอย่างมีประสิทธิภาพที่สุดสำหรับแพลตฟอร์ม CPU เฉพาะ แน่นอนว่าการใช้การเปลี่ยนบิตแบบฉลาด / ANDing / ORing ก็ถือว่าใช้ได้เช่นกัน
Laszlo Valko

ฉันคิดว่าฉันสามารถที่ analogin0.spibytes ในลำดับใดก็ได้ที่ฉันต้องการ แต่นั่นก็ดูเหมือนโกงนิดหน่อยเช่นกันเพราะฉันต้องจำคำสั่งให้ถอดออกเพื่อส่งผ่านทาง usart ฉันคิดว่ารูปแบบ 3 ไบต์ทำให้สิ่งต่าง ๆ ไม่ได้มาตรฐาน หากนี่คือ c ++ ฉันอาจพิจารณาชั้นเรียน
Scott Seidman

3
CMSISมี__REV()และ__REV16()สำหรับการย้อนกลับไบต์
Turbo J

3
ไม่ใช่การโกงเลย - เมื่อคุณทำ I / O ในระดับนี้คุณต้องระวังและจัดการกับความสัมพันธ์ระหว่างลำดับไบต์ภายนอกและลำดับไบต์ภายใน การตั้งค่าของฉันเองคือการแปลงการรับรองภายนอกเป็น / จากการเป็นตัวแทน "ดั้งเดิม" (ภายใน) ที่ระดับต่ำสุดในลำดับชั้นของซอฟต์แวร์ที่สมเหตุสมผลและให้ซอฟต์แวร์ระดับสูงกว่าทั้งหมดจัดการกับรูปแบบดั้งเดิม
Dave Tweed

แม้ว่าคอร์ที่ได้รับการออกแบบโดย ARM Corp. สามารถใช้งานได้กับทั้ง endianness แต่การใช้งาน ARM คอร์ใน STM32F407 นั้นเป็นแบบเอนด์เอนด์เท่านั้น ดูคู่มืออ้างอิง RM0090 หน้า 64 AIRCR.ENDIANNESS เป็นบิตอ่านอย่างเดียว
Laszlo Valko

คำตอบ:


6

ระบบสมองกลฝังตัวมักจะมีปัญหาใหญ่ / ปลายเล็ก ๆ น้อย ๆ วิธีการส่วนตัวของฉันคือการเข้ารหัสหน่วยความจำภายในด้วย endianiness ดั้งเดิมและแลกเปลี่ยนใด ๆ ที่ถูกต้องเมื่อข้อมูลเข้าหรือออก

ฉันโหลดข้อมูลโดยใช้ spi อ่านเป็น analogin0.spibytes [0] - [2] โดยที่ [0] เป็น MSB

โดยการโหลด [0] เป็น MSB คุณกำลังเข้ารหัสค่าเป็น big-endian

analogin0.spibytes [0] = 0xFF และและ analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] เท่ากับ 0xB5FF

สิ่งนี้บ่งชี้ว่าตัวประมวลผลเล็ก ๆ น้อย ๆ endian

หากคุณโหลดค่าแรกลงใน [2] และกลับไปที่ [0] จากนั้นคุณเข้ารหัสหมายเลขที่เข้ามาว่าเป็น endian ตัวน้อยโดยทำการแลกเปลี่ยนเป็นหลักเมื่อจำนวนนั้นเข้ามา เมื่อคุณทำงานกับการเป็นเจ้าของภาษาคุณสามารถกลับไปที่วิธีดั้งเดิมของการใช้การคำนวณทางคณิตศาสตร์ เพียงตรวจสอบให้แน่ใจว่าได้พลิกมันกลับเป็น big-endian เมื่อคุณส่งค่า


5

เกี่ยวกับความโปรดปราน "อยากรู้เกี่ยวกับโหมด endian ใหญ่ srm32f4" ไม่มีโหมด endian ใหญ่ ๆ บนชิปนี้ STM32F4 เข้าถึงหน่วยความจำทั้งหมดใน endian น้อย

คู่มือผู้ใช้http://www.st.com/web/en/resource/technical/document/programming_manual/DM00046982.pdfกล่าวถึงสิ่งนี้ในหน้า 25 แต่มีอีกมาก ในหน้า 93 คุณจะเห็นว่ามีคำแนะนำในการแลกเปลี่ยน endian REV และ REVB สำหรับบิตย้อนกลับและย้อนกลับ REV จะเปลี่ยน endianess สำหรับ 32 บิตและ REV16 จะทำเพื่อรับข้อมูล 16 บิต


3

นี่คือข้อมูลโค้ดสำหรับคอร์เท็กซ์ M4 ที่คอมไพล์ด้วย 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

จาก C การโทรสามารถ:

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

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

ไม่ทราบวิธีการทำเร็วกว่านี้ :-)


คุณสามารถใช้แมโครหรือฟังก์ชันอินไลน์เพื่อทำให้โค้ดนี้เร็วขึ้น
โปร

แล้วข้อมูลขนาด 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.