|
STM32F4+HAL+FIFO_DMA+UART_TX |
|
|
|
Aug 26 2015, 14:26
|
Группа: Участник
Сообщений: 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 не отработал?
|
|
|
|
|
Aug 26 2015, 14:41
|

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

|
 Этимологию и топонимику DMA в студию ... ( дубль 2 ) Внутренней аппаратной компоненте микроконтроллера под названием "DMA" глубоко индифферентны желания иных участников процесса .. Увы ... по определению ...
--------------------
"... Ищущий вечно, однажды найдя, то, что искал бесконечно, мимо прошёл, совершенно беспечно, с кем-то о вечном шутя ..."
|
|
|
|
|
Aug 26 2015, 16:51
|
Группа: Участник
Сообщений: 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
|
|
|
|
|
Aug 26 2015, 17:11
|
Группа: Участник
Сообщений: 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)
|
|
|
|
|
Aug 26 2015, 17:39
|
Группа: Участник
Сообщений: 13
Регистрация: 22-01-11
Пользователь №: 62 402

|
Цитата(SSerge @ Aug 26 2015, 20:33)  А зачем копировать из одного буфера в другой? Натравливайте DMA сразу на буфер фифо. Я так и хочу! Но не понимаю как это настроить. Либо ДМА начинает все с начала не обращая внимания на мои HAL_UART_DMAPause и __HAL_DMA_SET_COUNTER Либо выходит за пределы памяти, либо по кругу просто все выдает. Я просто думал есть какая то последовательность мне неизвестная.
|
|
|
|
|
Aug 26 2015, 19:48
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
Цитата - записал на скрижали ... sm.gif копирайт не забудьте.... Цитата Ну, в общем, один и тот же: ну тогда это не совсем то что вы на самом деле хотите менять... Я думаю стоит почитать про ДМА первоисточник, и забить на эти библиотеки. По мануалу там как-то все не сложно.
|
|
|
|
|
Aug 27 2015, 07:30
|
Профессионал
    
Группа: Свой
Сообщений: 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.
|
|
|
|
|
Aug 27 2015, 09:02
|
Гуру
     
Группа: Свой
Сообщений: 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 (разрешаю прерывание по завершению передачи). И по завершению передачи (переключение на приём).
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|