В каком случае доступ к памяти реализуется с помощью dma контроллера
Всем привет, сегодня мы с вами поговорим о DMA: именно о той технологии, которая помогает вашему компьютеру воспроизводить для вас музыку, выводить изображение на экран, записывать информацию на жесткий диск, и при этом оказывать на центральный процессор просто мизерную нагрузку.
Contiguous DMA
- Выделяется один буфер достаточно большого размера в оперативной памяти.
- Физический адрес (точнее сказать адрес на шине участка памяти, потому как physical address и bus address – равны в x86 архитектуре, но не равны в PPC) этого буфера записывается в регистр устройства.
- Во время того, как приходят данные на устройство, контроллер устройства инициирует DMA трансфер.
- После того, как буфер полностью заполнен, контроллер устройства инициирует прерывание, чтобы сообщить центральному процессору, что буфер следует передать операционной системе.
- Драйвер операционной системы обрабатывает прерывание, и передает полученные данные из буфера, далее по стеку устройств операционной системы.
DMA, что это? О чем вы говорите?
DMA, или Direct Memory Access – технология прямого доступа к памяти, минуя центральный процессор. В эпоху 486-ых и первых Pentium во всю царствовала шина ISA, а также метод обмена данными между устройствами – PIO (Programmed Input/Output).
- На сетевую карту пришло 1500 байт данных.
- Сетевая карта инициирует прерывание с целью сообщить процессору, что данные необходимо забрать с устройства, иначе произойдет так называемый buffer overrun.
- Операционная система ловит прерывание от контроллера прерываний и отдает его на обработку драйверу.
- Драйвер в цикле побайтно читает данные с регистров сетевой карты.
Когда объемы данных, которыми оперирует процессор начали возрастать, стало понятно, что нужно минимизировать участие процессора в цепочке обмена данными, а то прийдется туго. И вот тогда активное применение нашла технология прямого доступа к памяти.
Кстати говоря, 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 портов, куда и отображаются регистры устройства.
- Скорость доступа к физической памяти выше, нежели к IO портам.
- IO порты могут отображать не более 65535 байт регистров, в то время как размер ОЗУ современных компьютеров в разы больше.
- Читать регистры устройства из ОЗУ проще, нежели с помощью IO портов :)
Итак, существует два метода утилизации DMA: contiguous DMA и scatter/gather DMA.
Принцип работы
Большинство устройств ПК нуждаются в периодическом обмене данными не только с центральным процессором (ЦП), но и с оперативной памятью. В первых вариантах персональных компьютеров процесс обмена данными какого-либо устройства с ОЗУ протекал при помощи процессора. Такой метод получил название PIO (Programmable Input-Output, программируемый ввод-вывод). Однако этот метод имел ряд недостатков. Прежде всего, было очевидно, что поскольку процессор загружен множеством задач, то он не всегда может отвлекаться на то, чтобы управлять процессом чтения и записи данных ОЗУ, тем более, что объем этих данных в результате прогресса компьютерной техники все увеличивался и увеличивался.
Так появилась идея технологии DMA (сокращение от Direct Memory Access, т.е. Прямой Доступ к Памяти), состоящая в том, чтобы позволить различным устройствам обращаться к оперативной памяти напрямую, минуя ЦП. Также часто используется русская аббревиатура данной технологии – ПДП.
Первоначально практическая реализация этой технологии (в материнских платах на основе шины ISA) была осуществлена при помощи встроенного в материнскую плату контроллера ПДП, который был призван управлять процессом обмена данными между устройством и ОЗУ. При этом процессор также не был полностью исключен из этого процесса. Прежде всего, механизм ПДП инициализировался самим процессором, однако в ход процесса передачи данных он не вмешивался, занимаясь в это время другими задачами. После того, как обмен информацией между устройством и ОЗУ завершался, то процессор получал соответствующее прерывание, которое отсылал ему контроллера DMA.
В шине ISA также использовались специальные каналы ПДП, которые часто закреплялись за отдельным устройством:
Обычно данные каналы можно было устанавливать программным путем, но на некоторых старых устройствах, например, картах расширения для подключения накопителей CD-ROM, необходимо было вручную устанавливать значения нужных каналов при помощи перемычек.
Заключение
Появление технологии ПДП позволило разгрузить процессор и избавить его от большого объема рутинной работы по пересылке данных между оперативной памятью и устройствами, расположенными на материнской плате или подключенными к ней. Особенно важно использование разновидности технологии ПДП – Ultra DMA в винчестерах на основе интерфейса IDE, что позволяет значительно ускорить обмен данными между накопителем IDE и материнской платой.
Современная реализация
Начиная с появления шины ввода-вывода PCI, концепция практической реализации ПДП претерпела изменения. В материнских платах с шиной PCI больше не использовался контроллер DMA, а вместо этого стала применяться технология Bus Mastering. Суть этой технологии заключается в том, что любое устройство может обратиться к шине и полностью использовать ее в своих целях, в том числе, и для доступа к оперативной памяти. Кроме того, в шине PCI отпала необходимость в использовании каналов доступа к памяти. Подобный механизм используется также и в преемниках шины PCI –сверхбыстрых шинах AGP и PCI-Express.
Прямой доступ к памяти могут использовать любые устройства, расположенные в слотах расширения материнской платы, или подключенные к ней при помощи внутренних шин. Это могут быть, например, жесткие диски, накопители для оптических дисков, видеокарты, звуковые и сетевые карты, и т.д. Кроме того, технология DMA может использоваться как внутри процессоров – для передачи данных между отдельными ядрами, так и внутри самой оперативной памяти – для обмена данными между различными участками памяти.
Современные операционные системы, такие как MS Windows, умеют управлять режимом ПДП для многих устройств. В частности, пользователь имеет возможность включить или выключить режим DMA для жестких дисков.
В жестких дисках с интерфейсом IDE технология ПДП получила свое развитие в виде дополнительных режимов ПДП, получивших название Ultra DMA (UDMA). Всего стандарт Ultra DMA поддерживает 8 основных режимов передачи данных, обеспечивающих скорость от 16,7 до 167 МБ/c. Использование режимов Ultra DMA для винчестеров позволило значительно увеличить пропускную способность шины IDE. Включить или изменить режим Ultra DMA для жестких дисков можно при помощи специальной опции BIOS, обычно носящей название DMA (UDMA) Mode.
Scatter/gather DMA
С ростом скорости Ethernet адаптеров, contiguous DMA показал свою несостоятельность. В основном из-за того, что требовались области памяти достаточно большого размера, которые подчас невозможно было выделить, так как в современных системах фрагментация физической памяти достаточно высока. Во всем виноват механизм виртуальной памяти, без которого нынче никуда :)
Решение напрашивается само собой: использовать вместо одного большого участка памяти несколько, но в разных регионах этой самой памяти. Возникает вопрос, но как же сообщить контроллеру устройства, как инициировать DMA трансфер и по какому адресу писать данные? И тут нашли решение, использовать дескрипторы, чтобы описывать каждый вот такой участок в оперативной памяти.
- Адрес участка ОЗУ (именно bus address), который предназначен для DMA трансфера.
- Размер описываемого участка ОЗУ.
- Опциональные флаги и другие специфические аргументы.
- Адрес следующего дескриптора в памяти.
- Драйвер операционной системы выделяет и иницилизирует дескрипторы DMA буферов.
- Драйвер выделяет DMA буферы (участки ОЗУ для DMA трансфера) и записывает необходимую информацию о них в дескрипторы.
- Устройство по мере возникновения потребности, заполняет DMA буферы, и после того, как заполнен один или несколько буферов инициирует прерывание.
- Драйвер ОС просматривает все дескрипторы DMA буферов, определяет какие из них были заполнены контроллером устройства, пересылает данные из буфера далее по стеку устройств и помечает буфер как готовый к DMA трансферу.
На сегодня пожалуй все, иначе информации станет слишком много. В следующей статье я покажу вам, как с этой уличной магией работает IOKit. Жду отзывов и дополнений ;)
В уроке узнаем о способе передачи данных в режиме прямого доступа к памяти, реализации этого процесса в микроконтроллере STM32. Подробно изучим режимы работы, форматы регистров, особенности контроллера ПДП STM32.
Микроконтроллер обменивается данными с “внешним миром” только через периферийные устройства, а данные хранит в памяти. Поэтому в любой реальной программе существует задача обмена данными между оперативной памятью микроконтроллера и периферийными устройствами. Также бывает необходимость в обмене данными между блоками памяти (копирование блоков) и в передаче информации с одного периферийного устройства на другое.
В предыдущих уроках мы реализовывали подобные задачи программным способом. В цикле считывали данное из памяти или регистра и загружали его в регистр или память.
Использование прерываний позволяло только оперативно перераспределять работу процессора между задачами. Процессор все равно занимался примитивными операциями – пересылкой данных. Программа была простой, и мы могли позволить тратить почти все процессорное время на копирование данных.
- А вот при реализации цифрового осциллографа в уроке 29 программное чтение данных АЦП значительно ограничивало возможности устройства. На время чтения развертки осциллографа программа работала в блокирующем режиме, зависала. Приходилось даже запрещать прерывания. Благодаря высокому быстродействию STM32 удалось считывать данные АЦП с дискретностью 1 мкс, но это был предел. С большей скоростью поток данных уже не обработать.
А ведь задача пересылки данных между памятью и периферийным устройством с аппаратной точки зрения очень простая. Достаточно аппаратного счетчика адреса памяти, счетчика количества данных и промежуточного регистра. Данное считывается, например, из АЦП и записывается в память по адресу из специального регистра-счетчика. Его содержимое увеличивается на 1, и новое данное из АЦП записывается в память по следующему адресу. Процесс продолжается пока счетчик количества данных не остановит работу устройства. При этом процессор продолжает выполнение основной программы.
Подобные аппаратные узлы содержатся во всех современных высокопроизводительных микроконтроллерах. Конечно, их устройство сложнее, чем я описал в предыдущем абзаце, но и функций намного больше.
Процесс обмена данными без участия процессора (за счет аппаратных узлов микроконтроллера)
- между памятью и периферийными устройствами микроконтроллера,
- или между периферийными устройствами,
- или между блоками памяти
называется прямым доступом к памяти (ПДП). Английская аббревиатура DMA - Direct memory access.
Аппаратная часть микроконтроллера, реализующая процесс прямого доступа к памяти называется контроллер прямого доступа к памяти (контроллер DMA).
Использование ПДП (DMA) позволяет:
- освободить процессор для выполнения других задач;
- обеспечить высокие скорости передачи данных, недостижимые при использовании программного способа обмена.
Устройство контроллеров DMA, как правило, простое, очевидное и понятное для разработчиков программ.
Общие принципы работы контроллера прямого доступа к памяти в STM32.
В техническом описании STM32 есть функциональная схема контроллера DMA. Не вижу никакого смысла приводить ее здесь. На схеме показаны шины подключения контроллера к системным шинам, через которые происходит прямой доступ к оперативной памяти.
Но взаимодействие контроллера DMA с памятью, разрешение конфликтов при одновременном обращении процессора и контроллера к памяти или периферийным устройствам отрабатываются автоматически. Повлиять на них мы не можем. Поэтому просто считаем, что контроллер DMA имеет доступ к оперативной памяти.
- В микроконтроллере STM32F103c8 есть только один контроллер DMA c 7 каналами. В микроконтроллерах старших серий (high-density и XL-density) добавлено второе устройство DMA.
- Как я писал выше, прямой доступ к памяти происходит по общей с процессором системной шине. Поэтому при одновременном обращении к памяти или периферийному устройству процессора и контроллера DMA могут возникать конфликты. В результате работа процессора может приостанавливаться, но как минимум половина пропускной способности системной шины отдается процессору. Программа будет выполняться даже при интенсивном обмене через прямой доступ к памяти. Не говоря о том, что не каждая команда программы работает с оперативной памятью или периферийным устройством.
- Инициаторами обмена с помощью контроллера DMA являются аппаратные события микроконтроллера (event). Конечно, при наличии соответствующих разрешений в управляющих регистрах. В уроке 18 я писал о событиях, и чем они отличаются от прерываний.
В общем случае прямой доступ к памяти происходит следующим образом.
- В периферийном устройстве возникает событие, требующее операции DMA. Соответствующий сигнал поступает на контроллер DMA.
- Происходит обмен данными между памятью и периферийным устройством.
- Сразу после обращения к периферийному устройству, контроллер посылает ему сигнал подтверждения, который сбрасывает событие.
- Новое событие вызывает очередной обмен данными.
Сам обмен данными (транзакция) с использованием DMA состоит из трех операций.
- Данное загружается в промежуточный регистр из памяти или из регистра данных периферийного устройства в зависимости от направления обмена. Адреса памяти и периферийного устройства содержатся в специальных регистрах.
- Данное перегружается в память или регистр данных периферийного устройства.
- Регистр количества транзакций (элементов обмена) уменьшается на 1. При достижении 0, обмен завершается.
Все просто и логично.
Каналы DMA.
Повторюсь. В микроконтроллере STM32F103c8 есть только один контроллер DMA c 7 каналами.
С точки зрения программиста каждый канал – это отдельный аппаратный узел микроконтроллера. Он осуществляет DMAпередачу между регистром данных периферийного устройства (расположенным по фиксированному адресу) и памятью.
Каналы работают с общей памятью, через общую шину данных. Поэтому между ними могут возникать конфликты, которые разрешаются следующим способом.
В каждый момент времени обмен данными может происходить только по одному каналу. При нескольких активных каналах контроллер DMAразрешает работу канала с более высоким приоритетом.
Приоритет каждого канала устанавливается программно и может иметь четыре уровня:
- Very high priority (очень высокий);
- High priority (высокий);
- Medium priority (средний);
- Low priority (низкий).
При активных каналах с одинаковым уровнем приоритета преимущество имеют каналы с меньшим номером.
Регистры каналов.
Каждый канал для нас – отдельный аппаратный контроллер передачи данных.
Каждый канал имеет 4 собственных регистров, которые полностью определяют режим обмена данными по нему.
Каналы настолько независимы, что если вести речь обо всех регистрах контроллера DMA, то к регистрам каналов добавятся только регистры управления прерываниями.
Итак 4 регистра, с очевидным назначением.
-
— адрес памяти, по которому выполняется запись или чтение в зависимости от направления передачи данных. — адрес регистра данных периферийного устройства, из которого выполняется чтение или в который производится запись. В режиме обмена данными между блоками памяти он также содержит адрес памяти. — содержит количество элементов для передачи (количество транзакций). В качестве элемента транзакции могут быть выбраны байт, полуслово, слово. — регистр конфигурации канала. Разрешает работу канала, определяет режимы передачи данных.
Полное описание регистров есть в справочнике.
Количество элементов данных для передачи устанавливается программно и может быть от 0 до 65535.
В общем случае обмен DMA происходит между памятью и периферийным устройством. Источником или приемником данных периферийного устройства является регистр данных этого устройства. Он имеет фиксированный адрес, который должен оставаться неизменным в ходе DMA обмена.
Что касается памяти, то как правило, необходимо записывать или считывать данные по последовательно расположенным адресам.
Для этого существует режим инкрементации адреса. Он доступен как для адреса памяти (бит MINC регистра DMA_CCRx), так и для адреса периферийного устройства (бит PINC регистра DMA_CCRx). Хотя в последнем случае не имеет смысла.
Таким образом, после каждого обращения к памяти указатель увеличивается на 1, 2 или 4, в зависимости от заданного размера элемента данных. Начальные адреса передачи данных хранятся в регистрах DMA_CMARx (для памяти) и DMA_CPARx (для периферии). В процессе обмена данными их значения не изменяются, а текущие значения указателей содержатся во внутренних регистрах микроконтроллера и программе недоступны.
Размер элементов данных транзакции может иметь значения:
- 8 бит (байт);
- 16 бит (полуслово);
- 32 бита (слово).
Выбор размера данных осуществляется независимо не только для каждого канала, но и отдельно для приемника и передатчика данных каждого канала. Для этого в регистре 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_CPARx адрес регистра данных периферийного устройства. Данные будут записываться/считываться по этому адресу после возникновения события периферийного устройства, формирующего запрос DMA.
- Загрузить в DMA_CMARx начальный адрес блока памяти. В этот блок будут записываться данные из периферийного устройства, либо из него будут считываться данные для передачи в периферийное устройство.
- Записать в DMA_CNDTRx общее количество элементов обмена. Каждая транзакция будет уменьшать значение этого регистра. При достижении 0, DMA передача будет остановлена.
- Задать конфигурацию DMA передачи в регистре DMA_CCRx: направление передачи, приоритет канала, режим инкрементирования, размер элемента данных и т.д.
- Включить канал битом EN регистра DMA_CCRx.
После этого начинают обрабатываться запросы от периферийных устройств, подключенных к каналу.
Чтением регистра DMA_CNDTRx можно узнать количество оставшихся для передачи элементов.
- После завершения передачи половины от общего количества элементов, будет установлен флаг HTIFx в регистре DMA_ISR. Произойдет прерывание на это событие, если оно разрешено в регистре DMA_CCRx (бит HTIE).
- После завершения передачи всех данных, будет установлен флаг TCIFx в регистре DMA_ISR. Произойдет прерывание на это событие, если оно разрешено в регистре DMA_CCRx (бит TCIE).
- Если не задан циклический режим, то после пересылки заданного количества данных работа канала останавливается.
- В циклическом режиме (бит CIRC регистра DMA_CCRx) после завершения передачи данных, в регистр DMA_CNDTRx автоматически перезагружается его первоначальное значение, и обмен продолжается бесконечно.
Режим “из памяти в память”.
Режим используется для копирования блоков данных в памяти.
- Для его конфигурации необходимо установить бит MEM2MEM в регистре DMA_CCRx.
- После включения канала битом EN регистра DMA_CCRx сразу начнется передача данных. Дополнительные запросы не требуются.
Режим запрещено использовать совместно с циклическим режимом.
Выравнивание данных разной разрядности.
При DMA передаче данных разрядности приемника и источника могут не совпадать. Действует простое и логичное правило. Всегда сохраняются младшие разряды.
- Если разрядность источника превышает разрядность приемника, то лишние старшие разряды источника игнорируются. Например, 32х разрядное число 0x87654321 передаем в периферию с регистром данных 16 разрядов. В него запишется 0x4321.
- Если разрядность источника меньше разрядности приемника, то недостающие старшие разряды в приемнике будут заполнены нулями. Например, 16ти разрядное число 0x4321 источника загружаем в 32х разрядный приемник. Запишется 0x00004321.
В принципе используется правило преобразования (приведения) типов данных языка C.
Специфика доступа к периферийным устройствам через шины AHB/APB.
Периферийные устройства STM32 подключены к процессору и памяти через шины AHB/APB. Это 32х разрядные шины. Они не поддерживают операций передачи меньшей разрядности. В результате в регистры периферийных устройств можно записывать или считывать из них только 32х разрядные слова.
Многие регистры периферии имеют меньшую разрядность, часто 16 разрядов. В оставшиеся биты при передаче данных DMA тоже что-то будет записываться. В большинстве случае это не важно, разряды не значащие. Но все-таки давайте внесем определенность в этот процесс.
При записи в периферийное устройство байта или полуслова через шину AHB данные на неиспользуемых линиях шины дублируются. Например.
- Если мы записываем полуслово 0x4321, в регистр периферии загрузится 0x43214321.
- Если мы записываем байт 0x21, в регистр периферии загрузится 0x21212121.
- Но если мы укажем разрядность источника слово, т.е. запишем слово 0x87654321, а разрядность периферийного регистра укажем полуслово, то произойдет выравнивание разрядности, описанное в предыдущем абзаце. В результате в регистр загрузится 0x00004321.
Ошибки при DMA передаче.
Ошибка может возникнуть только в результате попытки доступа к зарезервированному адресному пространству. В этом случае канал, в котором произошла ошибка, отключается аппаратным сбросом бита EN. Устанавливается флаг ошибки TEIFx в регистре DMA_ISR. И формируется прерывание, если оно разрешено битом TEIE в регистре DMA_CCRx.
Прерывания DMA.
Для каждого канала могут генерироваться прерывания по следующим флагам регистра DMA_ISR.
- HTIFx – завершена передача половины данных. Разрешается прерывание битом HTIE в регистре DMA_CCRx.
- TCIFx - завершена передача всех данных. Разрешается прерывание битом TCIE в регистре DMA_CCRx.
- TEIFx – ошибка передачи. Разрешается прерывание битом TEIE в регистре DMA_CCRx.
Соответствие каналов DMA периферийным устройствам.
Каналов DMA только 7. Периферийных устройств больше. Поэтому на каждый канал поступают сигналы запросов от нескольких периферийных устройств, объединенные логическим элементом ИЛИ. В каждый момент времени только одно устройство может использовать канал.
Остальные периферийные устройства должны отключить свои запросы DMA. Для этого у каждого устройства с поддержкой DMA есть специальный бит активации/деактивации формирования запроса.
Остается привести таблицу соответствия каналов DMA периферийным устройствам.
На мой взгляд, контроллер DMA один из самых простых и понятных узлов STM32.
В следующем уроке будем разрабатывать программы, использующие контроллер DMA.
В уроке будем работать над проектом многоканального генератора импульсов. На практике научимся использовать систему прямого доступа к памяти микроконтроллера STM32.
В первую очередь будем разбираться, как использовать DMA с периферийными устройствами, которые мы уже изучили. А именно, с портами ввода-вывода, таймерами, UART. Работу с АЦП через DMA отложим на последующие уроки.
Я решил начать с DMA-передачи данных между памятью и портами ввода/вывода. Этот вариант использования прямого доступа к памяти практически не встречается в примерах. Тем не менее, он востребован во многих задачах.
Давайте разработаем практическое устройство, в котором DMA-обмен данными между портами ввода/вывода и памятью будет особенно эффективен. Более того, такое устройство, созданное без использования режима ПДП, будет иметь параметры как минимум на порядок хуже.
Используя одну плату STM32F103C8T6, сделаем некий комбинированный прибор, объединяющий функции многоканальных генератора последовательности импульсов и логического анализатора.
Поставим предварительное техническое задание.
- Оба устройства должны работать параллельно, в реальном времени. Т.е. логический анализатор сможет регистрировать сигналы собственного генератора импульсов.
- Число каналов для входных и выходных сигналов – по 4. В принципе, работать будем с байтами. Ничего не мешает реализовать устройство с 8 каналами для генератора и столько же для анализатора. Я ограничил разрядность, чтобы упростить себе задачу разработки программы верхнего уровня. Меньше диаграмм, проще отображать на экране компьютера, проще задавать последовательности импульсов. Из этих же соображений я ограничил некоторые другие параметры устройства.
- Число выборок анализатора – 10000.
- Число временных градаций (интервалов) для создания сигнала генератора – 16.
- Время дискретизации входных и выходных сигналов определим как минимально возможное. Сделаем этот параметр регулируемым в широких пределах и посмотрим на практике, что получится.
- Временные параметры работы устройства и формы сигналов генератора будем задавать от компьютера. На нем же собираемся выводить диаграммы логического анализатора и устанавливать режимы работы устройства.
Что в результате получится – не знаю. Придут в голову новые функции и возможности – добавим. Но есть стремление создать устройство, которое может найти практическое применение.
В момент написания этих строк у меня ничего нет. Разработка только начинается. Буду последовательно создавать программу, подробно рассказывать об использовании DMA.
С контроллером DMA в этом уроке работаем через регистры CMSIS.
Аппаратная часть.
На этом этапе необходимо назначить выводы микроконтроллера для входных сигналов анализатора и выходных сигналов генератора.
Я решил использовать разные порты для входных и выходных сигналов. Выбрал выводы:
- PA0, PA1, PA2, PA3 как входы логического анализатора;
- PB4, PB5, PB6, PB7 в качестве выходов генератора импульсов.
DMA-передача данных из памяти в порт ввода-вывода.
В этой части урока реализуем передачу данных из буфера ОЗУ в порт, тем самым создадим простой генератор импульсов. Последовательность состояний выходных сигналов пока зададим в программе, как инициализированный массив.
Используя STM32CubeMX, создадим проект Lesson34_1. В нем настроим только систему тактирования на максимальную частоту 72 мГц.
Конфигурируем порты ввода-вывода. Если забыли - открываете урок 7.
Разрешаем тактирование портов PA и PB.
/* USER CODE BEGIN SysInit */
// разрешение тактирования портов
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
Разряды PA0, PA1, PA2, PA3 устанавливаем в режим входов с подтягивающими к питанию резисторами.
- Биты MODE = 00 (режим вход)
- Биты CNF = 10 (подтягивающий резистор включен)
- Биты PxODR = 1 (подтягивающий резистор к питанию).
- В итоге CNF12[1:0], MODE12[1:0] = 1000.
В код добавляем:
// Выводы PA0, PA1, PA2, PA3 в режим входов с подтягивающими к питанию резисторами
GPIOA->CRL &= 0xffff0000;
GPIOA->CRL |= 0x00008888;
GPIOA -> BSRR = 0xf; // подтягивающие резисторы к + 3 В
Выводы PB4, PB5, PB6, PB7 конфигурируем, как активные выходы с максимальной частотой тактирования 50 мГц.
- Биты MODE = 11 (выход, 50 мГц)
- Биты CNF = 00 (активный выход)
- В итоге CNF12[1:0], MODE12[1:0] = 0011
В код добавляем:
// Выводы PB4, PB5, PB6, PB7 - активный выход с частотой тактирования 50 мГц.
GPIOB->CRL &= 0x0000ffff;
GPIOB->CRL |= 0x33330000;
- из регистра GPIOA->IDR всегда можно считать текущее состояние выводов PA0, PA1, PA2, PA3,
- все, что будет загружено в регистр GPIOB->ODR окажется на выводах PB4, PB5, PB6, PB7.
Сказанное распространяется и на операции записи-чтения через DMA.
Порты ввода-вывода выполняют простейшие действия - преобразование данных (чисел) в физические сигналы (в выходное напряжение на выводах микроконтроллера) и наоборот, уровни входного напряжения конвертируют в цифровой код. Они не могут формировать запросы DMA, тем более с нужной нам частотой.
Эту функцию выполняют аппаратные таймеры. Они могут формировать запросы DMA с заданной частотой.
Назначаем таймеры, каналы DMA, приоритеты.
Смотрим таблицу соответствия каналов DMA периферийным устройствам.
- таймер 2 для генерации импульсов.
- таймер 3 для сканирования входов анализатора.
Канал DMA | Источник запроса | Приоритет | Назначение |
2 | TIM2_UP | Средний | Генерация импульсов |
3 | TIM3_UP | Высокий | Сканирование анализатора |
4 | USART1_TX | Низкий | Передача данных на компьютер |
5 | USART1_RX | Низкий | Чтение данных от компьютера |
Значит, надо конфигурировать таймер 2 в режим циклического счетчика. Забывчивым можно просмотреть урок 19.
Зададим частоту тактирования таймера после предделителя равной 1 мГц. Получится, что если изменять значение регистра перезагрузки от 0 до 65535, счетчик будет сбрасываться с периодом от 1 мкс до 65,536 мс. С таким же периодом он будет формировать запросы DMA.
Добавляем в исходный код:
Последняя строка разрешает запрос DMA по переполнению счетчика. Это установка бита UDE в регистре TIMx_DIER. Я не приводил полный формат этого регистра в предыдущих уроках. Постепенно наведу порядок на странице ”Регистры STM32”. Распишу полностью форматы всех регистров периферийных устройств.
Теперь у нас таймер 2 циклически формирует запрос DMA, но сама передача данных не происходит. Запрещена работа контроллера DMA.
Настраиваем контроллер DMA.
Создадим массив, из которого будем передавать данные.
/* USER CODE BEGIN PV */
uint8_t generatorBuf[16]= < 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0 >; // буфер генератора
Я задал данные, формирующие диаграмму 4х разрядного двоичного счетчика.
Разрешаем тактирование DMA.
// Конфигурация DMA
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // разрешение тактирования DMA1
Задаем адрес буфера для передачи, адрес регистра данных порта вывода и количество данных
DMA1_Channel2->CMAR = (uint32_t) generatorBuf; // адрес буфера
DMA1_Channel2->CPAR = (uint32_t)(&GPIOB->ODR); // адрес регистра вывода данных порта
DMA1_Channel2->CNDTR = 16; // количество данных
Остается установить конфигурацию DMA в регистре CCR.
Я включил циклический режим, чтобы уже на этом этапе разработки программы можно было посмотреть осциллографом сигналы на выходе генератора.
Осталось включить передачу данных и можно проверять работу генератора.
У меня работает. Это 2 младших канала генератора.
Дискретность 10 мкс, как и задано. Второй канал подключен шнуром без делителя. Поэтому такие выбросы.
Функции (программные) управления генератором импульсов.
В нашем проекте надо будет включать генератор, выключать, задавать временные параметры. Давайте разработаем 3 очевидные функции для управления генератором.
Первая запускает генератор в однократном режиме, если аргумент нулевой, и в циклическом режиме при ненулевом аргументе.
Обратите внимание, что при каждом запуске необходимо заново устанавливать количество данных для передачи. При передаче каждого данного содержимое этого регистра уменьшается на 1 и по завершению передачи будет равно 0.
Еще не забывайте, что загрузка регистра CNDTR возможна только после отключения канала. В предыдущем уроке об этом написано подробно.
Вторая функция останавливает работу генератора.
И последняя, устанавливает период дискретизации сигналов генератора в микросекундах.
// период дискретизации генератора
void setPeriodGenerator (uint16_t period)
TIM2->ARR = period - 1;
>
Строку разрешения передачи данных после конфигурации DMA следует убрать. Теперь работа генератора начнется только после вызова startGenerator() .
Проект на этом этапе разработки можно загрузить по ссылке.
Генератор создан, работает. Осталось научиться управлять им.
Реализация управления генератором от компьютера.
Для управления генератором через UART я разработал простой цифровой протокол. Особенно не задумывался. Контрольные коды самые простые, но вполне достаточные при таком небольшом количестве данных.
Я реализую протокол обмена с компьютером самым простым способом – опросом состояния UART в основном цикле программы, без прерываний, в блокирующем режиме.
Этот блок я взял из урока 20. Только установил скорость 115200 и запретил прерывания UART.
// инициализация UART1
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // включаем тактирование UART1
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // разрешаем тактирование порта GPIOA
// настройка вывода PA9 (TX1) на режим альтернативной функции с активным выходом
// Биты CNF = 10, ,биты MODE = X1
GPIOA->CRH &= (~GPIO_CRH_CNF9_0);
GPIOA->CRH |= (GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9);
// настройка вывода PA10 (RX1) на режим входа с подтягивающим резистором
// Биты CNF = 10, ,биты MODE = 00, ODR = 1
GPIOA->CRH &= (~GPIO_CRH_CNF10_0);
GPIOA->CRH |= GPIO_CRH_CNF10_1;
GPIOA->CRH &= (~(GPIO_CRH_MODE10));
GPIOA->BSRR |= GPIO_ODR_ODR10;
// конфигурация UART1
USART1->CR1 = USART_CR1_UE; // разрешаем USART1, сбрасываем остальные биты
USART1->BRR = 625; // скорость 115200 бод
USART1->CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; // разрешаем приемник и передатчик
USART1->CR2 = 0;
USART1->CR3 = 0;
Дальше, в основном цикле реализовал блок обмена с компьютером. Посмотрите его в коде проекта. Логика этого блока проста и понятна.
Загрузить окончательный вариант проекта генератора можно здесь.
Основная программа все время тратит на ожидание команды от компьютера. Но это нисколько не отражается на работе генератора. Формирование импульсов происходит даже не в фоновом режиме, а в аппаратном блоке DMA.
Проверил работу обмена с компьютером с помощью терминала CoolTerm. Сформировал несколько команд вручную. Убедился, что приходят правильные ответные коды.
Убедился с помощью осциллографа, что выходные сигналы изменяются и соответствуют заданным.
Программа верхнего уровня для управления генератором импульсов.
Загрузить можно по ссылке GeneratorAnalyzer_1
Основное окно выглядит так.
- Кнопки:
- Пуск однократный - запускает последовательность из 16 ти интервалов.
- Пуск циклический – запускает последовательность импульсов в цикле.
- Стоп – останавливает цикл.
- Время дискретн. – загружает в устройство параметр - длительность одного интервала, заданный слайдером ниже. Т.е. изменяет время развертки выходного сигнала генератора.
Вот небольшой фильм о работе программы управления генератором импульсов.
Все работает, как было задумано.
Минимальное время дискретности, при которой работает генератор на практике, получилось 2 мкс. При периоде 1 мкс выходные сигналы перестают формироваться. Я не думаю, что это плохой результат. Возможно, таймер перестает вырабатывать запросы, когда в регистр перезагрузки заносится 0. Можно попробовать уменьшить значение предделителя.
TIM2->PSC = 71; // 1 мкс
Надеюсь, устройство найдет практическое применение. Может с доработками. Пока просто приятно нажимать мышью по диаграмме на мониторе компьютера и видеть, что сигналы на экране осциллографа изменяются.
В следующем уроке добавим к устройству логический анализатор. Данные будем передавать из порта ввода/вывода в память. Потом посылать на компьютер. Все через прямой доступ к памяти.
Термин прямой доступ к памяти (ПДП, DMA - Direct Memory Access) возник на заре развития процессорной техники. Этот термин фактически означает прямой доступ к памяти со стороны периферийного устройства без участия процессора. Смысл такого доступа очевиден: освободить процессор от рутинных операций пересылки данных между внешним устройством и памятью, а также из памяти в память, и за счёт этого сэкономить ресурс процессора (процессорное время), чтобы повысить его производительность на операциях обработки данных.
То устройство (функциональный узел), которое управляет пересылкой данных, называется контроллером ПДП. По мере развития процессорной техники, контроллеры ПДП стали многоканальными, что позволило поддерживать несколько каналов доступа к памяти от разных периферийных устройств, а также и из памяти в память. Сами архитектуры процессорных систем сильно видоизменялись, контроллеры ПДП, периферия и память входили в разные части процессорных систем, но общая идея прямого доступа к памяти фактически не изменилась. Упомянем далее несколько конкретных реализаций DMA.
У сигнальных процессоров ADSP-2185M есть два варианта доступа к внутренней памяти процессора со стороны внешнего устройства: IDMA и BDMA. Сигнальные процессоры Blackfin ADSP-BF523 имеют канал доступа Host DMA к памяти процессора со стороны внешнего устройства.
Режим DMA поддерживается на интерфейсах компьютера: ISA, PCI, PCI Express. Но для осуществления пересылок по DMA необходимо, чтобы периферийное устройство (вставленная в слот плата) поддерживала DMA.
Остановимся отдельно на режиме BUS Master компьютерных интерфейсов PCI и PCI Express. Плата, вставленная в слот, поддерживающая режим BUS Master, способна сама на время захватить шину, полностью взяв на себя роль контроллера ПДП. Для пользователя ПК это означает, что процессор во время пересылки данных по DMA (из платы в память ПК и обратно) может заниматься другими важными задачами, в том числе, и задачами пользователя.
Важно сказать, что отсутствие поддержки DMA, например, со стороны используемого PCI-устройства для системного интегратора должно означать, что для сохранения свойства воспроизводимости (унификации, переносимости) системы на другие компьютеры, нельзя закладываться на максимальные скорости передачи данных PCI-устройства, заявленные производителем, поскольку, в каждом конкретном случае максимальная скорость будет сильно зависеть не только от самого PCI-устройства, но и от текущей загрузки процессора компьютера, особенностей программной среды и чипсета материнской платы, от ситуации, когда не-DMA обращения к PCI-устройству на низком уровне системы будут не одинаково эффективно реализованы на фоне групповых пересылок, что может привести к зависимости от типа материнской платы, от операционной системы.
Перейти к другим терминам Cтатья создана: 14.08.2014 О разделе "Терминология" Последняя редакция: 25.08.2019 Термин используется при описании архитектур различных систем сбора данных, например, в документации перечисленных ниже систем.
АЦП: 16 бит; 16/32 каналов;
±0,2 В…10 В; 2 МГц
ЦАП: 16 бит; 2 канала; ±5 В; 1 МГц
Цифровые входы/выходы:
18/16 TTL 5 В
Интерфейс: PCI ExpressПлата АЦП/ЦАП
16/32 каналов, 16 бит, 2 МГц, PCI ExpressL-502
АЦП: 16 бит; 16/32 каналов;
±0,2 В…10 В; 2 МГц
ЦАП: 16 бит; 2 канала; ±5 В; 1 МГц
Цифровые входы/выходы:
17/16, ТТЛ 5 В
Интерфейс: USB 2.0 (high-speed), Ethernet (100 Мбит)
Гальваническая развязка.Модуль АЦП/ЦАП
16/32 каналов, 16 бит, 2 МГц, USB, EthernetE-502
АЦП: 14 бит; 16/32 каналов;
±0,07 В…10 В; 400 кГц
ЦАП: 12 бит; 2 канала; ±5 В; 8 мкс
Цифровые входы/выходы:
16/16 TTL 5 В
Интерфейс: PCIПлата АЦП/ЦАП
16/32 каналов, 14 бит, 400 кГц, PCIL-791
АЦП: 14 бит; 16/32 каналов;
±0,156 В…10 В; 400 кГц
ЦАП: 12 бит; 2 канала; ±5 В; 8 мкс
Цифровые входы/выходы:
16/16 TTL 5 В
Интерфейс: USB 2.0Термин DMA наверняка не раз встречался читателям в различных компьютерных статьях. Что же он означает, влияет ли это понятие на работу компьютера, и если да, то как? В этом мы и попытаемся разобраться.
Читайте также: