Проблема Endian на STM32

11

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

Я использую 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 с мегабодом, 8 бит за раз. Нет проблем.

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

Первоначальные попытки состояли в том, чтобы преобразовать два дополнения, которые АЦП дал мне для смещения двоичного кода, путем перехвата аналога аналога 0.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, хотя обе платформы имеют младший порядок байтов. Это верно?

  • Есть ли более элегантный способ справиться с этим? Было бы здорово, если бы я мог просто перевести ARM7 в режим с прямым порядком байтов. Я вижу много ссылок на то, что Cortex M4 является bi-endian, но, похоже, все источники не дают понять, как это сделать . Более конкретно, как мне перевести STM32f407 в режим с прямым порядком байтов , даже лучше, если это можно сделать в gcc. Это просто вопрос установки соответствующего бита в регистре AIRCR? Есть ли какие-либо последствия, такие как необходимость настройки соответствия компилятора, или математические ошибки позже с несовместимыми библиотеками?

Скотт Сейдман
источник
2
«Так как поступают три байта, сначала MSB» - это порядок с прямым порядком байтов, тогда как у вашего процессора порядок с прямым порядком байтов, так что здесь начинается ваша проблема. Я хотел бы найти макросы компилятора / функции стандартной библиотеки для выполнения 16/32-битной замены байтов, обычно они реализованы наиболее эффективным способом для конкретной платформы ЦП. Конечно, использование побитового сдвига / ANDing / ORing также хорошо.
Ласло Валко
Я полагаю, что я мог бы заполнить analogin0.spibytes в любом порядке, который я захочу, но это тоже немного обманывает, так как я должен был бы вспомнить приказ, чтобы распаковать его, чтобы передать через usart. Я думаю, что 3-байтовый формат делает вещи немного нестандартными. Если бы это был C ++, я мог бы рассмотреть класс.
Скотт Сейдман
3
CMSIS имеет __REV()и __REV16()для реверсирования байтов.
Turbo J
3
Это вовсе не обман - когда вы выполняете ввод-вывод на этом уровне, вы должны знать и иметь дело с отношениями между внешним порядком байтов и внутренним порядком байтов. Я предпочитаю преобразовывать внешние представления в / из «нативных» (внутренних) представлений на самом низком уровне в иерархии программного обеспечения, что имеет смысл, и позволять всему программному обеспечению более высокого уровня работать только с собственным форматом.
Дэйв Твид
Хотя ядро, разработанное ARM Corp., может работать с любым порядком байтов, STM реализует ядро ​​ARM в STM32F407 только с прямым порядком байтов. См. Справочное руководство RM0090 на стр. 64. AIRCR.ENDIANNESS - бит только для чтения.
Ласло Валко

Ответы:

6

Встраиваемые системы всегда будут иметь проблему с прямым порядком байтов / прямым порядком байтов. Мой личный подход состоял в том, чтобы всегда кодировать внутреннюю память с присущим ей порядком байтов и делать любые перестановки прямо при входе или выходе данных.

Я загружаю данные, используя spi-чтения в analogin0.spibytes [0] - [2], с [0] в качестве MSB

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

analogin0.spibytes [0] = 0xFF и и analogin0.spibytes [1] = 0xB5, analogin0.halfwords [0] был равен 0xB5FF

Это указывает на то, что процессор имеет младший порядок.

Если вместо этого вы загружаете первое значение в [2] и возвращаетесь к [0], то вы закодировали входящий номер как little-endian, по сути, делая своп при вводе числа. Как только вы работаете с нативным представлением, вы можете вернуться к исходному подходу использования арифметических операций. Просто убедитесь, что вы перевернули его в обратный порядок при передаче значения.

DrRobotNinja
источник
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

Вот фрагмент кода для коры 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 );

Не знаю, как сделать быстрее, чем это :-)

Оливье Монном
источник
Вы можете использовать макрос или встроенную функцию, чтобы ускорить этот код
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);
Аргебир Лаборатуравар
источник