Контроллер прямого доступа к памяти что это

ОБОРУДОВАНИЕ
ТЕХНОЛОГИИ
РАЗРАБОТКИ

Блог технической поддержки моих разработок

Урок 33. Прямой доступ к памяти в STM32. Контроллер DMA.

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

В уроке узнаем о способе передачи данных в режиме прямого доступа к памяти, реализации этого процесса в микроконтроллере STM32. Подробно изучим режимы работы, форматы регистров, особенности контроллера ПДП STM32.

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

В предыдущих уроках мы реализовывали подобные задачи программным способом. В цикле считывали данное из памяти или регистра и загружали его в регистр или память.

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

А ведь задача пересылки данных между памятью и периферийным устройством с аппаратной точки зрения очень простая. Достаточно аппаратного счетчика адреса памяти, счетчика количества данных и промежуточного регистра. Данное считывается, например, из АЦП и записывается в память по адресу из специального регистра-счетчика. Его содержимое увеличивается на 1, и новое данное из АЦП записывается в память по следующему адресу. Процесс продолжается пока счетчик количества данных не остановит работу устройства. При этом процессор продолжает выполнение основной программы.

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

Процесс обмена данными без участия процессора (за счет аппаратных узлов микроконтроллера)

Аппаратная часть микроконтроллера, реализующая процесс прямого доступа к памяти называется контроллер прямого доступа к памяти (контроллер DMA).

Использование ПДП (DMA) позволяет:

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

Устройство контроллеров DMA, как правило, простое, очевидное и понятное для разработчиков программ.

Общие принципы работы контроллера прямого доступа к памяти в STM32.

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

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

В общем случае прямой доступ к памяти происходит следующим образом.

Сам обмен данными (транзакция) с использованием DMA состоит из трех операций.

Все просто и логично.

Каналы DMA.

Повторюсь. В микроконтроллере STM32F103c8 есть только один контроллер DMA c 7 каналами.

С точки зрения программиста каждый канал – это отдельный аппаратный узел микроконтроллера. Он осуществляет DMAпередачу между регистром данных периферийного устройства (расположенным по фиксированному адресу) и памятью.

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

В каждый момент времени обмен данными может происходить только по одному каналу. При нескольких активных каналах контроллер DMAразрешает работу канала с более высоким приоритетом.

Приоритет каждого канала устанавливается программно и может иметь четыре уровня:

При активных каналах с одинаковым уровнем приоритета преимущество имеют каналы с меньшим номером.

Регистры каналов.

Каждый канал для нас – отдельный аппаратный контроллер передачи данных.

Каждый канал имеет 4 собственных регистров, которые полностью определяют режим обмена данными по нему.

Каналы настолько независимы, что если вести речь обо всех регистрах контроллера DMA, то к регистрам каналов добавятся только регистры управления прерываниями.

Итак 4 регистра, с очевидным назначением.

Полное описание регистров есть в справочнике.

Количество элементов данных для передачи устанавливается программно и может быть от 0 до 65535.

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

Что касается памяти, то как правило, необходимо записывать или считывать данные по последовательно расположенным адресам.

Для этого существует режим инкрементации адреса. Он доступен как для адреса памяти (бит MINC регистра DMA_CCRx), так и для адреса периферийного устройства (бит PINC регистра DMA_CCRx). Хотя в последнем случае не имеет смысла.

Таким образом, после каждого обращения к памяти указатель увеличивается на 1, 2 или 4, в зависимости от заданного размера элемента данных. Начальные адреса передачи данных хранятся в регистрах DMA_CMARx (для памяти) и DMA_CPARx (для периферии). В процессе обмена данными их значения не изменяются, а текущие значения указателей содержатся во внутренних регистрах микроконтроллера и программе недоступны.

Размер элементов данных транзакции может иметь значения:

Выбор размера данных осуществляется независимо не только для каждого канала, но и отдельно для приемника и передатчика данных каждого канала. Для этого в регистре DMA_CCRx существуют битовые поля PSIZE и MSIZE.

После выполнения последней передачи запросы по каналу перестают обслуживаться. Регистр количества данных передачи DMA_CNDTRx содержит 0. Чтобы инициировать новый DMA обмен необходимо загрузить в него новое значение. Это возможно только после отключения канала. При этом все остальные регистры канала (DMA_CMARx, DMA_CPARx и DMA_CCRx) сохраняют значения.

Если задан циклический режим (бит CIRC регистра DMA_CCRx), то по окончанию передачи данных в регистр DMA_CNDTRx автоматически загружается его первоначальное значение и процесс DMA обмена возобновляется.

Конфигурация канала DMA.

Для использования канала необходимо конфигурировать его, выполнив следующую последовательность действий. Изначально канал должен быть запрещен (бит EN регистра DMA_CCRx сброшен).

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

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

