Ендіанська проблема на STM32


11

Я використовую arm gcc (CooCox) для програмування відкриття STM32F4, і я вела боротьбу з ендіанською проблемою

Я здійснюю вибірку з 24-бітовим АЦП через SPI. Оскільки три байти входять, у MSB спершу у мене виникла ідея завантажити їх у союз, щоб зробити їх (все одно сподівався!) Трохи легшими у використанні.

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

Я завантажую дані, використовуючи spi reads, в analogin0.spibytes [0] - [2], з [0] як MSB, потім випилюю їх через USART з мегабаудом, 8 біт одночасно. Немає проблем.

Проблеми почалися, коли я намагався передати дані в 12-бітний ЦАП. Цей ЦАП SPI хоче 16-бітових слів, які складаються з 4-бітного префікса, що починається з MSB, а потім 12 біт даних.

Початкові спроби полягали в перетворенні доповнення двох, 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)

... і це спрацювало чудово. Коли я заглядаю на темп після першого рядка коду, його 0xFFB5, а не 0xB5FF, тому все добре

Отже, для питань ...

  • Кортекс для мене новий. Я не можу згадати PIC, коли б байт мінявся в int16, хоча обидві платформи мало ендіатичні. Це правильно?

  • Чи є більш елегантний спосіб впоратися з цим? Було б чудово, якби я міг просто перевести ARM7 в режим big-endian. Я бачу, що багато посилань на Cortex M4 є двоіндійськими, але, здається, всі джерела не відповідають дійсності розповісти мені, як . Більш конкретно, як перевести STM32f407 в режим big-endian , ще краще, якщо це можна зробити в gcc. ЧИ ПОТРІБНО це встановити відповідний біт у реєстрі AIRCR? Чи є які-небудь наслідки, такі як встановлення компілятора на збіг, чи математичне виправлення пізніше з непослідовними бібліотеками ??


2
"Оскільки три байти надходять, спочатку MSB" - це великий ендіанець, тоді як у вас процесор є мало-ендіанським, тому тут починається ваша проблема. Я б шукав макрос-компілятор / стандартні функції бібліотеки, щоб робити 16/32-бітові обміни байтів, як правило, вони реалізовані найбільш ефективно для конкретної платформи процесора. Звичайно, використання біт-розумного зсуву / ANDing / ORing також добре.
Ласло Валько

Я припускаю, що я міг би заповнити analogin0.spibytes в будь-якому порядку, який я хочу, але це здається чимось обманом, тому що я мав би пам’ятати наказ, щоб видалити його, щоб передати його через usart. Я думаю, що 3-байтний формат робить речі дещо нестандартними. Якби це c ++, я б міг вважати клас.
Скотт Сейдман

3
CMSIS має __REV()і __REV16()для обертання байтів.
Turbo J

3
Це зовсім не обман - коли ви робите I / O на цьому рівні, ви повинні бути обізнані і мати справу з взаємозв'язком між зовнішнім порядком байтів і внутрішнім порядком байтів. Моє власне вподобання - перетворити зовнішні представлення на / з "рідних" (внутрішніх) уявлень на найнижчому рівні в ієрархії програмного забезпечення, що має сенс, і дозволити всім програмним забезпеченням вищого рівня працювати лише з нативним форматом.
Дейв Твід

Навіть незважаючи на те, що ядро, розроблене ARM Corp., здатне працювати з будь-якою витримкою, реалізація ядра ARM в STM32F407 є лише малоінтенсивною. Див. Довідковий посібник RM0090, сторінка 64. AIRCR.ENDIANNESS - біт, доступний лише для читання.
Ласло Валько

Відповіді:


6

Вбудовані системи завжди матимуть проблему big-endian / little-endian. Мій особистий підхід полягав у тому, щоб завжди кодувати внутрішню пам'ять з прибутковою ендіанічністю і робити будь-які заміни правильно, коли дані надходять або залишаються.

Я завантажую дані, використовуючи spi reads, в analogin0.spibytes [0] - [2], з [0] як MSB

Завантажуючи [0] як MSB, ви кодуєте значення як big-endian.

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 біт, а REV16 зробить це для 16-бітових даних.


3

Ось фрагмент коду для кори М4, складений з 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 );

Не знаю, як це зробити швидше :-)


ви можете використовувати макрос або вбудовану функцію, щоб зробити цей код швидшим
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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.