
Комментарии 69
Использование FIFO для передачи данных из прерывания основной программе излишне, имхо. Если всё пишется аппаратно через DMA, то проще передавать указатель на начало/середину буфера с половиной длины. А в вашем случае вообще можно разгрузить процессор, напрямую передавая с АЦП в буфер одним ДМА-потоком, а из этого же буфера в ЦАП - другим.И Circular mode - это точно не режим цирка)
Пришел по DMA первый полу-буфер - установили флажок, в основном цикле проверили флажок и обработали его. Пришёл второй - установили второй флажок и обработали его. Потом снова первый... А можно прямо флажки DMA использовать - и прерывания не нужны. Понятно, что нужно успеть обработать до появления следующего полу-буфера. А если не успеваем - то это уже проблема. При редких неуспеваниях поможет дополнительная буферизация, при постоянных - изменение оптимизации, алгоритмов, МК, программистов итп...
Я собственно так и делал когда писал управляемый генератор синуса, суммы синусов и АМ. Один флаг на то с какой половиной буфера буфера работаем, вся математика во freertos задаче. Пока dma передаёт одну половину буфера - формируем другую, по прерываю выставляем флаг и будим задачу. В stm32 кстати для этого есть режим double buffer и по регистру CT можно проверить на каком мы буфере. У китайцев (artery at32) тоже есть похожая тема, там есть отдельный контроллер edma.
вообще то обычно в ЦОС сэмплы не получится обрабатывать пачкой. Например в задачах ПИД регулировки по приходу каждого семпла надо расчитать сигнал ошибки и отправить его в физический интерфейс управления, в других случаях, вообще, например ФФТ считается после прихода каждого нового сэмпла по значениям окна сигнала с этим добавленным сэмплом. Поэтому наличие накопления в вашей учебной программе - это уход в сторону от любой реальной задачи ЦОС.
На самом деле для ЦОС вместо такой функции:
bool adc_proc_one(uint8_t num) {
должна быть какая-то такая:
bool adc_proc_sample() {
или
void adc_proc_sample(ptr_t window_start) {
В вашей программе вот этот код
cli_printf("\rCode:%4u,%5.3f V",
sample,
AdcSample12ToVoltageVef3_3(sample));это и есть тело этой функции adc_proc_sample
Так ДМА оно же просто складывает данные из регистра в память, оно никуда данные не может передать! Для полноценной передачи эти данные еще куда то скопировать нужно! У вас все равно должен быть какой то код копирования еще! И если у вас только четыре или даже 16 тактов на копирование одного семпла (шорта например) возникает вопрос, а достаточно ли этого количества тактов для этого копирования! Мне кажется дело тут не в ДМА, все таки!
У меня была работающая программа которая принимала 2.5 Мега байт данных через ДМА не важно с какого интерфейса и передавала их в МАК контроллер для передачи по Ethernet-у, формировала данные для TCP пакетов через LWIP, но это было при частоте проца 300 МГц. Вот это была полноценная передача данных из одного аппаратного интерфейса в другой, в Ethernet, там в Ethernet-контроллере еще один ДМА на самом деле, встроенный есть, который готовый TCP пакет отправляет, но между интерфейсами аппаратными все равно код нужен который упаковку данных преобразует.
Очень поучительная статья и очень поучительная, можно сказать базовая фундаментальная задача программирования Цифровой Обработки Сигналов (ЦОС) в частности и эмбеддед программирования вообще. Только в реальном мире (а не в тепличных учебных условиях) дополнительная ФИФО очередь будет только мешать, и скорее всего все сломает всю работу алгоритма, так как она работает только при наличии очень большой памяти, которой в эмббедед мире никогда нет (не хватает). Потом вызывать функцию для копирования каждого сэмпла, да еще не одну - это просто убийство производительности, которая очень ограничена например в любом STM процессоре.
Как по мне это совсем не так:
Реализация FIFO это отдельная большая задача.
Это нисколько не сложнее реализации циркулярного буфера (с использованием ДМА или без использования). Обычно в этом месте требуется реализация циркулярного буфера который используется как ФИФО с контролем переполнения этого буфера.
Ну и непонятно почему одну функцию копирования половины буфера надо скопировать два раза как:
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
и как:
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc) {
Вообще говоря не хорошо оставлять копирование целого буфера, да еще и с многократным использованием каких-то сторонних (библиотечных?) функций непонятной производительности внутри прерывания. Это прям нарушение базовых принципов работы с прерываниями.
Хорошей задачей для студентов будет задача исправить эти ошибки. Я даже думаю лучший вариант со всеми исправленными ошибками и корректными методами измерения эффекта увеличения производительности при этом, вполне потянул бы на кандидатскую диссертацию, а может и на докторскую. Если возьметесь буду рад оказать вашей группе любое техническое (экспертное на базе своего опыта)... любое содействие в этом направлении.
Вторая fifo (для dac) нужна только для отладки факта корректной записи adc модулем. Из приложения я ее конечно же исключу.
TxFifo выступает в роли строительных лесов.
С каких пор код написанный под работу напрямую с регистрами, без использования библиотек абстракции тянет на что-то выше курсовой работы? Это шутка надеюсь. И смысл бить с пушки по воробьям на кГц выборках сигнала? Производительности HAL для этого вам с запасом хватит даже на сериях общего применения (F103/303 и им подобные).
ну вообще-то что-то вроде библиотеки абстракции и нужно написать в этом случае, универсальное решение которое зависит не от конкретных регистров, а от базовых принципов работы, которые эти регистры (любая реализация с регистрами) всегда реализуют. А еще, вы наверно пропустили, нужно сформулировать и показать что работают эффективно методы измерения производительности, чтобы показать что такая абстракция позволяет обеспечить лучшую производительность по сравнению с любым другим способом организации тех же вычислений.
буду рад оказать вашей группе любое техническое (экспертное на базе своего опыта)... любое содействие в этом направлении.
Самая первая проблема с которой я столкнулся оказалась такая:
ADC отсчёты всегда положительные 0.....4095.
Как на лету отсеивать постоянную составляющую сигнала для последующей ЦОС обработки?
Причем так, чтобы не сильно перегружать микропроцессор.
Y -= 2048?
ну или
Z+=X-(Z>>N);
Y-=Z>>N;

так а в чем у вас проблема-то в вашем коде, если вы позволяете себе целую функцию вызвать на каждый сэмпл просто для копирования как:
i_status ret = iqueue_enqueue(&Node->iQueue, &Node->RxSamples[i]);в чем проблема вычесть константу из этого значения:
unsigned short val = Node->RxSamples[i] - averConst;
i_status ret = iqueue_enqueue(&Node->iQueue, &val);это вычитание будет совершенно не заметно на фоне вызова функции iqueue_enqueue(), вы представляете сколько тактов занимает вызов функции, а сколько операция вычитания?
Но если вы хотите динамически следить за значением среднего, вам придется написать функцию которая как раз и будет обрабатывать КАЖДЫЙ сэмпл, тогда будет что-то вроде:
unsigned short val = Node->RxSamples[i];
averConst = adjustAver(val);
val -= averConst;
i_status ret = iqueue_enqueue(&Node->iQueue, &val);
И все сводится к тому КОГДА у вас должна вызываться основная функция ЦОС, для каждого сэмпла или для пачки накопленных сэмплов <+> и что от этого зависит после приема каждого сэмпла (или после скольки сэмплов?). Именно ответ на этот вопрос определяет сколько у вас есть реального времени для выполнения функции усреднения+функция ЦОС (если есть смысл разделить их как разные функции, обычно нет такого смысла, кстати, усреднение это часть алгоритма ЦОС, если не основная цель, обычно), при заданной частоте оцифровки - поступления сэмплов.
"Хорошей задачей для студентов будет задача исправить эти ошибки."
Студентам это лучше не показывать, а то они сразу в академики пойдут.
а то они сразу в академики пойдут.
Да. Автор теоремы Котельникова - Владимир Александрович как раз был академиком.
Сторонняя "умная" реализация fifo здесь действительно не нужна - звуковой семпл это не какая-то сложная структура, добавляются семплы с известной периодичностью, как и читаются. Фифо у вас уже есть, и декью при этом - просто чтение данных по указателю на начало или половину буфера + n семплов назад. Реально сдвигать очередь не нужно - запись всегда идёт в другую половину.
Как только вы попробуете крутить хоть сколько-нибудь сложное DSP, это станет очевидно по быстродействию.
Да. Можно добавить второй DMA канал на отправку в DAC прямо из того же массива куда пишет DMA от ADC.
DDS-ом один раз сгенерировать массив предварительно рассчитанного 1го периода синуса в RAM память. (Чем меньше частота синуса, тем длиннее массив).
И уже третьим каналом DMA из предварительно рассчитанного одного периода синтезированного синуса в RAM памяти отправлять семплы в DAC.
Тогда для CPU вообще не остается никакой работы.
DDS это точно НЕ вычисление синуса (значений гармонической функции), это чтение значений из таблицы этой гармонической функции записанной с высоким разрешением по времени. Вы маленько путаете людей, но если для популяризации абревиатуры и чтобы примерно обозначить о чем эта абривиатура, наверно сойдет.
Если читать из таблицы то ДМА будет очень в тему в этом случае, кстати!
Если читать из таблицы то ДМА будет очень в тему в этом случае, кстати!
Да. Я так и делал, когда тестировал аудиокодеки.
Обзор Aппаратного Aудио кодека MAX9860 (2x ADC+DAC)
https://habr.com/ru/articles/758140/
"
Как отладить I2S сейчас? Можно воспроизвести статический синус. Именно статический чтобы исключить ошибки в динамическом выделении памяти. Если выбрать частоту, например, 1kHz, то получится, что надо рассчитать всего 48 PCM отсчетов, чтобы синтезировать гармонический сигнал. В прошивке достаточно зарезервировать 48*2*2=192 байт.
Прямо в прошивке расчитать 1 период для синуса 1kHz, амплитудой 3333 PCM, частоты дискретизации 48kHz и проигрывать его в цикле по DMA.

На шине I2S должен появится такой цифровой сигнал. На проводе BCLK 1.724 MHz, LRCLK 53.591 kHz, OUTN 1.118 kHz
....
"
(del)
Использование FIFO для передачи данных из прерывания основной программе излишне, имхо. Если всё пишется аппаратно через DMA, то проще передавать указатель на начало/середину буфера с половиной длины. А в вашем случае вообще можно разгрузить процессор, напрямую передавая с АЦП в буфер одним ДМА-потоком, а из этого же буфера в ЦАП - другим.И Circular mode - это точно не режим цирка)
Использование FIFO для передачи данных из прерывания основной программе излишне, имхо. Если всё пишется аппаратно через DMA, то проще передавать указатель на начало/середину буфера с половиной длины.
А как быть, если примём семплов и обработка семплов происходят с разной скоростью? Причем скорость обработки семплов - величина переменная.
FIFO для того и созданы, чтобы сбалансировать нагрузку на обрабатывающий элемент.
Не делать же DSP обработку семплов внутри прерываний по DMA.
Обрабатывать выборки блоками, очевидно...
Как вы без FIFO обеспечите гарантию, что внутри функции main() семплы будут обрабатываться в хронологическом порядке?
Пришел по DMA первый полу-буфер - установили флажок, в основном цикле проверили флажок и обработали его. Пришёл второй - установили второй флажок и обработали его. Потом снова первый... А можно прямо флажки DMA использовать - и прерывания не нужны. Понятно, что нужно успеть обработать до появления следующего полу-буфера. А если не успеваем - то это уже проблема. При редких неуспеваниях поможет дополнительная буферизация, при постоянных - изменение оптимизации, алгоритмов, МК, программистов итп...
Можно попробовать. Но мне кажется, что приложение будет так занято, что будет пропускать сигналы от DMA между итерациями.
Если много длительных фоновых процессов, то можно использовать операционку. А обработчику поставить приоритет повыше, и запускать из прерывания по семафору.
Я собственно так и делал когда писал управляемый генератор синуса, суммы синусов и АМ. Один флаг на то с какой половиной буфера буфера работаем, вся математика во freertos задаче. Пока dma передаёт одну половину буфера - формируем другую, по прерываю выставляем флаг и будим задачу. В stm32 кстати для этого есть режим double buffer и по регистру CT можно проверить на каком мы буфере. У китайцев (artery at32) тоже есть похожая тема, там есть отдельный контроллер edma.
Можно без операционки, для этого можно триггерить программное прерывание. Единственное не вспомню точно, можно ли ему поднять приоритет.
вот поэтому я и говорю что это задача для кандидатской диссертации. Но решение у нее точно есть, потому что уже существует огромное количество эмбеддед систем в которых все это уже работает без всяких FIFO.
вообще то обычно в ЦОС сэмплы не получится обрабатывать пачкой. Например в задачах ПИД регулировки по приходу каждого семпла надо расчитать сигнал ошибки и отправить его в физический интерфейс управления, в других случаях, вообще, например ФФТ считается после прихода каждого нового сэмпла по значениям окна сигнала с этим добавленным сэмплом. Поэтому наличие накопления в вашей учебной программе - это уход в сторону от любой реальной задачи ЦОС.
На самом деле для ЦОС вместо такой функции:
bool adc_proc_one(uint8_t num) {
должна быть какая-то такая:
bool adc_proc_sample() {
или
void adc_proc_sample(ptr_t window_start) {
В вашей программе вот этот код
cli_printf("\rCode:%4u,%5.3f V",
sample,
AdcSample12ToVoltageVef3_3(sample));это и есть тело этой функции adc_proc_sample
Поэтому наличие накопления в вашей учебной программе - это уход в сторону от любой реальной задачи ЦОС.
Есть достаточное множество задач ЦОС где задержка обработки никак не влияет но при этом требуется обработка в реальном времени. Например, обработка звука и/или изображения. Тот же SDR, где, кстати, так же присутствует БПФ, причём, сразу во множественном числе каскадно.
Как раз для СДР, БПФ (оно же ФФТ, или просто преобразование Фурье) по определению применяется после прихода каждого сампла, причем самплов там надо два параллельно (строго одновременно) получить чтобы комплексное число сформировать для использования в этом БПФ.
Поэтому у нас это на ПЛИС-ах обычно делают, а вот буржуи умеют на процессорах, и специальный тип процессоров для этого придумали который обзывают DSP.
SDR один из примеров. Основной посыл был в том, что не все алгоритмы ЦОС поддаются конвееризации и/или требуют конвееризацию. И даже то, что
буржуи умеют на процессорах
как раз и говорит о том, что они накапливают окно перед обработкой. Программы на ПК (вроде HDSDR или SDRSharper) тоже работают с окнами, которые не стараются обрабатывать окно с каждым пришедшим на скорости 10MSPS (мой SDR так может) сэмплом, это оверхед для программной обработки. Окна, кстати, можно настраивать в некоторых пределах, получая разную точность результата.
Это каким образом БПФ обрабатывается после прихода сэмпла? Только блочно - там куча вычислений, для которых нужны как минимум 2 выборки. Вызывать обработчик для каждой выборки зачастую медленее, чем в цикле обработать блок. Особенно если уметь оптимизировать не в стиле "красивый код", а "эффективный код". Многие DSP-функции уже в микроконтроллеры внедряют, а то и в ядро процессора (ARM, например).
Он имел в виду, что для обеспечения непрерывности результата спектра сигнала БПФ производится над окном после получения каждого нового сэмпла. При этом, само окно выступает в роли FIFO и требует своего наполнения прежде чем результат станет репрезентативным. Т.е., первые N сэмплов, где N - размер окна, нужно для наполнения окна и при этом результат БПФ отбрасывается. А затем каждый новый сэмпл вытесняет самый старый сэмпл в окне и БПФ будет отражать непрерывность спектра поступаемого сигнала.
Это тогда не fifo , а циклический буфер.
каждый новый сэмпл вытесняет самый старый сэмпл в окне и
Классический fifo при переполнении заклинивает.
Это всего лишь вопрос терминологии. Выталкиваемый сэмпл вполне может быть куда-то применён, например на ЦАП.
каждый новый сэмпл вытесняет самый старый сэмпл в окне и
только не вытесняет, а перезаписывает. Старый стирается этой перезаписью. Только тогда адрес начала буфера придется с каждым семплом сдвигать на этот сэмпл, Буфер в этом случае действительно будет циркулярным, с размером кратным степени 2-ки, по определению для БПФ. Только там должен быть еще один буфер с константами -коэфициентами для БПФ. Базовая ДСП операция это умножение+накопление на три операнда: op3+=op1*op2, как одна ассемблерная инструкция. Или точнее, это делается с адресами:
(*op3р++)+=(*op1р++) * (*op2р++)
буфер там приходится вроде сдвигать, но я уже не помню все нюансы техники вычислений.
Обычно берется блок выборок по количеству бинов, применяется оконная функция, вычисляется БПФ, берётся следующий блок итд. Не встречал, чтобы БПФ считали с каждой выборкой, это уже вэйвлеты какие-то будут.
Не встречал, чтобы БПФ считали с каждой выборкой
Это обычно в конвеерах на FPGA, потому что практически ничего не стоит. А на процессорах, конечно, не каждый сэмпл считают, обычно пачку надо принять, пока вычисляется предыдущий БПФ.
в задаче радио локации например, надо найти максимум значения сверки эталлонного сигнала с принятым, свертку нужно делать для каждого нового семпла по новой. Вспомнил! А то БПФ меня в сторону тянуло.
Хорошо, когда частота семплирования меньше, чем слышит ухо, и вообще, это единственная задача. В таком случае, правда, можно обойтись и прерываниями и даже поллингом в основном цикле :)))
У DMA есть скрытая до поры проблема. Шина памяти одна, а "пользователей", конкурирующих за шину может быть несколько. Причём пользователями являются не только явно обозначенные модули периферии, но и, например, сам CPU. Как гласит документ AN2548:
The bus matrix gives the priority to the Cortex‑M CPU versus the DMA controllers, for a CPU shorter latency.
Для ADC "гуляние" DMA latency, кстати, не так критично. Максимум, будут пропуски семплов.
А вот с DAC будет интереснее :)
P.S. Представил себе реплику mp3-плеера iRiver T10 на STM32, запущенном (для экономии батареи) от чего-нибудь типа LSI на 32кГц. Когда дисплей выключен, главный цикл крутит пустой while. Когда дисплей включён, в него из RAM идут посылки с графонием и пользователь слышит высокочастотный писк. Все накидываются на трассировщика, виня его в "грязном" питании, а он не при делах :)
Хорошо, когда частота семплирования меньше, чем слышит ухо, и вообще, это единственная задача.
При чем тут ухо? Может это семплы от радарной антенны или вообще от фотодиода.
При чем тут ухо?
При том, что ухо, прямо скажем, не ахти какой быстрый сенсор.
А здесь - "ещё медленней, чем ухо".
Чтобы DMA работал стабильнее - придумали всякие аппаратные bus matrix, burst и fifo...
Пытался однажды запустить два канала ДМА на сбор данных от двух АЦП при 5 МГц. Были потери данных. Высшие приоритеты по прерываниям ничего не меняли. Пришлось использовать два отдельных ДМА используя только один канал в каждом. Еще параллельно нужно было выводить данные на экран, и потребовался третий ДМА. Пришлось использовать H7. DMA в STM32 не очень хорошо реализованы. В H7 тактовые частоты намного выше чем в F4, но ДМА все равно медленные. Может какие-то тонкости есть, чтобы использовать потенциал по максимуму.
Пытался однажды запустить два канала ДМА на сбор данных от двух АЦП при 5 МГц. Были потери данных.
Ееее! :)
А сама частота работы МК была какая? И это... оба АЦП триггерились от одного таймера?
ADC не обязательно тригерить по таймеру. В continuous conversion режиме АЦП тригерится по завершению предыдущего АЦП, то есть по внутреннему таймеру АЦП. АЦП не может работать без своего собственного таймера. Только continuous conversion обеспечивает максимальную частоту работы АЦП, но эта частота тоже управляется, там должны быть настройки делителя опорной частоты для внутреннего таймера АЦП и выбор опорной частоты этого таймера. 5 МГц как раз выглядит как значение близкое к максимальной частоте.
Ммм. Тут бы внести ясность от @LinkToOS
Потому, что у STM32F407, например, такие временнЫе характеристики ADC:
Скрытый текст

