Пролог

В этом тексте вы узнаете, что общего между I2S трансивером и оладьями. Да... Именно так. Зачем программисту микроконтроллеров конвейеры и цифровые фильтры.

В этом тексте изложено как источать звук при помощи I2S DMA.

В чём проблема?

В прошлом тексте было упомянуто, что работа I2S трансивера в режиме прерываний сопряжена с относительно высокой нагрузкой на процессорное ядро. Это вызвано тем, что часто происходят вызовы обработчиков прерываний. Если мы передаём звук с частотой 48kHz, то прерывания будут происходить с частотой 96kHz.

Надо что-то сделать с этой проблемой.

Постановка задачи

Написать firmware приложение, которое будет одновременно и непрерывно записывать (R) и непрерывно транслировать (T) данные по I2S2 в микроконтроллере Artery. При этом, всё должно работать в реальном времени. Реализовать cхему Receive R-> Transmit T.

В переводе на кухонный язык, надо сделать полностью цифровое эхо. Через RAM память.

В качестве задачи со звёздочкой попробовать, ещё тут же в реальном времени слегка подшаманить принятые данные перед отправкой назад в I2S. Например, пропустить через какой-н цифровой фильтр. То есть реализовать схему Receive(R)->Proc(P)->Transmit(T)

Решение

Итак, танцуем от печки... Классическим решением проблемы является трансляция и запись звука через прямой доступ к памяти, direct memory access (DMA). DMA-это такой сопроцессор, который умеет только перемещать данные внутри физической памяти микропроцессора. По сути DMA - это аппаратная реализация функции memcpy(). DMA хорош тем, что при окончании отправки он генерирует прерывание DONE. Также DMA умеет генерировать прерывание об успешном перемещении половины от запланированного объёма данных. Вот так.

Передача данных от RAM в SPI
Передача данных от RAM в SPI

У DMA есть 3 режима.

Src

Dist

Пояснение

1

RAM

PHY

Трансляция в периферию

2

PHY

RAM

Запись из периферии

3

RAM

RAM

memcpy()

4

PHY

PHY

такой режим отсутствует

Очевидно, что мы не может одновременно и записывать и воспроизводить одни и те же PCM данные. Надо их записывать по кусочкам и воспроизводить тоже по кусочкам. Например по 1024 семпла. Один семпл 2 канала. В каждом канале 2 байт (int16_t).

Наивно-интуитивно напрашивается вот такой способ решить проблему цифрового эхо

Однако это очень плохая идея, так как тут нарушается непрерывность записи звука и нарушается непрерывность воспроизведения звука. Такой способ будет работать только в случае, когда размер записываемой звуковой дорожки составляет всего лишь один семпл! Однако так не решить проблему слишком частых прерываний.

Как же быть?

Чтобы решить эту инженерную проблему надо вспомнить старинную русскую народную логическую задачку.

Как пожарить 3 оладьи за 3 минуты, если каждая сторона жарится по 1 мин., а на сковородку помещается только 2 оладьи ?

А решение у задачи такое. T-top layer, B-bottom layer.

Это, своего рода, самый первый жизненный опыт применения конвейерной обработки на бытовом уровне. Аналогичный принцип надо применить в обработке цифрового звука I2S. Только тут вместо оладьей будут массивы слов по 514 байт. Вместо сковородки - I2S трансивер. Только и всего...

Вот и получается, что надо поделить временную память на две одинаковые части Low и Hi. Пока записывается L транслировать H, пока записывается H транслировать L.

Учитывая, что в DMA есть циркулярный режим, то такая обработка будет работать сама по себе бесконечно долго. Её надо лишь завести. Как завести? Если говорить на рабоче- крестьянском языке, то I2S-DMA надо завести "с толкача". Как это выглядит?

Придется в коде прошивки реализовать вот такой простенькие конечный автомат на 4 состояния. У автомата задача, не много не мало, а обеспечить правильный старт и последующую само синхронизацию между передачей и отправкой семплов так, чтобы запись отставала по фазе от воспроизведения на полпериода. Это необходимо для реализации полностью цифрового loopback. Вот граф этого конечного автомата.