Режим “из памяти в память”.

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

Режим запрещено использовать совместно с циклическим режимом.

Выравнивание данных разной разрядности.

При DMA передаче данных разрядности приемника и источника могут не совпадать. Действует простое и логичное правило. Всегда сохраняются младшие разряды.

В принципе используется правило преобразования (приведения) типов данных языка C.

Специфика доступа к периферийным устройствам через шины AHB/APB.

Периферийные устройства STM32 подключены к процессору и памяти через шины AHB/APB. Это 32х разрядные шины. Они не поддерживают операций передачи меньшей разрядности. В результате в регистры периферийных устройств можно записывать или считывать из них только 32х разрядные слова.

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

При записи в периферийное устройство байта или полуслова через шину AHB данные на неиспользуемых линиях шины дублируются. Например.

Ошибки при DMA передаче.

Ошибка может возникнуть только в результате попытки доступа к зарезервированному адресному пространству. В этом случае канал, в котором произошла ошибка, отключается аппаратным сбросом бита EN. Устанавливается флаг ошибки TEIFx в регистре DMA_ISR. И формируется прерывание, если оно разрешено битом TEIE в регистре DMA_CCRx.

Прерывания DMA.

Для каждого канала могут генерироваться прерывания по следующим флагам регистра DMA_ISR.

Соответствие каналов DMA периферийным устройствам.

Каналов DMA только 7. Периферийных устройств больше. Поэтому на каждый канал поступают сигналы запросов от нескольких периферийных устройств, объединенные логическим элементом ИЛИ. В каждый момент времени только одно устройство может использовать канал.

Остальные периферийные устройства должны отключить свои запросы DMA. Для этого у каждого устройства с поддержкой DMA есть специальный бит активации/деактивации формирования запроса.

Остается привести таблицу соответствия каналов DMA периферийным устройствам.

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

На мой взгляд, контроллер DMA один из самых простых и понятных узлов STM32.

В следующем уроке будем разрабатывать программы, использующие контроллер DMA.

Источник

СОДЕРЖАНИЕ

Принципы

Третья сторона

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

Стандартный DMA, также называемый сторонним DMA, использует контроллер DMA. Контроллер DMA может генерировать адреса памяти и инициировать циклы чтения или записи памяти. Он содержит несколько аппаратных регистров, которые могут быть записаны и прочитаны ЦП. К ним относятся регистр адреса памяти, регистр подсчета байтов и один или несколько регистров управления. В зависимости от того, какие функции предоставляет контроллер DMA, эти регистры управления могут определять некоторую комбинацию источника, пункта назначения, направления передачи (чтение с устройства ввода-вывода или запись на устройство ввода-вывода), размер единица передачи и / или количество байтов, передаваемых за один пакет.

Для выполнения операций ввода, вывода или преобразования памяти в память главный процессор инициализирует контроллер прямого доступа к памяти, подсчитывая количество слов для передачи и адрес памяти для использования. Затем ЦП дает команду периферийному устройству инициировать передачу данных. Затем контроллер DMA предоставляет адреса и линии управления чтением / записью в системную память. Каждый раз, когда байт данных готов для передачи между периферийным устройством и памятью, контроллер DMA увеличивает свой внутренний адресный регистр до тех пор, пока не будет передан полный блок данных.

Освоение автобуса

Режимы работы

В режиме серийной съемки

В пакетном режиме весь блок данных передается в одной непрерывной последовательности. Как только контроллер DMA получает доступ к системной шине от ЦП, он передает все байты данных в блоке данных, прежде чем отдать управление системными шинами обратно ЦП, но делает ЦП неактивным на относительно длительные периоды времени. Этот режим также называют «режимом блочной передачи».

Циклический режим кражи

Прозрачный режим

Согласованность кеша

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

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

Также существуют гибриды, в которых вторичный кэш L2 согласован, а кэш L1 (обычно на ЦП) управляется программным обеспечением.

Примеры

