Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32F4+HAL+FIFO_DMA+UART_TX
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
vitmeat
Доброго времени суток.

Я пытаюсь настроить передачу по UART при помощи кольцевого FIFO буфера с использованием соответствующего режима DMA.
Но как то не получилось у меня приостанавливать передачу, и стартовать ее заново с того же места, всегда получается с начала.
Функции HAL_UART_DMAPause и HAL_UART_DMAResume не работают, по крайней мере так, как я ожидаю.
__HAL_DMA_GET_COUNTER - возвращает количество еще не переданных данных.
__HAL_DMA_SET_COUNTER - устанавливает количество еще не переданных данных, но она опять же не работает.

Настройки DMA
Код
    hdma_tx.Init.Channel             = USARTx_TX_DMA_CHANNEL;
    hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
    hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;
    hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;
    hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
    hdma_tx.Init.Mode                = DMA_CIRCULAR;
    hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;
    hdma_tx.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
    hdma_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
    hdma_tx.Init.MemBurst            = DMA_MBURST_SINGLE;
    hdma_tx.Init.PeriphBurst         = DMA_MBURST_SINGLE;


В итоге сейчас остановился на реализации отдельного кольцевого FIFO буфера и обычной передаче по DMA, а в прерывании DMA дописываю в буфер то, что еще пришло за время передачи. Но получается что у меня два буфера, один FIFO другой DMA.

Подскажите пожалуйста, как правильно работать с кольцевым режимом DMA.
То есть, хочется докладывать в кольцевой буфер пока DMA передает, и не блокировать процесс, и чтобы это был всего один буфер.
Или этого нельзя делать пока DMA не отработал?

DrGluck
sm.gif Этимологию и топонимику DMA в студию ... ( дубль 2 )

Внутренней аппаратной компоненте микроконтроллера под названием "DMA" глубоко индифферентны желания иных участников процесса ..
Увы ... по определению ...
Golikov A.
да ничего они не индифферентны....
они шаряться по памяти, и если вы ее меняете, то и они меняются.
Есть проблема определения точного места, где находиться ДМА сейчас, там есть фифо, и некие задержки. Но в целом реализуемо.
Другое дело как заставить ДМА тормозить когда он отправил что было в буфере, и начинать ехать дальше когда вы дослали данных. Скорее это надо делать на 2 буферах, и по прерыванию отправки первого буфера переводить ДМА на отправку 2 буфера, а самим переключаться на наполнение 1 и так по кругу... Но концы посылок должны быть... ИМХО
vitmeat
Ну в общем суть такая:

* Послали данных, настроили ДМА и он начал их отправлять в юарт
* Дослали еще данных, предыдущая передача ДМА еще не завершена:
- приостанавливаем ДМА HAL_UART_DMAPause
- дописываем данные в буфер
- берем из __HAL_DMA_GET_COUNTER сколько было не отправлено
- плюсуем с количеством новых данных и устанавливаем __HAL_DMA_SET_COUNTER
- обратно запускаем HAL_UART_DMAResume
- в прерывании делаем HAL_UART_DMAStop
Так по идее должно работать.

Как у меня сейчас
* Послали данных, закинули их в фифо, вынули из фифо в буфер ДМА, настроили ДМА и он начал их отправлять в юарт
* Дослали еще данных, предыдущая передача ДМА еще не завершена:
- закинули их в фифо, установили флаг что есть еще данные
- в прерывании окончания передачи, смотрим на флаг, если он установлен, копируем из фифо в буфер ДМА данные и опять запускаем передачу
Дело в том, что так тратится ДВА буфера одинакового размера.
Golikov A.
__HAL_DMA_SET_COUNTER
__HAL_DMA_GET_COUNTER
как объявлены? они точно не один и тот же счетчик крутят?
vitmeat
Ну, в общем, один и тот же:
#define __HAL_DMA_SET_COUNTER(__HANDLE__, __COUNTER__) ((__HANDLE__)->Instance->NDTR = (uint16_t)(__COUNTER__))
#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->NDTR)
DrGluck
"... да ничего они не индифферентны.... они шаряться по памяти, и если вы ее меняете, то и они меняются " ... ©

- записал на скрижали ... sm.gif

SSerge
Цитата(vitmeat @ Aug 26 2015, 23:51) *
Как у меня сейчас
* Послали данных, закинули их в фифо, вынули из фифо в буфер ДМА, настроили ДМА и он начал их отправлять в юарт