Можно выделать массив семплов I2STempData[1024], обнулить его и отправить по DMA в I2S. Как только сработает прерывание по половине отправки включить запись по DMA в этот же массив I2STempData. Далее всё будет происходить на аппаратном уровне само по себе. И цифровое эхо заработает.

Получается, что прерывания происходят каждые 512 семплов. То есть с частотой 93.75 Hz. Каждые 10.6 ms прерывание по DMA. Это в 512 раз лучше чем без DMA. Причем нагрузку можно даже уменьшить ещё, если увеличить размер временного массива с семплами I2STempData.

Тут всё в полном порядке: запись непрерывная, воспроизведение тоже непрерывное. Один лишь минус это запаздывание выходного сигнала на половину длинны временного массива. Насколько велико это запаздывание? Допустим мы работаем на частоте 48kHz. Длительность одного семпла составляет 20.8333us. Сколько времени надо для воспроизведения 512 семплов? Ответ: всего 10.6ms. В принципе всё, что меньше 15ms человеку не заметно.

А как быть, если мы хотим перед отправкой ещё немного подшаманить данные? Тогда появится новая фаза Proc (P). Получится вот такой трёхтактный конвейер.

Однако трёх-тактным конвейером не получится дирижировать, так как обычно DMA подсистемы на ARM Cortex-MCU микроконтроллерах не генерируют прерывания по 1/3 и 2/3 отправки. Да.. Вот так... У нас в распоряжении есть только одно промежуточное прерывание по 1/2 отправки. К этому приходятся приспосабливаться.

Поэтому придется сделать прозябающий конвейер, который 25% времени ничего не делает. Вот так, четырехтактный конвейер.

Это паллиaтивное решение, чисто из-за совместимости с Artery MCU (у STM та же самая ситуация). Теперь мы уже оперируем четвертинками временного массива с семплами. Однако кто отдаст приказ начать обрабатывать четвертинки?

Для этого, как ни крути, но придется запускать аппаратный таймер №8, который генерирует прерывания каждые 21.3ms. Это то самое время, которое уходит на трансляцию временного массива из 1024 семплов на частоте дискретизации 48kHz. Получается, что таймер будет переполняться с частотой 46.875 Hz. А прерывания будут происходить с частотой 93.75 Hz.

Также надо настроить на аппаратном таймере прерывание по сравнению с компаратором на половине периода. Впрямь как в DMA.

На нижнем графике All_Interrupts видно, что прерывания теперь происходят каждые 256 семплов, через каждые 5.3(3)ms. То есть с частотой 187.5 Hz. Это в 256 раз меньшая нагрузка на процессор, в сравнении с тем, когда мы источали I2S трафик по прерываниям вообще без использования DMA. Успех!

Однако кто даст отмашку аппаратному таймеру начать считать? Тут чисто по комбинаторике, как ни крути, существует всего-навсего 4 варианта

№ DMA

Data direction

Event

Action

1

Tx

Half

Timer Counter = 75%

2

Tx

Done

Timer Counter = 25%

3

Rx

Half

Timer Counter = 25%

4

Rx

Done

Timer Counter = 75%

И как видно, ответ никто. В прерываниях по DMA придется просто устанавливать значение аппаратного счетчика в 25% или 75% от периода счета. Это даже хорошо так как постоянно будет происходить синхронизация I2S и аппаратного таймера.

Так мы получим нужную шкалу для запуска процедуры обработки принятых семплов (например цифровое изменение громкости, или цифровая фильтрация). Осталось только понять кто именно даст команду на обработку каждой отдельной четвертинки принятого массива. Вот эта таблица показывает кто будет делать обработку.

С теорией определились. Теперь надо это всё реализовать на электронной плате.

Каков план?

--Подать тактирование на DMA1

--Определить канал1 DMA1 для отправки аудио потока в I2S2

--Определить канал2 DMA1 для приёма аудио потока из I2S2

--Запустить аппаратный таймер 8

--Настроить компаратор на таймере 8 и генерировать прерывание на половине периода.

--Настроить I2S2 на следующие параметры: 16bit, Master I2S, AudioFreq: 48kHz.

Практическая часть

Настало время сделать решающий эксперимент и проверить эту идею на реальном железе. В качестве прототипа я воспользуюсь учебно-треннировочной электронной платой AT-Start-F437. Перед вам двухъярусная сборка: AT-Start-F437 + WM8731.

вот оно в натуре
вот оно в натуре

Пришлось выбрать аудиокодек WM8731 так как это единственный аудиокодек для которого продается отладочные платы. Вот так соединены модуль аудио кодека WM8731 и учебно-треннировочную электронную плату AT-START-F473

Wire

GPIO

PIN MUX

Pull

MCU dir

WM8731 board

I2S2_CK

PС7

5

Down

out

BCLK

I2S2_WS

PB9

5

Up

out

DACLR, ADLRC

I2S2_SD

PC3

4

Up

out

DADAT

I2S2_SDEXT

PC2

6

Up

in

ADDAT

I2S2_MCK

PA3

5

Air

out

--

I2C2_SDA

PF0

4

Up

in_out

SDA

I2C2_SCL

PF1

4

Up

out

SCL

схема подключения модуля с аудиокодеком WM8731.

Учебно-тренировочный комплект для записи и воспроизведения цифрового звука
Учебно-тренировочный комплект для записи и воспроизведения цифрового звука

На уровне SoC(а) AT32F437 надо активировать трансиверы SPI2 на передачу, а I2S2EXT на приём. Плюс включить TMR8

Тут надо пояснить, что в Artery, чтобы I2S заработал в полном дуплексе надо как бы составить его из двух отдельных одновременно работающих синхронно I2S трансиверов.

#

Аппаратный модуль

Направление

шина

режим

Boundary address

1

SPI2

Передатчик

APB1

MASTER_TX

0x4000 3800

2

I2S2EXT

Приёмник

APB2

SLAVE_RX

0x4001 7800

Стоит заметить, что сброс регистров в SPI2 одновременно сбрасывает регистры в I2S2EXT. Вот такой привет со стороны Artery.

Надо назначить каналам DMA функции I2S2. Вот так.

DMA

Channel

Function

Function

Direction

Data Width

Direction

1

1

13

SPI2_TX

out

HALF_WORD

MEM->PERIPH

1

2

110

I2S2_EXT_RX

in

HALF_WORD

PERIPH->MEM

Обработка принятых семплов

Как мы помним, при частоте дискретизации в 48kHz на обработку одного семпла у нас в распоряжении есть всего лишь 20.83 us. Это значит, что в конвейере на обработку непрерывной порции из 256 семплов у нас будет максимум 5.3ms. На всё. Поэтому необходимо чтобы Си функция в прошивке успела сделать свою DSP обработку за менее чем 5.3ms.

В качестве примера простой аудио обработки я попробовал реализовать искусственный эхо эффект на основе цифрового БИХ фильтра. Вот его цифровая цепь.

Все вычисления были для 16-битных семплов. На ARM Cortex-M4F при частоте ядра 250MHz эти 256 16-бит семплов обсчитываются IIR фильтром всего за 1.613 ms. Получается один семпл обрабатывается 6.3 us. Это в 3.3 раза быстрее, чем позволяет deadline. Успех.

Как отлаживать I2S?

Любая разработка начинается только тогда, когда появляются средства отладки. Особенно в I2S так как это hi load интерфейс. Тут мегагерцы в битовой скорости. Как за всем уследить? Как всё проверить? Ответ прост. Надо отлаживать аудио тракт по частям, подобно тому как в математике вычисляют интегралы по частям.

Фаза1: Проверка I2S на стороне MCU

I2S в режиме полного дуплекса можно проверить весьма остроумно. Можно установить перемычку с выхода на вход и включить передачу I2S данных. Это называется loopback Ожидается, что после остановки запишется тот же I2S поток, что и был воспроизведён. Бит в бит.

Таким образом можно будет проверить корректность настройки I2S трансивера на стороне микроконтроллера.

Фаза2: Проверка I2S на стороне аудио кодека WM8731

Аналогично можно проверить сам Audio Codec. Надо взять джампер или перемычку и соединить пины ADC-DATA и DAC_DATA. Таким образом получится полностью цифровое I2S - эхо. Бит в бит. Тот же самый loopback.

Затем можно попробовать сказать что-н в микрофон и одновременно слушать наушники.

++Если конфиг в кодеке исправен, то вы услышите то, что сказали в реальном времени. Без задержки.

---Если звука нет или появилась какая-то трескотня, то ищите ошибку в конфиге I2C ячеек внутри ASIC аудио кодека.

Вот так. Всего одним проводком вполне реально найти ошибку в конфигах, либо в MCU, либо в кодеке.

Когда заработает I2S Full Duplex режим на осциллографе должна быть вот такая картинка.

опечатка: не I2C2_SCL, а I2S2_CLK
опечатка: не I2C2_SCL, а I2S2_CLK

Фаза 3 Проверка синхронизации

Также можно использовать GPIO для контроля и отладки синхронизации между DMA каналами приёма и отправки. Надо лишь в обработчиках DMA прерываний выполнять вот такие действия:

DMA

Канал

Прерывание

Действие GPIO

GPIO

пояснение

1

1

Half

Установить 3.3V

PB6

Отправка половины

1

2

Half

Установить 3.3V

PB7

Приём половины

1

1

Done

Установить 0V

PB6

Отправка завершена

1

2

Done

Установить 0V

PB7

Приём завершен

Тогда, если всё хорошо, то должна получится вот такая осциллограмма

Как раз DMA отправка и DMA прием смещены друг относительно друга на пол периода. Так и должно быть.

Теперь можно спокойно запустить Timer8 c каналом сравнения №1 для выработки сигналов для запуска обработки принятых звуковых семплов. Тут стоит отметить, что в микроконтроллере Artery каналы сравнения есть не у всех таймеров. У таймера 6 их нет вообще. И у всех таймеров разное количество каналов сравнения. Прерывание по середине счета я сделал на основе аппаратного компаратора, которые есть в таймерах и эти компараторы тоже могут генерировать прерывания.

Как видно, удалось на практике разбить приём I2S 1024 семплов на 4 равных по времени интервала и вырабатывать триггеры на каждую четвертинку. Теперь остается только добавить в обработчики прерываний таймера 8 и DMA каналов код обработки семплов и получится обработка звука в реальном времени.

Достоинства I2S в режиме DMA

++Низкая нагрузка на центральный процессор микроконтроллера. Максимум 2 прерывания на всю передачу данных. Отправка и приём происходят полностью аппаратно. Это открывает дорогу для обработки I2S трафика в реальном времени прямо на MCU.

Недостатки I2S в режиме DMA

--В DMA нет прерываний по 1/3 и 2/3 от переданных данных. Как по мне, дак это печально. Прерывания на 33% 66% работы позволили бы выполнить оптимизацию производительности конвейера типа Read-Proc-Transmitt (R-P-T).

Идеи проектов

1--Если удастся написать FFT, которое обсчитывает 256 штук int16_t семплов за 5.3ms, то получится спектро анализатор на MCU.

2--Можно сделать прибор для искажения голоса в реальном времени. Чтобы никто не идентифицировал человека по голосу в телефоне.

3--Можно сделать звуковые эффекты для электрогитары. Очень просто наложить дисторшн, хорус, овердрайв, тремоло и прочие звуковые эффекты.

4--Можно незаметно подмешивать в аудиопоток бинарные данные. Например, TimeStamp(ы) записи. Чтобы было видно, что аудиодорожку потом перекроили в Audacity.

Итоги

Как видите, запуск I2S в режиме DMA это та ещё морока. Это hi-load тема. Надо производить всё предельно аккуратно. Нужно отладить и запустить целый конвейер. Надо разбираться в конечных автоматах. Надо знать как отлаживаться.

Зато DMA это единственно верный способ работы с такими интерфейсами как I2S, UART, SPI и прочее.

Этот текст пример того, что в программировании микроконтроллеров грамотная документация важнее, чем исходный код. По хорошей документации кто угодно напишет код. А имея только код восстановить документацию порой просто нереально.

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

Мне вот удалось реализовать цифровой эхо эффект на основе IIR фильтра, который работает полностью в реальном времени. Прямо на микроконтроллере.

Надеюсь, что текст поможет кому-нибудь тоже разобраться в I2S-DMA и написать своё интересное звуковое приложение прямо на микроконтроллере.

Словарь

Акронима

Расшифровка

DMA

direct memory access

БИХ

бесконечная импульсная характеристика

IIR

Infinite impulse response

I2S

Inter-Integrated Circuit Sound

R-P-T

Read-Рrocess-Transmitt

R-T

Read-Transmitt

GPIO

General-purpose input/output

Ссылки

Комментарии (16)


  1. ZavDimka
    18.08.2024 00:25
    +1

    Хорошая статья, но мне одному никогда в голову не приходило работать с I2S без ДМА и необходимость прерывания по 1\3 заполнения?


    1. aabzel Автор
      18.08.2024 00:25

      Благодарю Вас, Дмитрий, за оценку.

      В принципе ,прерывание на 1/3 DMA можно и на аппаратном таймере сделать. Битовую скорость I2S и частоту семплов знаем. Значит есть всё, что нужно для вычислений периода и компаратора аппаратного таймера.


      1. ZavDimka
        18.08.2024 00:25
        +1

        Главный вопрос, зачем? Уменьшить задержку - уменьшаете буфер. Принцип симметрии - если вам хватает 2\3 буфера, там уменьшаете его, пока 1\3 не превратится в 1\2.


  1. iShrimp
    18.08.2024 00:25
    +1

    Как всегда, очень интересно. Есть 2 вопроса:

    1. Как организовать более длинную задержку эха, не увеличивая задержку основного сигнала? Понятно, что нужно увеличить общий размер буфера, но придётся подстраиваться под скорость DMA (а она в этих МК фиксированная или может плавать?)

    2. Удавалось ли вам запустить более сложную обработку сигнала с прямым и обратным FFT? При какой длине буфера это реально?


    1. Yuri0128
      18.08.2024 00:25
      +1

      более длинную задержку эха, не увеличивая задержку основного сигнала

      Ну так сделайте бОльший буфер и запуск передачи не с 5 мс а с 50 мс (то есть на нужную задержку между стартом приема и стартом передачи). Буфер кольцевой - будет все автоматом.

      По 2 вопросу - я не автор, но честно говоря, я не понял суть вопроса. Если вам надо запустить быстрое Фурье, - так при чем там буфер? Из него данные берите просто, чем больше данных, - тем меньше сетка.


      1. iShrimp
        18.08.2024 00:25
        +1

        Ну так сделайте бОльший буфер и запуск передачи не с 5 мс а с 50 мс

        Не всё так просто. Для реалтайм-эффектов важна и низкая задержка (по оценке автора, не более 15 мс), и большой доступный буфер данных. Эти два требования уже ставят нас в жесткие временные рамки. Например, если мы хотим создать эффект "эхо", мы должны смешать исходный сигнал (с минимальной задержкой) с задержанной на 50-300 мс копией. Но бывают и гораздо более сложные звуковые эффекты.

        Если вам надо запустить быстрое Фурье, - так при чем там буфер

        Здесь тоже всё непросто, нужен компромисс между качеством, длительностью обработки и вычислительной сложностью БПФ, которая пропорциональна n * log(n).


        1. Yuri0128
          18.08.2024 00:25

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

          Я б ставил на 10-12 мс. Но при чем здесь длина самого буфера? Берем и стартуем передачу из входного буфера через 50 мс - всегда будет задержка равно 50 мс при равных частотах i2s. А буфер может быть и на 200 мс, просто голова приема и голова передачи будут на 50 мс друг от друга. А буфер - кольцевой.

          Здесь тоже всё непросто

          Согласен, ноставилась задача в 256 сэмплов int16 - вполне можо посчитать. Я тама ниже писал. Предел при хорошем ДСП думаю что 5-10 ксэмплов (это где-то 28-30 разрядов получим), дальше - FPGA (тут тоже - до 15 к норм, а вот дальше жопа).

          По миксам и эхо - ну имеем примерно 20 мкс на обработку сэмпла int16 (для микса) - ну проц будет довольно сильно занят, но справится. А если есть ДСП - так ваще лафа. Вот посчитать БФП не настолько просто. Тама иже писалось "синтезировать FIR или IIR" - ну для старта его можна.


    1. aabzel Автор
      18.08.2024 00:25

      Как всегда, очень интересно.

      Спасибо, рад что пригодилось.

      1. Удавалось ли вам запустить более сложную обработку сигнала с прямым и обратным FFT? При какой длине буфера это реально?

      Фурье пока в real time не считал. Надо попробовать.

      Да и потом. Я тут обсчитываю с 256 семплов за раз. Это сигнал 5.3ms. Получается, что минимальную частоту, которую можно будет обнаружить это 187.5 Hz потом 375.0 Hz ;562.5 Hz ; 750.0 Hz ; 937.5 Hz ; ...

      Если удастся написать FFT, которое обсчитывает 256 штук int16_t семплов за 5.3ms, то получится спектро анализатор на MCU.

      Лучше попробовать цифровой фильтр синтезировать FIR или IIR. Там будет меньше вычислений.


      1. Yuri0128
        18.08.2024 00:25
        +2

        Если удастся написать FFT, которое обсчитывает 256 штук int16_t семплов за 5.3ms

        На ARM- с хорошим клоком или с ДСП можно легко посчитать БПФ, вот ДПФ - тут уже вопрос посеръезнее....


      1. AlanDrakes
        18.08.2024 00:25

        https://gist.github.com/Tomwi/3842231

        Вот эта библиотека работает с целочисленными значениями для построения FFT спектра. На STM32F030, запущенного на 20МГц (пришлось выбирать рабочую частоту так, чтобы можно было управлять через SPI "умными" светодиодами (WS2812 в микро корпусе). Буфер на 512 сэмплов обрабатывает быстро (хотя и не замерял), но его заполнение, обработка, поиск нужной частоты (среднее + сравнение его с пятью "нужными" сэмплами) довольно неоптимальным кодом... спокойно укладывается в 50мс. Да, всё крутится вокруг DMA при этом.

        Настроили АЦП, прикрутили к нему DMA.
        Настроили SPI на отправку буфера для светодиодов, чуть-чуть заполнили его, запустили DMA, пусть он запрашивает следующие 48(24) байт.
        Начали читать данные через I2C? Ну... а на этом чипе так не получилось. Ну ой.


  1. rukhi7
    18.08.2024 00:25
    +1

    Удивительно что есть ещё люди которые способны изложить на таком приличном уровне формулирлвку чисто практической задачи, и её решения , и особенностей аппаратной платформы которые необходимо использовать. Даже не верится что подобные статьи возможны на Хабре.

    Если бы с такой работой можно было ещё и кандидатскую диссертацию защитить на родине, наша наука смогла бы быстро придти в тонус и перейти к более серьёзным проблемам, а пока надо освоить фундаментальную базу, которой пока нет.


    1. aabzel Автор
      18.08.2024 00:25

      Удивительно что есть ещё люди которые способны изложить на таком приличном уровне формулировку чисто практической  задачи

      Благодарю Вас, Сергей, за оценку.


  1. rukhi7
    18.08.2024 00:25
    +1

    В моих процессорах в ДМА есть спец режим когда конфигурируются буферы связанные можно даже один задать чтобы он сам на себя переключался хотя в этом смысла вроде нет. Но можно и 2 и 3 то есть N.

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

    К сожалению к этому классу задач научный подход применять у нас не принято - исторически не сложилось.


    1. aabzel Автор
      18.08.2024 00:25

      В моих процессорах в ДМА есть спец режим когда конфигурируются буферы связанные можно даже один задать чтобы он сам на себя переключался хотя в этом смысла вроде нет. Но можно и 2 и 3 то есть N.

      Это называется scatter/gather DMA . И в Artery AT32F437xx он тоже есть в составе MII трансивера.


      1. rukhi7
        18.08.2024 00:25

        С этой схемой у меня остался не выясненный вопрос о работе на аппаратном уровне. ДМА должен загрузить структуру дескриптора в свои регистры при переключении на следующий буфер. Как на это времени хватает или пропускной способности шины данных при повышенной частоте пересылки байтов(например) по ДМА не очень понятно. Непонятно даже как это исследовать.

        Вроде как только на аппаратном уровне можно разбираться.


    1. aabzel Автор
      18.08.2024 00:25

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

      Я не совсем понял постановку задачи. Разве в работе двух dma есть какая- то высшая математика?