реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> STM32F4+HAL+FIFO_DMA+UART_TX
vitmeat
сообщение Aug 26 2015, 14:26
Сообщение #1





Группа: Участник
Сообщений: 13
Регистрация: 22-01-11
Пользователь №: 62 402



Доброго времени суток.

Я пытаюсь настроить передачу по 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 не отработал?

Go to the top of the page
 
+Quote Post
DrGluck
сообщение Aug 26 2015, 14:41
Сообщение #2


Местный
***

Группа: Участник
Сообщений: 340
Регистрация: 25-11-05
Из: Россия
Пользователь №: 11 361



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

Внутренней аппаратной компоненте микроконтроллера под названием "DMA" глубоко индифферентны желания иных участников процесса ..
Увы ... по определению ...


--------------------
"... Ищущий вечно, однажды найдя, то, что искал бесконечно, мимо прошёл, совершенно беспечно, с кем-то о вечном шутя ..."
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Aug 26 2015, 16:22
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



да ничего они не индифферентны....
они шаряться по памяти, и если вы ее меняете, то и они меняются.
Есть проблема определения точного места, где находиться ДМА сейчас, там есть фифо, и некие задержки. Но в целом реализуемо.
Другое дело как заставить ДМА тормозить когда он отправил что было в буфере, и начинать ехать дальше когда вы дослали данных. Скорее это надо делать на 2 буферах, и по прерыванию отправки первого буфера переводить ДМА на отправку 2 буфера, а самим переключаться на наполнение 1 и так по кругу... Но концы посылок должны быть... ИМХО
Go to the top of the page
 
+Quote Post
vitmeat
сообщение Aug 26 2015, 16:51
Сообщение #4





Группа: Участник
Сообщений: 13
Регистрация: 22-01-11
Пользователь №: 62 402



Ну в общем суть такая:

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

Как у меня сейчас
* Послали данных, закинули их в фифо, вынули из фифо в буфер ДМА, настроили ДМА и он начал их отправлять в юарт
* Дослали еще данных, предыдущая передача ДМА еще не завершена:
- закинули их в фифо, установили флаг что есть еще данные
- в прерывании окончания передачи, смотрим на флаг, если он установлен, копируем из фифо в буфер ДМА данные и опять запускаем передачу
Дело в том, что так тратится ДВА буфера одинакового размера.

Сообщение отредактировал vitmeat - Aug 26 2015, 16:54
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Aug 26 2015, 17:03
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



__HAL_DMA_SET_COUNTER
__HAL_DMA_GET_COUNTER
как объявлены? они точно не один и тот же счетчик крутят?
Go to the top of the page
 
+Quote Post
vitmeat
сообщение Aug 26 2015, 17:11
Сообщение #6





Группа: Участник
Сообщений: 13
Регистрация: 22-01-11
Пользователь №: 62 402



Ну, в общем, один и тот же:
#define __HAL_DMA_SET_COUNTER(__HANDLE__, __COUNTER__) ((__HANDLE__)->Instance->NDTR = (uint16_t)(__COUNTER__))
#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->NDTR)
Go to the top of the page
 
+Quote Post
DrGluck
сообщение Aug 26 2015, 17:18
Сообщение #7


Местный
***

Группа: Участник
Сообщений: 340
Регистрация: 25-11-05
Из: Россия
Пользователь №: 11 361



"... да ничего они не индифферентны.... они шаряться по памяти, и если вы ее меняете, то и они меняются " ... ©

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



--------------------
"... Ищущий вечно, однажды найдя, то, что искал бесконечно, мимо прошёл, совершенно беспечно, с кем-то о вечном шутя ..."
Go to the top of the page
 
+Quote Post
SSerge
сообщение Aug 26 2015, 17:33
Сообщение #8


Профессионал
*****

Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528



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

А зачем копировать из одного буфера в другой? Натравливайте DMA сразу на буфер фифо.


--------------------
Russia est omnis divisa in partes octo.
Go to the top of the page
 
+Quote Post
vitmeat
сообщение Aug 26 2015, 17:39
Сообщение #9





Группа: Участник
Сообщений: 13
Регистрация: 22-01-11
Пользователь №: 62 402



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

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

Я просто думал есть какая то последовательность мне неизвестная.
Go to the top of the page
 
+Quote Post
SSerge
сообщение Aug 26 2015, 18:04
Сообщение #10


Профессионал
*****

Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528



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


--------------------
Russia est omnis divisa in partes octo.
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Aug 26 2015, 19:48
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



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

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

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

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

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

Go to the top of the page
 
+Quote Post
king2
сообщение Aug 26 2015, 22:05
Сообщение #12


Местный
***

Группа: Свой
Сообщений: 255
Регистрация: 17-08-06
Из: Москва
Пользователь №: 19 646



Можно еще настроить DMA в режим curcular? чтобы он осылал буфер с начала до конца и потом начинал снова с начала?
тогда по прерыванию half transfer complete докидываете в буфер первую половину, по прерыванию full transfer complete докидываете вторую.
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Aug 27 2015, 05:14
Сообщение #13


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



wacko.gif
Работаешь - работаешь. Читаешь доки - читаешь. А потом послушаешь некоторых из местных, и таким... э... идиотом себя чувствуешь... ))

Прошу прощения, а можно мне пояснить ...
Зачем использовать DMA с FIFO. Скажем, что это даёт? Я когда читал, то прошёл это по диагонали. Подумал, что это используется с устройствами которые с FIFO. А так как в UART FIFO нет, то и сделал без этого ... Растолкуйте по русски плиз. ))
Go to the top of the page
 
+Quote Post
SSerge
сообщение Aug 27 2015, 07:30
Сообщение #14


Профессионал
*****

Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528



Цитата(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). Для более сложных устройств двойная буферизация - в программе и в драйвере - обычное дело.


--------------------
Russia est omnis divisa in partes octo.
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Aug 27 2015, 09:02
Сообщение #15


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Цитата(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 (разрешаю прерывание по завершению передачи). И по завершению передачи (переключение на приём).
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 19th July 2025 - 01:51
Рейтинг@Mail.ru


Страница сгенерированна за 0.01505 секунд с 7
ELECTRONIX ©2004-2016