STM32'de Endian sorunu


11

Kol gcc (CooCox) bir STM32F4discovery programlamak için kullanıyorum ve bir endian sorunu w / güreş

SPI üzerinden 24 bitlik ADC ile örnek alıyorum. Üç bayt geldiğinden, MSB ilk önce onları (daha önce umuyorum!) Kullanımı biraz daha kolay hale getirmek için bir sendikaya yükleme fikrim vardı.

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

MSB olarak [0] ile spi okumaları analogin0.spibytes [0] - [2] 'ye veri yükledim, sonra bir kerede 8 bitlik bir megabaudda USART aracılığıyla tükürdüm. Sorun yok.

Verileri 12 bitlik bir DAC'ye aktarmaya çalıştığımda sorunlar başladı. Bu SPI DAC, MSB'de başlayan 4 bit önek ve ardından 12 bit veri içeren 16 bit sözcükler ister.

İlk denemeler, ADC'nin verdiği ikişer tamamlayıcıyı, analogin0.spihalfwords [0] 'u 0x8000 ile xoring ederek, sonucu alt 12 bite kaydırarak ve sonra önekini aritmetik olarak ekleyerek ikiliyi dengelemekti.

Analogin0.spibytes [0] = 0xFF ve ve analogin0.spibytes [1] = 0xB5 için analogin0.halfwords [0] 'ın 0xFFB5'e eşit olduğunu ve 0xFFB5'e eşit olduğunu fark edene kadar inanılmaz derecede sinir bozucu !!!!!

Bunu fark ettikten sonra, aritmetik işlemleri ve yarım kelimeyi kullanmayı bıraktım ve bitsel mantık ve baytlara yapıştım

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)

... ve bu işe yaradı. Kodun ilk satırından sonra temp'e göz attığımda 0xFFB5 değil 0xB5FF, bu yüzden her şey iyi

Sorular için ...

  • Korteks benim için yeni. Her iki platformun da küçük endian olmasına rağmen PIC'yi int16'larda değiştirdiğini hatırlayamıyorum. Bu doğru mu?

  • Bununla baş etmenin daha zarif bir yolu var mı? ARM7'yi büyük endian moduna sokabilseydim harika olurdu. Cortex M4'ün bi-endian olduğuna dair birçok referans görüyorum, ancak tüm kaynaklar bana nasıl yapılacağını söylemekten yoksun gibi görünüyor . Daha spesifik olarak, STM32f407'yi büyük endian moduna nasıl yerleştirebilirim, gcc'de yapılabilirse daha iyi. Bu SADECE AIRCR kaydında uygun bitin ayarlanmasıyla mı ilgili? Derleyiciyi eşleştirmek için ayarlamak veya daha sonra tutarsız kütüphanelerle matematik vidaları gibi herhangi bir sonuç var mı?


2
"Üç bayt geldiğinden, önce MSB" - bu big-endian, CPU'nuz küçük-endian, yani probleminiz burada başlıyor. 16/32-bit bayt değişimi yapmak için derleyici makroları / standart kütüphane fonksiyonları ararım, genellikle belirli CPU platformu için en verimli şekilde uygulanırlar. Tabii ki, bit bilge kaydırma / ANDing / ORing kullanmak da iyidir.
Laszlo Valko

Ben herhangi bir sırayla analogin0.spibytes şeyler olabilir sanırım, ama usart üzerinden geçmek için unstuff emri hatırlamak zorunda gibi, bu da biraz hile gibi görünüyor. Bence 3 bayt formatı işleri biraz standart dışı kılıyor. Bu c ++ olsaydı, bir sınıf düşünebilirsiniz.
Scott Seidman

3
CMSIS sahiptir __REV()ve __REV16()bayt tersine çevrilmesi için.
Turbo J

3
Her bir hile değil - bu seviyede / O I yapıyoruz, sen gerekir farkında olmalı ve dış bayt düzen ve iç bayt düzen arasındaki ilişki ile uğraşmak. Kendi tercihim, harici gösterimleri anlamlı olan yazılım hiyerarşisinde en düşük düzeyde "yerel" (dahili) gösterimlere dönüştürmek ve tüm üst düzey yazılımların yalnızca yerel formatla ilgilenmesine izin vermektir.
Dave Tweed

ARM Corp. tarafından tasarlanan çekirdek her iki endianlıkla da çalışabilse de, STM'nin STM32F407'deki ARM çekirdeğini uygulaması sadece küçük endian. Bkz. Referans Kılavuz RM0090 sayfa 64. AIRCR.ENDIANNESS salt okunur bir bit.
Laszlo Valko

Yanıtlar:


6

Gömülü sistemlerde her zaman big-endian / little-endian sorunu olacaktır. Kişisel yaklaşımım, iç belleği her zaman yerel endianiness ile kodlamak ve veri girdiğinde veya ayrıldığında doğrudan takas yapmak olmuştur.

Verileri spi reads kullanarak analogin0.spibytes [0] - [2] 'ye yüklüyorum, [0] MSB olarak

MSB olarak [0] yükleyerek, değeri big-endian olarak kodlamış olursunuz.

analogin0.spibytes [0] = 0xFF ve ve analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] 0xB5FF'ye eşitti

Bu, işlemcinin küçük endian olduğunu gösterir.

Bunun yerine, ilk değeri [2] 'ye yüklerseniz ve [0]' a geri çalışırsanız, gelen sayıyı küçük endian olarak kodladınız ve esas olarak sayı girilirken takas yaparsınız. Yerel temsil ile çalıştıktan sonra, aritmetik işlemleri kullanma konusundaki orijinal yaklaşımınıza dönebilirsiniz. Değeri ilettiğinizde tekrar big-endian'a çevirdiğinizden emin olun.


5

"Gerçekten gerçekten srm32f4 büyük endian modu hakkında bilmek istiyorum" ile ilgili, bu çip üzerinde büyük bir endian modu yoktur. STM32F4 tüm bellek erişimini küçük endian olarak yapar.

Kullanım kılavuzu http://www.st.com/web/tr/resource/technical/document/programming_manual/DM00046982.pdf bu sayfa 25'ten bahseder. Ama dahası da var. 93 Sayfasında endian takas talimatları vardır. Ters ve ters bit için REV ve REVB. REV 32 bit için endianess'i değiştirecek ve REV16 16 bit veri için yapacak.


3

İşte gcc ile derlenmiş bir korteks M4 için bir kod snippet'i

/*
 * 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'den çağrı şu olabilir:

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

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

Bundan daha hızlı nasıl yapılacağını bilmiyorum :-)


bu kodu daha hızlı yapmak için bir makro veya satır içi işlev kullanabilirsiniz
pro

24 bit veriye ne dersiniz?
pengemizt

1

CooCox STM32F429 için bu sorun değil:

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);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.