Перейти к содержанию
    

Сдвиг на бит при передаче по SPI DMA

Запускаю ЦАП AD5320BRTZ-500RL7 на STM32F407 при помощи HAL, использую SPI на скорости 62 кб/с и софтовый чип-селект, настроенный как GPIO_SPEED_FREQ_VERY_HIGH. Вот таким кодом в блокирующем режиме я успешно устанавливаю требуемое напряжение на выходе ЦАП:

Спойлер

HAL_StatusTypeDef DAC_send_spi (DAC_config_packet_t config_packet)
{
  uint8_t  mode;                 /// - переменная для хранения режима ЦАП, извлеченного из переданного в функцию экземпляра;
  uint16_t value;                /// - переменная для хранения значения ЦАП, извлеченного из переданного в функцию экземпляра;
  uint8_t  dac_spi_tx_packet[2]; /// - массив для отправки в ЦАП;

  /// - парсим экземпляр;
  mode  = config_packet.dac_mode;
  value = config_packet.dac_value;

  /// - обнуление не значащих бит;
  mode  &= ~0xFC;
  value &= ~0xF000;

  /// - формирование массива из двух байт для отправки в ЦАП;
  dac_spi_tx_packet[0] = (uint8_t) ((mode<<4) | (value>>8));
  dac_spi_tx_packet[1] = (uint8_t) value;

  /// - опускаем чип-селект SPI1;
  HAL_GPIO_WritePin(DAC_SYNC_1_GPIO_Port, DAC_SYNC_1_Pin, GPIO_PIN_RESET);

  /// - отправка команды в ЦАП по SPI1 в блокирующем режиме;
  HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, dac_spi_tx_packet, 2, 100);

  HAL_GPIO_WritePin(DAC_SYNC_1_GPIO_Port, DAC_SYNC_1_Pin, GPIO_PIN_SET);
  return status;
}

 Но стало интересно передать при помощи DMA. Меняю код, у DMA приоритет very high. Чип-селект поднимаю в прерывании по окончанию передачи:

Спойлер

HAL_StatusTypeDef DAC_send_spi (DAC_config_packet_t config_packet)
{
  uint8_t  mode;                 /// - переменная для хранения режима ЦАП, извлеченного из переданного в функцию экземпляра;
  uint16_t value;                /// - переменная для хранения значения ЦАП, извлеченного из переданного в функцию экземпляра;
  uint8_t  dac_spi_tx_packet[2]; /// - массив для отправки в ЦАП;

  /// - парсим экземпляр;
  mode  = config_packet.dac_mode;
  value = config_packet.dac_value;

  /// - обнуление не значащих бит;
  mode  &= ~0xFC;
  value &= ~0xF000;

  /// - формирование массива из двух байт для отправки в ЦАП;
  dac_spi_tx_packet[0] = (uint8_t) ((mode<<4) | (value>>8));
  dac_spi_tx_packet[1] = (uint8_t) value;

  /// - опускаем чип-селект SPI1;
  HAL_GPIO_WritePin(DAC_SYNC_1_GPIO_Port, DAC_SYNC_1_Pin, GPIO_PIN_RESET);

  /// - отправка команды в ЦАП по SPI1 в блокирующем режиме;
  HAL_StatusTypeDef status = HAL_SPI_Transmit_DMA(&hspi1, dac_spi_tx_packet, 2);


  return status;
}

 

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
  /// Включает в себя следующие операции:
  /// - если переданный в структуру экземпляр это hspi1, то:
  if (hspi == &hspi1)
  {
  /// - поднимаем чип-селект SPI1;
  HAL_GPIO_WritePin(DAC_SYNC_1_GPIO_Port, DAC_SYNC_1_Pin, GPIO_PIN_SET);
  }
}

В итоге получается, что ЦАП устанавливает значение напряжения, смещенное на 1 бит вправо относительно требуемого (напряжение в 2 раза меньше требуемого). При передаче по прерыванию та же самая проблема.

Читал что писали здесь про подобные проблемы, про жертвы SPL и отслеживания флагов в интерфейсе SPI. Сомневаюсь, что HAL библиотекой не добиться нормальной передачи. Был бы признателен, если подскажите куда копать, чтобы решить проблему только библиотекой  HAL.

Код инициализации SPI:

Спойлер

hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 10/6/2023 at 11:30 AM, Turgenev said:

Был бы признателен, если подскажите куда копать, чтобы решить проблему только библиотекой  HAL.

 

Осциллографом надо посмотреть, что там на SPI твориться.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Надо использовать событие по окончанию RX, даже если прием не используете. Событие по TX это когда освободился буфер передатчика, но передача ещё продолжается из сдвигового регистра.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 минуту назад, deni сказал:

Надо использовать событие по окончанию RX, даже если прием не используете. Событие по TX это когда освободился буфер передатчика, но передача ещё продолжается из сдвигового регистра.

Читал про это на форуме и попробовал поднимать чип-селект по колбеку приема:

Спойлер

void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
  /// Включает в себя следующие операции:
  /// - если переданный в структуру экземпляр это hspi1, то:
  if (hspi == &hspi1)
  {
  /// - поднимаем чип-селект SPI1;
  HAL_GPIO_WritePin(DAC_SYNC_1_GPIO_Port, DAC_SYNC_1_Pin, GPIO_PIN_SET);
  }
}