А зачем копировать из одного буфера в другой? Натравливайте DMA сразу на буфер фифо.
vitmeat
Цитата(SSerge @ Aug 26 2015, 20:33) *
А зачем копировать из одного буфера в другой? Натравливайте DMA сразу на буфер фифо.

Я так и хочу! Но не понимаю как это настроить.
Либо ДМА начинает все с начала не обращая внимания на мои HAL_UART_DMAPause и __HAL_DMA_SET_COUNTER
Либо выходит за пределы памяти, либо по кругу просто все выдает.

Я просто думал есть какая то последовательность мне неизвестная.
SSerge
Настроить в регистрах DMA начальный адрес и количество пересылаемых байт, запустить. Как закончит - передвинуть свои указатели в фифо, если есть ещё не переданные данные - зарядить следующую пересылку.
Кольцевой режим DMA использовать не получится, только однократный.
В случае, когда данные лежат в конце буфера и продолжаются с его начала придётся первую пересылку настраивать от первого непереданного байта и до конца буфера, а вторую - от начала и до последнего байта на передачу.
Golikov A.
Цитата
- записал на скрижали ... sm.gif

копирайт не забудьте....

Цитата
Ну, в общем, один и тот же:

ну тогда это не совсем то что вы на самом деле хотите менять...

Я думаю стоит почитать про ДМА первоисточник, и забить на эти библиотеки. По мануалу там как-то все не сложно.

king2
Можно еще настроить DMA в режим curcular? чтобы он осылал буфер с начала до конца и потом начинал снова с начала?
тогда по прерыванию half transfer complete докидываете в буфер первую половину, по прерыванию full transfer complete докидываете вторую.
SasaVitebsk
wacko.gif
Работаешь - работаешь. Читаешь доки - читаешь. А потом послушаешь некоторых из местных, и таким... э... идиотом себя чувствуешь... ))

Прошу прощения, а можно мне пояснить ...
Зачем использовать DMA с FIFO. Скажем, что это даёт? Я когда читал, то прошёл это по диагонали. Подумал, что это используется с устройствами которые с FIFO. А так как в UART FIFO нет, то и сделал без этого ... Растолкуйте по русски плиз. ))
SSerge
Цитата(SasaVitebsk @ Aug 27 2015, 12:14) *
Прошу прощения, а можно мне пояснить ...
Зачем использовать DMA с FIFO. Скажем, что это даёт?

Буферизация ввода/вывода в любом случае полезна, даже без DMA.
Вот написали Вы в своей программе printf("Hello world!\n"); что приведёт к передаче в stdout тринадцати байт, а то и 14, если \n заменяется при выводе на пару \n\r.
Ну что теперь, ждать пока все эти байты будут переданы уартом и только после этого возвращать управление из printf ?
Конечно же нет, гораздо лучше передаваемые данные закинуть в некий промежуточный буфер, инициировать начало передачи и вернуться в основную программу, следующий оператор исполнять. А уж драйвер, обслуживающий уарт пусть сам разбирается с передачей, нам по большому счёту неважно как он будет это делать, по прерываниям или с использованием DMA.

Аналогично и на приёме. Байты по последовательной линии приходят не так уж и часто, по сравнению с быстродействием процессора, поэтому постоянно крутиться в цикле ожидания - а не принял ли наш уарт очедной байт? - неэффективно, надо же и другую, не менее важную работу делать. Поэтому драйвер уарта самостоятельно (по прерываниям, например) принимает приходящие данные и складирует их до времени в буфер (тот самый фифо). А когда основная программа соизволит поинтересоваться "ну что там?" отдаст ей принятое в лучшем виде.

Собственно, в обоих случаях фифо исполняет по сути одну и ту же функцию - выравнивает неравномерность скоростей поступления и переработки информации, и чем больше эта неравномерность тем больше должен быть размер буфера для избежания необходимости приостанавливать работу то источника, то потребителя.

В примере с printf буферизация на самом деле выполняется дважды. Ведь stdout это указатель на структуру FILE, в этой структуре лежат указатели на несколько буферов, т.е. эта структура вместе с низкоуровневыми функциями является фифо-буфером. В случае с уартом в принципе можно обойтись однократной буферизацией, либо работая напрямую с буферами из FILE либо заменив его своей реализацией фифо (и потерять с возможность использовать printf и putchar). Для более сложных устройств двойная буферизация - в программе и в драйвере - обычное дело.
SasaVitebsk
Цитата(SSerge @ Aug 27 2015, 10:30) *
Буферизация ввода/вывода в любом случае полезна, даже без DMA.