В IBM PC / AT улучшенная шина AT (более известная как ISA или «промышленная стандартная архитектура») добавила второй контроллер DMA 8237, чтобы обеспечить три дополнительных, и, как было подчеркнуто конфликтами ресурсов с дополнительной расширяемостью XT по сравнению с оригинальный ПК, столь необходимые каналы (5–7; канал 4 используется как каскад к первому 8237). Регистр страницы также был изменен для адресации полного адресного пространства памяти 16 Мбайт ЦП 80286. Этот второй контроллер также был интегрирован таким образом, чтобы обеспечивать возможность выполнения 16-битных передач, когда устройство ввода-вывода используется в качестве источника данных и / или назначения (поскольку он фактически обрабатывает данные только для передачи из памяти в память, в противном случае просто управление потоком данных между другими частями 16-битной системы, делая ширину собственной шины данных относительно несущественной), удваивая пропускную способность при использовании трех верхних каналов. Для совместимости нижние четыре канала DMA по-прежнему были ограничены только 8-битными передачами, и хотя передачи из памяти в память теперь были технически возможны из-за освобождения канала 0 от необходимости обрабатывать обновление DRAM, с практической точки зрения они имели ограниченную ценность из-за связанной с этим низкой пропускной способности контроллера по сравнению с тем, что теперь мог достичь ЦП (т.е. 16-битный, более оптимизированный 80286, работающий как минимум на 6 МГц, против 8-битного контроллера, заблокированного на 4,77 МГц). В обоих случаях проблема границы сегмента 64 КБ осталась, когда отдельные передачи не могли пересекать сегменты (вместо этого «зацикливаться» на начало того же сегмента) даже в 16-битном режиме, хотя на практике это было больше проблемой программирования. сложность, чем производительность, поскольку постоянная потребность в обновлении DRAM (как бы обрабатывалась) для монополизации шины примерно каждые 15 мкс не позволяла использовать большие (и быстрые, но бесперебойные) передачи блоков.

Каждый канал DMA имеет 16-битный адресный регистр и связанный с ним 16-битный счетный регистр. Чтобы инициировать передачу данных, драйвер устройства устанавливает адрес канала DMA и регистры счета вместе с направлением передачи данных, чтения или записи. Затем он инструктирует оборудование DMA начать передачу. Когда передача завершена, устройство прерывает работу ЦП.

Дискретно-сборный или векторный ввод-вывод DMA позволяет передавать данные в и из нескольких областей памяти за одну транзакцию DMA. Это эквивалентно объединению в цепочку нескольких простых запросов DMA. Мотивация состоит в том, чтобы разгрузить несколько задач прерывания ввода / вывода и копирования данных из ЦП.

16-битное управление шиной разрешено ISA.

Стандартные назначения ISA DMA:

PCI архитектура не имеет центрального контроллера DMA, в отличие от ISA. Вместо этого любое устройство PCI может запросить управление шиной («стать мастером шины ») и запросить чтение и запись в системную память. Точнее, компонент PCI запрашивает владение шиной у контроллера шины PCI (обычно мост хоста PCI и мост PCI-PCI), который будет выполнять арбитраж, если несколько устройств запрашивают владение шиной одновременно, поскольку одновременно может быть только один мастер шины. Когда компоненту предоставляется право собственности, он будет выдавать обычные команды чтения и записи на шине PCI, которые будут востребованы контроллером шины PCI.

I / OAT

Дальнейшие ориентированные на производительность усовершенствования механизма DMA были внесены в процессоры Intel Xeon E5 с их функцией прямого ввода-вывода данных ( DDIO ), позволяющей «окнам» DMA располагаться в кэшах ЦП, а не в системной ОЗУ. В результате кэши ЦП используются в качестве основного источника и места назначения для ввода-вывода, позволяя контроллерам сетевого интерфейса (NIC) выполнять прямой доступ к памяти напрямую в кэш последнего уровня (кэш L3) локальных ЦП и избегать дорогостоящего извлечения данных ввода-вывода. данные из системной оперативной памяти. В результате DDIO снижает общую задержку обработки ввода-вывода, позволяет полностью выполнять обработку ввода-вывода в кэше, предотвращает превращение доступной полосы пропускания / задержки ОЗУ в узкое место производительности и может снизить энергопотребление, разрешив ОЗУ дольше оставаться в маломощном состоянии.

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

Клетка

DMA в ячейке полностью согласован с кешем (обратите внимание, однако, локальные хранилища SPE, управляемые DMA, не действуют как глобально согласованный кеш в стандартном смысле ). Как при чтении («получить»), так и при записи («положить») команда DMA может передавать либо одну область блока размером до 16 КБ, либо список от 2 до 2048 таких блоков. Команда DMA выдается путем указания пары локального адреса и удаленного адреса: например, когда программа SPE выдает команду put DMA, она указывает адрес своей собственной локальной памяти в качестве источника и адрес виртуальной памяти (указывающий на либо основная память, либо локальная память другого SPE) в качестве цели вместе с размером блока. Согласно эксперименту, эффективная пиковая производительность DMA в ячейке (3 ГГц при равномерном трафике) достигает 200 ГБ в секунду.

Конвейерная обработка

Источник

DMA для новичков или то, что вам нужно знать

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

DMA, что это? О чем вы говорите?

DMA, или Direct Memory Access – технология прямого доступа к памяти, минуя центральный процессор. В эпоху 486-ых и первых Pentium во всю царствовала шина ISA, а также метод обмена данными между устройствами – PIO (Programmed Input/Output).

Когда объемы данных, которыми оперирует процессор начали возрастать, стало понятно, что нужно минимизировать участие процессора в цепочке обмена данными, а то прийдется туго. И вот тогда активное применение нашла технология прямого доступа к памяти.

Кстати говоря, DMA используется не только для обмена данными между устройством и ОЗУ, но также между устройствами в системе, возможен DMA трансфер между двумя участками ОЗУ (хотя данный маневр не применим к x86 архитектуре). Также в своем процессоре Cell, IBM использует DMA как основной механизм обмена данными между синергетическими процессорными элементами (SPE) и центральным процессорным элементом (PPE). Также каждый SPE и PPE может обмениватся данными через DMA с оперативной памятью. Данный прием – на самом деле большое преимущество Cell, ибо избавляет от проблем когерентности кешей при мультипроцессорной обработке данных.

И снова теория

Прежде чем мы перейдем к практике, я бы хотел осветить несколько важных аспектов программирования PCI, PCI-E устройств.

Я вскользь упомянул о регистрах устройства, но как же к ним имеет доступ центральный процессор? Как многие из вас знают, есть такая сущность в компьютерных технологиях, как IO порты (Input/Output ports). Они предназначены для обмена информацией между центральным процессором и периферийными устройствами, а доступ к ним возможен с помощью специальных ассемблерных инструкций — in/out. BIOS (или OpenFirmware на PPC based системах) на ранних этапах инициализации PCI устройств, а также некоторых других (Super IO контроллера, контроллера PS/2 устройств, ACPI timer и т.д.), закрепляет за определенным контроллером собственный диапазон IO портов, куда и отображаются регистры устройства.

Итак, существует два метода утилизации DMA: contiguous DMA и scatter/gather DMA.

Contiguous DMA

Scatter/gather DMA

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

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

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

Источник

Электроника для всех

Блог о электронике

Контроллер прямого доступа к памяти (DMA) контроллера STM32

Работа с контроллером DMA

▌Что это?
Есть в современных контроллерах такой блок DMA — контроллер прямого доступа к памяти (ПДП). Штука очень простая, несмотря на умное название. Вся ее суть заключается в том, чтобы по команде от периферии или ядра взять и скопировать кусок памяти с одного места на другой.

Что с этим можно делать? Ой да много чего. Можно, задать в качестве источника адрес какой-либо периферии, скажем выходной регистр АЦП, а в качестве приемника адрес массива в ОЗУ, дать приказ DMA по команде завершения оцифровки АЦП хватать результат и пихать в массив. При этом DMA сам скопирует, сам увеличит адрес, а как заполнит буфер, то обнулится и начнет его переписывать по кругу заново.

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

А можно сделать и наоборот. Сказать DMA что мол вот тебе буфер, там лежит пара сотен байт, возьми и запихай их все в жерло UART и пойти по своим делам, а DMA трудолюбиво отправит в передачу весь буфер.

▌Ближе к теме
Контроллеры
Если рассмотреть конкретно STM32 то там находится один (на малых контроллерах вроде STM32F103C8T6) или два DMA контроллера DMA1 и DMA2 соответственно. На DMA1 есть 7 каналов, на DMA2 всего 5. Оба канала DMA сидят на шине AHB и перед тем как начать с ним работать надо на него подать тактирование, подняв биты DMA1EN и DMA2EN в регистре RCC_AHBENR, как и с любой другой периферией на STM32. В остальном они идентичные и работа с первым и вторым одинакова.

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

Каналы контроллера
Каждый канал независимый и может работать сам по себе со своими настройками. Но в один момент времени может работать только один канал у каждого контроллера. Чтобы избежать коллизий у каждого канала есть два уровня приоритета. Первый, программный. Мы просто в битах настройки задаем один из четырех уровней. А второй аппаратный, если придут запрос на два канала с одинаковым приоритетом в настройках, то победит тот, чей номер меньше. Сходно с обработкой прерываний. Там такой же двухступенчатый арбитраж.

За выбор приоритета отвечают два бита PL регистра DMA_CCRx Для каждого канала регистр свой. Вариантов там немного, всего четыре:

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

Каждый канал привязан к конкретной периферии. Т.е. если вам нужно чтобы DMA пинал АЦП, то ищите к какому каналу подключено именно АЦП.

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это
Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

Обратите внимание на то, что помимо аппаратных событий в мультиплексор еще ведет софтверный триггер от бита MEM2MEM. Для каждого канала:

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

Размер данных
За раз DMA может копировать порцию данных в 1, 2 или 4 байта. В первую очередь это влияет на приращение адресов при вычислении куда класть. Так что это настройка жизненно важна. Сколько и куда класть определяется обычно периферией. Т.е. если UART принимает и выдает по байту, то результат у нас 8 битный. А вот АЦП, может, например, выдать 16 битный результат. Значит размер указывать надо два байта, чтобы сразу за один заход их все забрать. Ну и, очевидно, что размер принимаемых и сохраняемых данных обычно совпадает. Хотя, вам никто не запретить класть однобайтные данные периферии в 32 разрядный массив, выравниваясь по двойному слову. Тогда размер может быть и разный.

За размер данных периферии и памяти отвечают два бита PSIZE и MSIZE регистра DMA_CCRx

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

00 — 1 байт
01 — 2 байта
10 — 4 байта
11 — не используется.

Откуда куда
Адрес периферии для каждого канала задается в регистре DMA_CPARх этого канала. Просто пишем туда адрес нужного регистра периферии. Но есть два важных момента. Во-первых, нельзя писать в этот регистр при включенном DMA. Т.е. при изменении их бит EN должен быть снят. Второе, адрес зависим от битов PSIZE регистра DMA_CCRx. Т.е. если у нас указан размер данных как 1 байт (PSIZE = 00), то активные все биты регистра DMA_CPARx. Но если данные указаны как слова или как двойное слово, по 16 или 32 бита соответственно, то один или два младших бита этого регистра игнорируются вообще. Т.е. получается, что адрес выравнивается по словам или двойным словам. Т.е. DMA не сможет записать данные словами начиная с нечетного адреса, но адреса все выровнены по словам, так что это пофигу.

Адрес памяти лежит в аналогичном регистре DMA_CMARx и там все то же, что и для DMA_CPARx только за размер отвечают биты MSIZE и его тоже нельзя трогать на включенном канале.

Также надо указать направление копирования. За него отвечает бит DIR регистра DMA_CCRx.

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

Когда он 0 то мы читаем из адреса DMA_CPARx и пишем по адресу DMA_CMARx. А когда он 1, то наоборот, соответственно. Название у бита идиотское. Не, ну понятно, что направление, но лучше бы назвали его M2P, то есть если 1, то из памяти в периферию. Или как то так. Долго никак не мог запомнить направление, пока не связал, что 0 он такой округлый и похож на такую же округлую букву P — Periph. А 1 угловатая, прям как буква М — Мemory.

Ну и опции инкремента адреса. биты PINC и MINC во все том же DMA_CCRx. Они отвечают за то, чтобы после каждой сработки у нас автоматически увеличивался адрес которые в DMA_CPARx или с DMA_CMARx соответственно.

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

Адрес периферии прибит намертво и редко когда надо его менять, так что обычно PINC всегда равно нулю. Т.е. никакого инкремента. Вам же не надо, чтобы после чтения из DR того же UART1 на следующем байте было уже из следующего по списку BRR :)))

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

Зачем же нужен инкремент DMA_CPARx? А для режима копирования из памяти в память. Тогда мы в DMA_CPARx пишем адрес одного буфера, в DMA_CMARx адрес другого, ставим бит MEM2MEM, даем разрешение и поехали!

А вот бит MINC ставится почти всегда. Мы указываем DMA_CMARx начало буфера и DMA, увеличивая адрес, его последовательно заполняет, или читает из него в периферию.
Впрочем, если нам надо гнать из периферии в периферию, скажем из АЦП сразу в SPI, то бит MINC тоже равен нулю будет.

Ну и есть еще один вариант, когда инкремента нет ни на адресе приемника, ни на адресе источника. Таким образом делаются прямые перегонки, например, из АЦП сразу в USART, минуя процессор. Или на SPI. Так можно в десяток строк кода превратить STM32 в какой нибудь вариант SPI АЦП 🙂

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

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

Сколько?
За то сколько раз должно отработать DMA отвечает регистр DMA_CNDTRx. На каждую сработку от входного сигнала (или от постоянно стоящего бита MEM2MEM) DMA копирует один обьект и уменьшает число в DMA_CNDTRx и так до нуля. Дойдет до нуля, канал выключится. Бит EN тут уже ничего решать не будет. Но если стоит бит CIRC, то регистр перезагрузится исходным значением и продолжит работу. Значение может быть до 65535, используются только младшие 16 бит, старшие два байта ДОЛЖНЫ БЫТЬ НУЛЕМ ВСЕГДА.

Записать в регистр DMA_CNDTRx можно только при выключенном DMA канале. Читать можно когда угодно, по нему можно определять сколько объектов осталось DMA передать.

И тут есть важный нюанс. Я намеренно выше говорил, что «сколько осталось объектов». DMA_CNDTRx Считает не БАЙТЫ, а сколько раз DMA сработал. А за одну сработку, он зависимости от настроек, может пересунуть 1, 2 или 4 байта.

Т.е. если вы откуда то скопипастите код в котором будет что-то вида:

Разумеется изменив PSIZE и MSIZE на 32 бита, то вы получите веселуху: sizeof(IN_Buffer) от 10 uint32_t даст вам 40 и DMA пропишет вам в оперативку 40 раз по четыре байта, захреначив все до куда дотянется :)))) Так что либо корректируйте результат операции sizeof с учетом разрядности данных, либо напрямую указывайте сколько у вас повторений в подходе.

▌Обратная связь
Жевать байты это замечательно, но должен же этот контроллер как то сообщать фоновой программе, что «он сделяль»? Само собой. И реализовано это совершенно традиционно, через прерывания. Их у него три:

За включение этих прерываний отвечают биты

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

TEIE: Transfer error interrupt enable
HTIE: Half transfer interrupt enable
TCIE: Transfer complete interrupt enable

Регистра DMA_CCRx. У каждого канала свои. С прерываниями тут тоже все щедро. У каждого канала свой вектор, если заглянете в startup_stm32f103xb.s файл, то там будет что то вида:

Это вектора прерываний и есть. Чуть ниже, если вообще есть, будут и вектора для контроллера номер два. А понять же по какому поводу нас вызвало можно из регистра DMA_ISR — Interrupt status registry. Он на каждый контроллер DMA свой. В нем стоят все флаги какие только можно, оптом для всех каналов сразу. Сюда можно только только смотреть. Регистр read only.

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

Для сброса флага нужно записать 1 в соответственный ему бит регистра DMA_IFCR — Interrupt flag clear registry. Запись нуля же не означает ничего. Так что пишем сразу маску и не паримся.

Контроллер прямого доступа к памяти что это. Смотреть фото Контроллер прямого доступа к памяти что это. Смотреть картинку Контроллер прямого доступа к памяти что это. Картинка про Контроллер прямого доступа к памяти что это. Фото Контроллер прямого доступа к памяти что это

▌Инциализация и запуск

Вот теперь самое интересное. Прочитали выше написанное, поняли что нужно сделать… А теперь важно сделать это все в правильном порядке. DMA все критические настройки у DMA требуют, чтобы канал был выключен в момент и изменения. За включение и выключение отвечает бит EN регистра DMA_CCRx. Причем крайне желательно ставить этот бит отдельно от всех и снимать отдельно от всех.

Иначе могут быть приколы. Я тут недавно прикольные вилы словил. Сделал процедурку инициализации, думаю, а чего это я их буду по одному ставить? В одном же слове все, дай их сразу и пропишу как надо? За один заход. Выставил все биты конфигурации, на UART1 — работает. Ну окей, взял ЭТИ ЖЕ функции и накатил их на инициализацию UART2 с ТОЧНО таким же кодом, только про UART2, т.е. поменял только имена регистров и каналов DMA. Запускаю… не работает, хоть убейся. Первый UART через DMA работает, второй нет. И так и эдак… Ничего не понимаю. Ладно я бы что-то не то сделал, так тогда бы оба не работали…

Стал под отладчиком ходить, смотреть по регистрам DMA, а у меня запись в регистр конфигурации втором случае DMA_CCRx не проиходит. Т.е. бит должен записаться, а не записывается. Стал разбираться что за фигня и как это так получается вообще? Оказалось, что это оптимизатор так решил, что ему удобней будет записать в одном случае (который работает) сначала старший байт в порт, а потом младший. При этом бит EN записывается последним, а во втором случае наоборот. Записывает младший, т.е.бит EN, а при записи старшего происходит аппаратный сброс этого бита EN. ИЧСХ в даташите ни разу не сказано, что бит EN может сниматься аппаратно. Нигде. Но это происходит.

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

#define DMAEnable (1 CCR; // Копируем биты настройки tmp &= CCR_CLEAR_Mask; // и стираем все кроме битов EN. А он и так будет 0 tmp |= Conf; // Закатываем на результат наши биты настроек. Channel->CNDTR = Size; // Заполняем все нужные поля. Размер передчи Channel->CPAR = Perif; // Адрес периферии Channel->CMAR = Mem; // Адрес в памяти Channel->CCR = tmp; // Записываем настройки в память. >

Эти две фукнции просто включают и выключают определенный канал.