Ничего не изменилось- так же сдвинут пакет на ЦАПе.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 минуты назад, Turgenev сказал:

Ничего не изменилось- так же сдвинут пакет на ЦАПе.

Прием никак не может закончится раньше, чем когда придет последний клок. Может у вас полярность и фаза клока неверно настроена? Надо смотреть логическим анализатором, что происходит на линиях.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

21 минуту назад, deni сказал:

Может у вас полярность и фаза клока неверно настроена?

 

20 минут назад, Edit2007 сказал:

проверьте фазу формирования сигнала относительно CLK.

C ней я много намучался. Но если было бы неправильно настроено, смог бы я отправлять правильно в блокирующем режиме? А ведь в нем у меня проблем нет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

26 минут назад, Turgenev сказал:

C ней я много намучался. Но если было бы неправильно настроено, смог бы я отправлять правильно в блокирующем режиме? А ведь в нем у меня проблем нет.

В блокирующем режиме в конце опрашивается флаг SPI_FLAG_BSY. Возможно в режиме DMA код HAL не дожидается окончания передачи. И если даже использовать окончание по RX, оно будет вызвано раньше на пол клока окончания передачи. Так как частота SPI у вас не высокая, контроллер успевает выставить CS на GPIO раньше окончания передачи.

Изменено пользователем deni

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 минуту назад, deni сказал:

Так как частота SPI у вас не высокая контроллер успевает выставить CS на GPIO раньше окончания передачи.

Ок. Попробую передавать по DMA на бОльшей скорости и со всеми режимами полярности и фазы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хотя. Так надоело гадать. Подскажите как по ДШ надо было это все понять и настроить с первого раза. 

Судя по временной диаграмме (скрин) на полярность тактового сигнала все равно. Судя по той же диаграмме и описанию (скрин), смотреть надо по ниспадающему фронту, то есть hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;

Спойлер

image.thumb.png.4e71df2b06a88fc48f834f4965b168a6.pngimage.png.2646e616f2fa9403157a0b9101547bb4.png

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

22 минуты назад, Turgenev сказал:

Хотя. Так надоело гадать. Подскажите как по ДШ надо было это все понять и настроить с первого раза. 

Выкинуть Cube и HAL, сделать на LL.

 

25 минут назад, Turgenev сказал:

Судя по временной диаграмме (скрин) на полярность тактового сигнала все равно.

Судя по диаграмме, нужно установить CS (SYNC), потом снять CS (SYNC) и потом по SPI выдать данные привязанные к падающему фронту.

 

Если хочется полной автоматизации передачи, то проще по таймеру по одному из каналов выдать импульс SYNC через PWM и по далее по событию от таймера через DMA в SPI выдать данные.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 минут назад, deni сказал:

Если хочется полной автоматизации передачи, то проще по таймеру по одному из каналов выдать импульс SYNC через PWM и по далее по событию от таймера через DMA в SPI выдать данные.

Именно так и работаю сейчас с аналогичной AD5322: LDAC формируется ШИМ-ом, а передачи 2-х слов между импульсами LDAC - посредством таймер->DMA->SPI(с аппаратным формированием CS). Таймер, раз в период, пинает DMA, тот читает 1 слово из ОЗУ и пишет 2 полуслова в SPI (2 фрейма получается). Работает как часы. Без всяких Кубов и Калов. На скорости на ~порядок выше. При том, что процессор у меня гораздо слабее чем у ТС.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 10/6/2023 at 2:11 PM, jcxz said:

 Без всяких Кубов и Калов. 

Это же надо в Reference Manual вникать :sarcastic:

On 10/6/2023 at 2:00 PM, deni said:

Судя по диаграмме, нужно установить CS (SYNC), потом снять CS (SYNC) и потом по SPI выдать данные привязанные к падающему фронту.

Немного не так.

SYNC должен быть в единице между транзакциями. Это тот же самый переименованный CS.

Quote

Level Triggered Control Input (Active Low).

This is the frame synchronization signal for the input data. When SYNC goes low, it enables the input shift register and data is transferred in on the falling edges of the following clocks.

The DAC is updated following the 16th clock cycle unless SYNC is taken high before this edge, in which case the rising edge of SYNC acts as an interrupt and the write sequence is ignored by the DAC.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

35 минут назад, deni сказал:

Если хочется полной автоматизации передачи, то проще по таймеру по одному из каналов выдать импульс SYNC через PWM и по далее по событию от таймера через DMA в SPI выдать данные.

Чем моя реализация плоха с установкой/сброса пина чип-селекта, зачем делать это именно через ШИМ? Все в свое время выполняется же, кроме окончания- но я ради интереса подождал пару секунд перед очередным подъемом чип-селекта после передачи пакета. Без разницы.

 

38 минут назад, deni сказал:

Выкинуть Cube и HAL, сделать на LL.

Тогда конкретный вопрос: чисто на HAL-функциях нормальную передачу по SPI не сделать что ли?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 10/6/2023 at 2:39 PM, Turgenev said:

Чем моя реализация плоха с установкой/сброса пина чип-селекта, зачем делать это именно через ШИМ? Все в свое время выполняется же, кроме окончания- но я ради интереса подождал пару секунд перед очередным подъемом чип-селекта после передачи пакета. Без разницы

В вашем случае смысл передачи по DMA пропадает, т.к. все равно приходится отвлекаться на дергание ножкой CS. 

Для такого режима передачи проще использовать прерывания от SPI.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...