Наверное я объяснил неверно. Буферизация мне понятна. Вот сделал я буфер и отсылаю этот готовый пакет в UART. Я его отсылаю ч/з DMA. А причём здесь FIFO. Вот это непонятно.
Вот так я передаю
Код
// Передача в nCOM. nCOM = 0, 1, 2.
// Через DMA
void vUSARTTransmit(uint32_t nCom)
{
  DMA_Stream_TypeDef *DMA_Stream;
  
  // Выбрать канал DMA
  // Остановить его на всякий случай
  // Очистить флаги
  // Выбрать канал
  // Инициализировать адрес периферии в DMA контроллере
  if(nCom == 1)
  {
    USART2_WR;                                                // Переключаю на передачу
    //vTaskDelay(10);                                            // Сделать паузу
    DMA_Stream = DMA1_Stream6;
    //DMA_Stream->CR &= ~DMA_SxCR_EN;
    //DMA1->LIFCR = DMA_LIFCR_CTCIF2 | DMA_LIFCR_CHTIF2 | DMA_LIFCR_CTEIF2 | DMA_LIFCR_CDMEIF2 | DMA_LIFCR_CFEIF2;
    DMA_Stream->PAR = (uint32_t)&USART2->DR;                // USART 2
    USART2->CR3 |= USART_CR3_DMAT;                            // разрешить передачу
    USART2->CR1 &= ~USART_CR1_RE;                            // для подавления эха запретить чтение
    //USART2->SR = (uint16_t)~USART_SR_TC;
  }
  else if(nCom == 2)
  {
    USART3_WR;                                                // Переключаю на передачу
    //vTaskDelay(10);                                            // Сделать паузу
    DMA_Stream = DMA1_Stream3;
    //DMA_Stream->CR &= ~DMA_SxCR_EN;
    //DMA1->LIFCR = DMA_LIFCR_CTCIF3 | DMA_LIFCR_CHTIF3 | DMA_LIFCR_CTEIF3 | DMA_LIFCR_CDMEIF3 | DMA_LIFCR_CFEIF3;
    DMA_Stream->PAR = (uint32_t)&USART3->DR;                // USART 3
    USART3->CR3 |= USART_CR3_DMAT;                            // разрешить передачу
    //USART3->SR = (uint16_t)~USART_SR_TC;
  }
  else
  {
    DMA_Stream = DMA2_Stream7;
    //DMA_Stream->CR &= ~DMA_SxCR_EN;
    //DMA1->LIFCR = DMA_LIFCR_CTCIF1 | DMA_LIFCR_CHTIF1 | DMA_LIFCR_CTEIF1 | DMA_LIFCR_CDMEIF1 | DMA_LIFCR_CFEIF1;
    DMA_Stream->PAR = (uint32_t)&USART1->DR;                // USART 1
    USART1->CR3 |= USART_CR3_DMAT;                            // разрешить передачу
    //USART1->SR = (uint16_t)~USART_SR_TC;
  }

  ComData[nCom].fRead = FALSE;
  // CR Memory & Peripheral single transfer, Priority level Medium, Peripheral increment PSIZE, Memory & Peripheral data size = byte
  // Memory increment, Peripheral no increment, не повторять, Memory-to-peripheral, Transfer complete interrupt enable, канал 4
  DMA_Stream->CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_PL_0 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE;
  // Указать буфер
  DMA_Stream->M0AR = (uint32_t)ModbusData[nCom].pTxBuf;
  // Загрузить длину пакета
  DMA_Stream->NDTR = ModbusData[nCom].Ptr;
  // FIFO is full
  //DMA_Stream->FCR = DMA_SxFCR_FTH;
  ComData[nCom].state = - ModbusData[nCom].Ptr;
  
  
  // Разрешить работу DMA
  DMA_Stream->CR |= DMA_SxCR_EN;
}

ну и два прерывания. По завершению DMA (разрешаю прерывание по завершению передачи). И по завершению передачи (переключение на приём).
SSerge
Ну так модбасу больше одного пакета за раз передавать не надо, а если просто с терминалом общаться?
Всего-то и делов - две строчки выдать одну за другой, но как? К тому моменту как захочется вторую строку напечатать из первой от силы два байта успеет уйти, а единственный буфер уже занят, с ним сейчас DMA работает, опять ждать?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.