void DMA_Enable(DMA_Channel_TypeDef* Channel) < Channel->CCR |= DMA_CCR1_EN; > void DMA_Disable(DMA_Channel_TypeDef* Channel) < Channel->CCR &= (uint16_t)(

Еще нужна процедурка деинициализации DMA, чтобы вернуть все настройки в изначальное состояние, как после сброса:

void DMA_DeInit(DMA_Channel_TypeDef* Channel) < Channel->CCR &= (uint16_t)(

DMA_CCR1_EN); Channel->CCR = 0; Channel->CNDTR = 0; Channel->CPAR = 0; Channel->CMAR = 0; if (Channel == DMA1_Channel1) < /* Reset interrupt pending bits for DMA1 Channel1 */ DMA1->IFCR |= DMA1_Channel1_IT_Mask; > else if (Channel == DMA1_Channel2) < /* Reset interrupt pending bits for DMA1 Channel2 */ DMA1->IFCR |= DMA1_Channel2_IT_Mask; > else if (Channel == DMA1_Channel3) < /* Reset interrupt pending bits for DMA1 Channel3 */ DMA1->IFCR |= DMA1_Channel3_IT_Mask; > else if (Channel == DMA1_Channel4) < /* Reset interrupt pending bits for DMA1 Channel4 */ DMA1->IFCR |= DMA1_Channel4_IT_Mask; > else if (Channel == DMA1_Channel5) < /* Reset interrupt pending bits for DMA1 Channel5 */ DMA1->IFCR |= DMA1_Channel5_IT_Mask; > else if (Channel == DMA1_Channel6) < /* Reset interrupt pending bits for DMA1 Channel6 */ DMA1->IFCR |= DMA1_Channel6_IT_Mask; > else if (Channel == DMA1_Channel7) < /* Reset interrupt pending bits for DMA1 Channel7 */ DMA1->IFCR |= DMA1_Channel7_IT_Mask; > else if (Channel == DMA2_Channel1) < /* Reset interrupt pending bits for DMA2 Channel1 */ DMA2->IFCR |= DMA2_Channel1_IT_Mask; > else if (Channel == DMA2_Channel2) < /* Reset interrupt pending bits for DMA2 Channel2 */ DMA2->IFCR |= DMA2_Channel2_IT_Mask; > else if (Channel == DMA2_Channel3) < /* Reset interrupt pending bits for DMA2 Channel3 */ DMA2->IFCR |= DMA2_Channel3_IT_Mask; > else if (Channel == DMA2_Channel4) < /* Reset interrupt pending bits for DMA2 Channel4 */ DMA2->IFCR |= DMA2_Channel4_IT_Mask; > else < if (Channel == DMA2_Channel5) < /* Reset interrupt pending bits for DMA2 Channel5 */ DMA2->IFCR |= DMA2_Channel5_IT_Mask; > > >

И, собственно, примеры:

▌Копирование одного массива в другой. Режим MEM2MEM

// Массив который копируем и куда копируем static uint32_t INbuff[10] = <0xFFFFFFF1,0xFFFFFFF2,0xFFFFFFF3,0xFFFFFFF4,0xFFFFFFF5,0xFFFFFFF6,0xFFFFFFF7,0xFFFFFFF8,0xFFFFFFF9,0xFFFFFF10>; static uint32_t OUTbuff[10] = <0>; // Включаем тактирование DMA RCC->AHBENR |= RCC_AHBENR_DMA1EN; // Обнуляем канал который будем использовать. Канал берем от балды. Для этой цели подойдет любой свободный. DMA_DeInit(DMA1_Channel3); // Настраиваем DMA_Init( DMA1_Channel3, // Какой канал работать будет (uint32_t)INbuff, // Откуда (uint32_t)OUTbuff, // Куда 10, // Сколько. 10 двойных слов, не байтов. Массив у нас на 10 элементов TransCompl_Int_Disable + // Прерывание по передаче выключено HalfCompl_Int_Disable + // Прерывание по половине выключено TransError_Int_Disable + // Прерывание по ошибке выключено ReadPerif + // Читаем из «периферии». CircularMode_Disable + // Циклический режим не нужен. Копируем один раз. PeripheralInc_Enable + // Увеличиваем адрес источника MemoryInc_Enable + // Увеличиваем адрес приемника PDataSize_DW + // Размер источника двойной слово MDataSize_DW + // Размер приемника двойное слово DMA_Priority_Low + // Низкий приоритет M2M_Enable ); // Копирование память-память. // Разрешаем копирование. DMA_Enable(DMA1_Channel3); >

Вуаля! Данные будут скопированы.

▌Копирование из периферии в буфер памяти.
В данном случае из выходного регистра USART в кольцевой буфер в ОЗУ. Все что попадет в USART окажется в памяти автоматом.

static volatile char BufferForRecieving1[256]; // Кольцевой приемный буфер. RCC->AHBENR |= RCC_AHBENR_DMA1EN; // Подали тактирование на DMA DMA_Disable(DMA1_Channel5); // Выключили канал. DMA_DeInit(DMA1_Channel5); // Обнулили DMA канал USART1->SR &=

Теперь все что попадает в USART будет DMA утаскивать прямо в буфер, остается только его проверять. Ну или включить прерывания по половине и/или окончании передачи и реагировать на них.

▌Копирование из буфера в периферию

И после подачи разрешения буфер будет загружен в UAR побайтно, по мере отправки байт через последовательный интерфейс.

Ну и сами два файлика которые я использую как библиотечку для DMA

Спасибо. Вы потрясающие! Всего за месяц мы собрали нужную сумму в 500000 на хоккейную коробку для детского дома Аистенок. Из которых 125000+ было от вас, читателей EasyElectronics. Были даже переводы на 25000+ и просто поток платежей на 251 рубль. Это невероятно круто. Сейчас идет заключение договора и подготовка к строительству!

А я встрял на три года, как минимум, ежемесячной пахоты над статьями :)))))))))))) Спасибо вам за такой мощный пинок.