Т.е. fADC =5МГц здесь получается весьма лайтовым вариантом работы АЦП.
А fS=5МГц(Мсемп) здесь получается как бы даже не запредельным вариантом работы.
(del)
решил все таки посмотреть пдф-ку, в режиме
12-bit resolution Interleave Triple ADC mode
действительно максимально 6 Msps
АЦП работали предсказуемо. Сложности были с DMA. Максимальная теоретическая производительность DMA при 80 МГц и 4 тактах на обмен равна 20М (процессор в состоянии HALT). Но реально 2 по 5М данных от двух АЦП, с помощью одного DMA при использовании обоих его каналов передать не получилось. С двумя отдельными DMA все работало.
Так ДМА оно же просто складывает данные из регистра в память, оно никуда данные не может передать! Для полноценной передачи эти данные еще куда то скопировать нужно! У вас все равно должен быть какой то код копирования еще! И если у вас только четыре или даже 16 тактов на копирование одного семпла (шорта например) возникает вопрос, а достаточно ли этого количества тактов для этого копирования! Мне кажется дело тут не в ДМА, все таки!
У меня была работающая программа которая принимала 2.5 Мега байт данных через ДМА не важно с какого интерфейса и передавала их в МАК контроллер для передачи по Ethernet-у, формировала данные для TCP пакетов через LWIP, но это было при частоте проца 300 МГц. Вот это была полноценная передача данных из одного аппаратного интерфейса в другой, в Ethernet, там в Ethernet-контроллере еще один ДМА на самом деле, встроенный есть, который готовый TCP пакет отправляет, но между интерфейсами аппаратными все равно код нужен который упаковку данных преобразует.
передача данных из одного аппаратного интерфейса в другой, в Ethernet
Вот бы STM добавили DMA режим peripheral to peripheral.
на сколько я помню из регистра IO в другой регистр IO (с одного адреса IO памяти по другому адресу) передавать(копировать) с помощью ДМА можно (у меня не СТМ был, но в этом нет ничего принципиально не реализуемого), только это не поможет нам напрямую из АЦП в МАК (Ethernet) контроллер данные отправлять, так как МАК контроллер требует не тривиального управления при подготовке и отправке данных. Но вот в UART например можно напрямую данные по ДМА перенаправить, только как вы в приемнике этих данных будете определять начало и конец этих данных не очень понятно, хотя предположить можно, но это все равно будет пакетная передача, у которой как минимум должна быть метка запуска или команда на запуск.
В худшем случае можно два ДМА настроить peripheral to memory + memory to peripheral, все уже есть.
DMA суть автомат шины, который может имитировать CPU по части управления этой шиной в те моменты, когда этот самый CPU переваривает очередную команду а шина при этом простаивает. Сам DMA позволяет существенно улучшить отношение пересланные_данные/такты, даже если сам CPU при этом требуется приостановить.
Т.к. у контроллеров STM есть несколько шин, то соединять устройства DMA может только сидящие на одной шине. Для понимания в букваре обычно приводится вот такая картинка:

В теории, можно наладить datapath к APBx через AHB1, но возможно это только на DMA2. DMA1 же всегда одной ногой в памяти а другой в APB1.
К стати, а STM32F407 может исполнять программу из SPI-NorFlash памяти eXecute in Place (XiP) ?
так у вас же тут GP DMA2 одной ногой в матрице, а другой ногой
И в матрице;
И в APB2. О чем это говорит я, честно говоря, уже не очень помню-знаю, на каждом типе микросхем каждый раз приходится перепроверять по новой так как у меня в наличии только регистры есть, через которые я с этим богатством работаю, а на заборе тоже много чего нарисовано, а за забором дрова лежат.
Но эти буквари еще и маленько разные для разных процов разных производителей.
ДМА оно же просто складывает данные из регистра в память, оно никуда данные не может передать
Это уже разговоры про терминологию. DMA (по русски ПДП) копирует данные из одного места в другое, в пределах адресного пространства, без помощи CPU (или ядра CPU, здесь могут быть разночтения). В этом суть DMA. “Пересылка” данных, “обмен”, “транзакция” - синонимы. “Складывает” или “накапливает” - это уже на уровне понимания процессов программистом. Передать с помощью DMA можно даже один байт. “Предавать” - не обязательно куда-то за пределы контролера.
DMA (по русски ПДП) копирует данные из одного места в другое, в пределах адресного пространства, без помощи CPU
И, к слову, в обход запретам MPU (Memory Рrotection Unit )...
https://habr.com/ru/articles/950298/
По моему если вы постоянно перезаписываете данные в памяти с помощью ДМА и эти данные никто и никогда не использует то называть это передачей данных как то странно. Да это в какой-то степени вопрос терминологии, вопрос чем отличается термин "выкинуть" от термина "передать". Если удобно считать что разницы между этими терминами нет, хорошо! Но меня просто маленько удивляет такая позиция.
если вы постоянно перезаписываете данные в памяти с помощью ДМА и эти данные никто и никогда не использует то называть это передачей данных как то странно.
Это если обсуждать логику программы. А если обсуждать работу DMA, то неважно что потом происходит с данными. Задача DMA скопировать из одного адреса в другой. Это действие называется data transfer, по документации. Хотя по существу это именно копирование, потому что это действие не изменяет данные в источнике.
Частота 80 МГц. Это был не F4, а L47x. Триггер АЦП по одному таймеру.
Пощупайте DMA у Миландровских cortex-m3... Вот там действительно "веселье". Особенно когда у части периферии ещё и свой не отключаемый FIFO есть.
Кто работал с Sound Blaster'ом под DOS тот все эти "секретики" знает.

Потоковая запись ADC семплов на STM32