22 thoughts on “Контроллер прямого доступа к памяти (DMA) контроллера STM32”

Я на СТМ8 пользовался как раз для фоновой обработки АЦП в кольцевой буфер. очень удобно оказалось

Там он вроде бы очень похож на тот, что в стм32.

Ещё есть интересные примеры использования DMA+таймеров есть в этой теме: http://kazus.ru/forums/showthread.php?t=107109
сообщение 8 — управление восьмисегментными индикаторами
сообщение 9 — управление дисплеями HD44780/WH1602A

Находил ещё вот такой интересный материал про DMA: https://habr.com/ru/post/437112/. Не пугайтесь, там только вначале про Cypress, а потом уже про STM32. Был несколько удивлен после прочтения и некоторых размышлений.

Хм, а чему тут удивляться? Дма это не про скорость, а про параллельность. В чем ее и прелесть.

насчет параллельности тоже ведь не все просто. Сколько одновременно потоков может обращаться к памяти? судя по статье с хабра — 2 (по крайней мере для описаного МК). при этом не важно кто: ядро или DMA каналы …

но опять же, в наших то применениях этого и не замечаешь особо.

кстати, а может кто знает: если ОЗУ физически разделена на отдельные регионы (SRAM1, SRAM2, …), то глядя на матрицу шин в даташите, возникает вопрос: возможен ли одновременный доступ ядра к одной области и DMA к другой?
Вопрос чисто теоритический, но вдруг кто на практике проверял)

В данном случае главное, что в это время код выполняется тоже.

Доступ вряд ли доступен одновременно. На то и приоритеты даны, ядро будет приоритетней.

Для приема по USART можно использовать IDLE line detected, чтобы не ждать середины/конца заполнения буфера.

А я DMA настроил на вывод картинки на экран. В памяти сделал фреймбуфер, натравил на этот буфер DMA, и теперь просто рисую в этот буфер, и всё это само появляется на экране. Удобно, быстро и не напрягает процессор кучей прерываний по TXE SPI.

Почитайте внимательно мануал раздел 9.13.17. После программного сброса бита DMA_CCR_EN, нужно дождаться его фактического сброса, циклически перечитывая CCR, и тестируя текущее значение CCR_EN. Иначе ваша инициализация на работающем DMA может с некоторой вероятностью обломиться. И ещё неплохо бы раскрыть тему FIFO, которое позволяет многократно сократить нагрузку на системную шину со стороны DMA.

А вы часом семейства не путаете? В F103 я ничего такого не нашел в описании (RM00008).

Глава 9 вообще про GPIO и там всего 5 подглав.

Да, действительно, я перепутал, это для F4 так, поэтому код от F1 нельзя просто без изменений перенести на старшие семейства, несмотря на внешнюю одинаковость регистров, он вроде и будет работать, но на самом деле не совсем:).

А вот это интересное замечание получилось! SPL она же вроде сквозная идет. В том числе и на ф4, но там я не увидел выборки контроллера в библиотеке и не проверяется бит ЕН.

SPL для каждого семейства своя, хотя у них кое-как совместимый внешний API.

>> …оптимизатор так решил, что ему удобней будет записать в одном случае (который работает) сначала старший байт в порт, а потом младший.
>>
>> …при записи в регистры DMA надо явно указывать тип данных, чтобы компилятор сделал запись обои байт, старшего и младшего.
>>
>> #define DMAEnable ((uint16_t)(1<>
>>

Регистры имеют тип uint32_t, константы (1 uint32_t при присвоении не должно порождать кода преобразования вообще. Откуда чудеса?

Я думаю корни растут из Thumb2 который позволяет упаковку данных в памяти побайтно и работу с отдельными байтами. Так что он вполне может упихать константу в байт.

Не в тему, но про надежность кода — плюсовать битовые параметры не есть хорошая идея, лучше использовать битовый OR

Согласен, если какой-нибудь бит по какой-либо причине продублируется, то в случае «+» будет непонятный глюк, а в случае OR всё в порядке.

Привет. Сори, не знаю где спросить, пишу сюда. Почему в руководстве по Си http://easyelectronics.ru/file/yazyk-programmirovaniya-s-spravochnik/124 после пункта 1.8 Not found страницы?

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

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *