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

от admin

DMA — технология прямого доступа к памяти

Термин DMA наверняка не раз встречался читателям в различных компьютерных статьях. Что же он означает, влияет ли это понятие на работу компьютера, и если да, то как? В этом мы и попытаемся разобраться.

DMA - Direct Memory Access

Принцип работы

Большинство устройств ПК нуждаются в периодическом обмене данными не только с центральным процессором (ЦП), но и с оперативной памятью. В первых вариантах персональных компьютеров процесс обмена данными какого-либо устройства с ОЗУ протекал при помощи процессора. Такой метод получил название PIO (Programmable Input-Output, программируемый ввод-вывод). Однако этот метод имел ряд недостатков. Прежде всего, было очевидно, что поскольку процессор загружен множеством задач, то он не всегда может отвлекаться на то, чтобы управлять процессом чтения и записи данных ОЗУ, тем более, что объем этих данных в результате прогресса компьютерной техники все увеличивался и увеличивался.

Так появилась идея технологии DMA (сокращение от Direct Memory Access, т.е. Прямой Доступ к Памяти), состоящая в том, чтобы позволить различным устройствам обращаться к оперативной памяти напрямую, минуя ЦП. Также часто используется русская аббревиатура данной технологии – ПДП.

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

В шине ISA также использовались специальные каналы ПДП, которые часто закреплялись за отдельным устройством:

  1. Обновление DRAM
  2. Аппаратный пользовательский канал, обычно использовался для 8-битных звуковых карт
  3. Контроллер дисковода гибких дисков
  4. Жесткий диск. Канал не использовался для жестких дисков с поддержкой PIO, а затем с UDMA. Также канал использовался для некоторых звуковых карт и параллельного порта
  5. Каскадное прерывание от XT-контроллера ПДП
  6. Пользовательский канал, иногда использовался для жестких дисков или 16-битных звуковых карт
  7. Пользовательский канал
  8. Пользовательский канал

Обычно данные каналы можно было устанавливать программным путем, но на некоторых старых устройствах, например, картах расширения для подключения накопителей CD-ROM, необходимо было вручную устанавливать значения нужных каналов при помощи перемычек.

Современная реализация

Начиная с появления шины ввода-вывода 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.

Заключение

Появление технологии ПДП позволило разгрузить процессор и избавить его от большого объема рутинной работы по пересылке данных между оперативной памятью и устройствами, расположенными на материнской плате или подключенными к ней. Особенно важно использование разновидности технологии ПДП – Ultra DMA в винчестерах на основе интерфейса IDE, что позволяет значительно ускорить обмен данными между накопителем IDE и материнской платой.

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

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

Для этого мы в настройки 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 Для каждого канала регистр свой. Вариантов там немного, всего четыре:

  • 00 — низкий
  • 01 — средний
  • 10 — высокий
  • 11 — сверхвысокий.

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


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

Т.е. мы можем гонять данные не только по приказу периферии, но и просто по желанию левой пятки. Просто надо поставить бит MEM2MEM, указать откуда куда и дать приказ в виде EN бита. И DMA исполнит. Это дает некоторую свободу. Например, у нас нет возможности повесить DMA канал на периферию ,т.к. ее родной канал уже занят другой периферией. Бывает такое огорчение. Что делать? Правильно, взять любой другой канал, настроить на обслуживание этой периферии, а пинок давать не через аппаратное событие, в прерывании, вручную дернуть этот канал через Mem2Mem бит. Да будет не совсем красиво, но куда лучше чем в том же прерывании вручную копировать все это. Хотя тут надо смотреть что будет быстрей. Возможно выгоды не будет никакой. Ради одного байта заряжать DMA смысла нет, а вот ради четырех… уже может иметь смысл.

Размер данных
За раз 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 байта.

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

. uint8_t IN_buffer[10]; . DMA1_Channel1->CNDTR = sizeof(IN_Buffer); .

. uint32_t IN_buffer[10]; . DMA1_Channel1->CNDTR = sizeof(IN_Buffer); .

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

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

  1. Transfer error interrupt — ошибка передачи. Честно говоря не знаю, что должно произойти, чтобы эта ошибка вылезла. Никаких ограничений вроде бы нет. Выход за адресное пространство разве что?
  2. Half transfer interrupt — передана половина данных. Очень удобное прерывание. Можно организовать передачу по половинка буфера. Т.е. нажираем пол буфера, вызываем это прерывание, оно дает отмашку фоновой проге, что пора разгребать,а пока та разгребает, нажираем вторую половину. И как нажрем произойдет:
  3. Transfer complete interrupt — прерывание по окончании передачи. Ну тут все понятно

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

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

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

. .long DMA1_Channel1_IRQHandler // DMA1 Channel 1 .long DMA1_Channel2_IRQHandler // DMA1 Channel 2 .long DMA1_Channel3_IRQHandler // DMA1 Channel 3 .long DMA1_Channel4_IRQHandler // DMA1 Channel 4 .long DMA1_Channel5_IRQHandler // DMA1 Channel 5 .long DMA1_Channel6_IRQHandler // DMA1 Channel 6 .long DMA1_Channel7_IRQHandler // DMA1 Channel 7 .

Это вектора прерываний и есть. Чуть ниже, если вообще есть, будут и вектора для контроллера номер два. А понять же по какому поводу нас вызвало можно из регистра 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<<0) #define DMADisable (0) #define TransCompl_Int_Enable (1<<1) #define TransCompl_Int_Disable (0) #define HalfCompl_Int_Enable (1<<2) #define HalfCompl_Int_Disable (0) #define TransError_Int_Enable (1<<3) #define TransError_Int_Disable (0) #define ReadMemory (1<<4) #define ReadPerif (0) #define CircularMode_Enable (1<<5) #define CircularMode_Disable (0) #define PeripheralInc_Enable (1<<6) #define PeripheralInc_Disable (0) #define MemoryInc_Enable (1<<7) #define MemoryInc_Disable (0) #define PDataSize_B (0) #define PDataSize_W (0x0100) #define PDataSize_DW (0x0200) #define MDataSize_B (0) #define MDataSize_W (0x0400) #define MDataSize_DW (0x0800) #define DMA_Priority_Low (0) #define DMA_Priority_Med (1<<12) #define DMA_Priority_Hi (2<<12) #define DMA_Priority_VHi (3<<12) #define M2M_Enable (1<<14) #define M2M_Disable (0) #define CCR_CLEAR_Mask (0xFFFF8001)

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

А если указать тип явно, то все становится прекрасно.

#define DMAEnable ((uint16_t)(1<<0)) #define DMADisable ((uint16_t)0) #define TransCompl_Int_Enable ((uint16_t)(1<<1)) #define TransCompl_Int_Disable ((uint16_t)0) #define HalfCompl_Int_Enable ((uint16_t)1<<2) #define HalfCompl_Int_Disable ((uint16_t)0) #define TransError_Int_Enable ((uint16_t)(1<<3)) #define TransError_Int_Disable ((uint16_t)0) #define ReadMemory ((uint16_t)(1<<4)) #define ReadPerif ((uint16_t)0) #define CircularMode_Enable ((uint16_t)1<<5) #define CircularMode_Disable ((uint16_t)0) #define PeripheralInc_Enable ((uint16_t)(1<<6)) #define PeripheralInc_Disable ((uint16_t)0) #define MemoryInc_Enable ((uint16_t)(1<<7)) #define MemoryInc_Disable ((uint16_t)0) #define PDataSize_B ((uint16_t)0) #define PDataSize_W ((uint16_t)0x0100) #define PDataSize_DW ((uint16_t)0x0200) #define MDataSize_B ((uint16_t)0) #define MDataSize_W ((uint16_t)0x0400) #define MDataSize_DW ((uint16_t)0x0800) #define DMA_Priority_Low ((uint16_t)0) #define DMA_Priority_Med ((uint16_t)(1<<12)) #define DMA_Priority_Hi ((uint16_t)(2<<12)) #define DMA_Priority_VHi ((uint16_t)(3<<12)) #define M2M_Enable (1<<14) #define M2M_Disable 0 #define CCR_CLEAR_Mask ((uint32_t)0xFFFF8001)

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

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

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)(

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

▌Копирование одного массива в другой. Режим 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_SR_TC); // Сбросили флаг передачи USART1->CR3 |=USART_CR3_DMAR; // Включили сигнал от приема на DMA. В данном случае за UART1 на RX закреплен DMA1_Channel5) // Настраиваем канал DMA_Init( DMA1_Channel5, // Пятый канал 1 контроллера. (uint32_t)&(USART1->DR), // Адрес откуда брать — адрес регистра DR в USART1 (uint32_t)BufferForRecieving1, // Адрес куда класть результат sizeof(BufferForRecieving1), // Сколько класть? Так как буфер у нас из char, то sizeof будет равен числу элементов. Но лучше так не делать 😉 TransCompl_Int_Disable + // Прерывание по окончанию выключено HalfCompl_Int_Disable + // Прерывание по половине выключено TransError_Int_Disable + // Прерывание по ошибке выключено ReadPerif + // Читаем из периферии CircularMode_Enable + // Цикличный режим включен PeripheralInc_Disable + // Адрес периферии не увеличиваем MemoryInc_Enable + // А вот адрес примного буфера увеличиваем, перебирая байт за байтом его PDataSize_B + // Размер данных из периферии — байт MDataSize_B + // Размер данных в памяти — байт DMA_Priority_Low + // Низкий приоритет M2M_Disable ); // Режим копирования память-память выключен. // Включаем DMA — поехали! DMA_Enable(DMA1_Channel5);

Читать:
Как в питоне вернуться на определенную строку

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

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

static volatile char BufferForSending1[256]; DMA_Disable(DMA1_Channel4); // Выключили канал. DMA_DeInit(DMA1_Channel4); // Сбросили все настройки DMA_Init( DMA1_Channel4, // Канал будет 4 контроллера 1. Именно этот канал пинает USART1 при передаче (uint32_t)&(USART1->DR), // Указываем адрес регистра данных USART (uint32_t)BufferForSending1, // Указываем адрес буфера для передачи. В него предварительно записаны данные. 256, // Указываем размер буфера. TransCompl_Int_Disable + // Прерывание по окончанию выключено HalfCompl_Int_Disable + // Прерывание по половине посылке выключено TransError_Int_Disable + // Прерывание по ошибке выключено ReadMemory + // Читаем из памяти (а пишем в периферию) CircularMode_Disable + // Циклический режим выключен PeripheralInc_Disable + // Адрес периферии не меняем MemoryInc_Enable + // А вот адрес памяти наоборот увеличиваем PDataSize_B + // Данные размером в байт в периферии MDataSize_B + // Данные размером в байт в памяти DMA_Priority_Low + // Низкий приоритет M2M_Disable ); // Не из памяти в память. DMA1->IFCR = DMA_IFCR_CTCIF4; // Сбрасываем флажок бита пустого регистра. Чтобы не отправить сразу нулевой байт USART1->CR3 |=USART_CR3_DMAT; // Подключаем канал DMA к UART. Установив бит отвечающий за пинание уартом дма канала. DMA_Enable(DMA1_Channel4); // Поехали!

И после подачи разрешения буфер будет загружен в 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< uint16_t => uint32_t при присвоении не должно порождать кода преобразования вообще. Откуда чудеса?

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

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

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

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

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

26. Прямой доступ к памяти

DMA (Direct Memory Access) – режим прямого доступа к памяти, особый режим работы устройств, при котором устройства пишут в память и читают из памяти без участия центрального процессора. Это позволяет во многих случаях очень сильно повысить производительность. В этом режиме активным устройством является контроллер прямого доступа к памяти (КПДП). Процессор, получив от КПДП заявку на прямой доступ, прерывает свою работу и отключается от интерфейса, передавая его задатчику, т.е. КПДП. Процессор при этом не выполняет контекстного переключения, а может продолжать свою работу, если она не требует интерфейса. Управление интерфейсом переходит к КПДП, который посредством выполнения операций чтения и записи передает информацию между ОЗУ и ПУ с соответствующим заданием адресов памяти. В этом режиме используется механизм задания уровня приоритетов для тех ПУ, которые работают с прямым доступом к памяти. Этот режим также должен быть предусмотрен в интерфейсах.

Это доступ без участия процессора между устройствами вв./выв. и памятью, между отдельными блоками памяти.

Ц ентральный процессор и др. устройства должны уметь отключаться при ПДП. Для этого – запрос и ответ. Процесс инициируется по сигналу ПУ. Как только разрешен ПДП, шиной начинает управлять контроллер ПДП. При этом отключается адресация ПУ. При обмене между блоками памяти используется 2 адреса – источник и приемник. Этот процесс инициируется процессором. Запускать обмен может таймер: цикл чтения и цикл записи (2 цикла). Так было раньше почти во всех процессорах. В настоящее время, как такового, контроллера ПДП почти не стало.

В режиме ПДП «степень свободы» больше.

Общий алгоритм ПДП.

Для осуществления прямого доступа к памяти контроллер должен выполнить ряд последовательных операций:

принять запрос (DREQ) от устройства ввода-вывода;

сформировать запрос (HRQ) в процессор на захват шины;

принять сигнал (HLDA), подтверждающий захват шины;

сформировать сигнал (DACK), сообщающий устройству о начале обмена данными;

выдать адрес ячейки памяти, предназначенной для обмена;

выработать сигналы (MEMR, IOW или MEMW, IOR), обеспечивающие управление обменом;

по окончании цикла DMA либо повторить цикл DMA, изменив адрес, либо прекратить цикл.

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

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

27. Устройства ввода-вывода: адресация, организация доступа.

Использование механизма прерываний позволяет разумно загружать процессор в то время, когда устройство ввода-вывода занимается своей работой. Однако запись или чтение большого количества информации из адресного пространства ввода-вывода (например, с диска) приводят к большому количеству операций ввода-вывода, которые должен выполнять процессор. Для освобождения процессора от операций последовательного вывода данных из оперативной памяти или последовательного ввода в нее был предложен механизм прямого доступа внешних устройств к памяти – ПДП или Direct Memory Access – DMA. Прямой доступ к памяти (DMA) — это метод непосредственного обращения к памяти, минуя процессор. Процессор отвечает только за программирование DMA: настройку на определенный тип передачи, задание начального адреса и размера массива обмениваемых данных. Обычно DMA используется для обмена массивами данных между системной памятью и устройствами ввода-вывода.

Обмен данными между процессором и устройствами ввода-вывода осуществляется по системной шине, «хозяином» которой является процессор. При использовании контроллера DMA на время обмена данными он должен получить управление системной шиной, т.е. стать ее «хозяином». По окончании обмена подсистема DMA возвращает процессору право управления шиной.

Распространены два типа контроллеров DMA — контроллеры для IBM PC/XT и контроллеры для IBM AT. Вначале мы расскажем о первом типе контроллеров, затем займемся контроллером DMA компьютера IBM AT.

Контроллер прямого доступа для IBM PC/XT реализован на базе микросхемы Intel 8237A и содержит четыре канала.

Архитектура компьютера PC AT включает в себя подсистему DMA, состоящую из двух контроллеров DMA Intel 8237, регистра старшего адреса DMA и регистров страниц DMA. Эти контроллеры обеспечивают 7 каналов DMA.

Система обеспечивает передачу данных по каналам DMA как по одному байту за цикл DMA, так и по два байта за цикл, исходя из возможностей архитектуры процессора (двухбайтной шины данных). Чтобы сохранить преемственность подсистемы DMA в PC AT с аналогичной подсистемой в PC XT каскадирование «байтного» контроллера DMA с распределением каналов XT осуществляется через «словный» контроллер DMA.

Общий алгоритм ПДП.

Для осуществления прямого доступа к памяти контроллер должен выполнить ряд последовательных операций:

принять запрос (DREQ) от устройства ввода-вывода;

сформировать запрос (HRQ) в процессор на захват шины;

принять сигнал (HLDA), подтверждающий захват шины;

сформировать сигнал (DACK), сообщающий устройству о начале обмена данными;

выдать адрес ячейки памяти, предназначенной для обмена;

выработать сигналы (MEMR, IOW или MEMW, IOR), обеспечивающие управление обменом;

по окончании цикла DMA либо повторить цикл DMA, изменив адрес, либо прекратить цикл.

Режимы обслуживания.

Режим одиночной передачи (Single Transfer Mode)

В этом режиме контроллер DMA выполняет только одну передачу. Адрес и счетчик слов будут изменяться при каждой передаче. DREQ должен быть активным, пока не активизируется соответствующий DACK.

Режим передачи блока (Blok Transfer Mode)

В этом режиме передается блок информации во время обслуживания подсистемы DMA. DREQ должен быть активным, пока не появиться активный DACK.

Режим передачи по требованию (Demand Transfer Mode)

В этом режиме передача данных выполняется до тех пор, пока не появится TC или внешний -EOP, либо когда DREQ станет неактивным

Каскадный режим (Cascade Mode)

Этот режим использует объединение нескольких контроллеров DMA для расширения числа подключаемых каналов. Выходы HRQ и входы HLDA от дополнительных контроллеров соединяются соответственно со входами DREQ и выходами DACK первичного контроллера DMA .

Режим память-память

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

Контроллер прямого доступа к памяти: схема, логика, состояния и режим работы

В качестве примера реализации принципов прямого доступа к памяти (ПДП) рассмотрим микросхему КР580ВТ57. Программируемый контроллер ПДП предназначен для высокоскоростного обмена данными между памятью системы и че­тырьмя внешними устройствами (ВУ).

Контроллер осуществляет двунаправленный обмен данными между памятью и ВУ (по требованию ВУ), при этом в адресном канале микропроцессорной систе­мы формируются параметры заданного массива адресов ячеек памяти (началь­ный адрес и число циклов) и управляющие сигналы. Каждый из четырех каналов контроллера обеспечивает адресацию (путем инкрементирования выработанного адреса) внешней памяти массивами объемом до 16К байт с возможностью зада­ния любого из 64К начальных адресов.

Состав контроллера ПДП

рис. 3.11.1

В контроллере можно выделить следующие блоки (рис. 3.11.1): блок обработки запросов, формирователь адреса, блок логики чте­ния–записи, блок управления, буфер данных и два регистра — регистр установки режима и регистр состояния каналов. Рассмотрим их особенности.

Блок обработки запросов предназначен:

● для приема сигналов запроса ЗПДП0–ЗПДП3 на прямой доступ к памяти от ВУ;

● для маскирования входов каналов К0–К3;

● для выдачи сигналов подтверждения запроса ¯ППДП0 – ¯ППДП3 прямого доступа к памяти, информирующих ВУ о готовности контроллера к обмену данными по каналу ПДП.

Формирователь адреса содержит 16–разрядные регистры начального адреса (РНА0…РНА3) и числа циклов (РЧЦ0…РЧЦ3), схему инкремента–декремен­та, триггер. В процессе выполнения программы начальной установки в РНА запи­сывается начальный адрес ячейки памяти, к которой будет обращаться ВУ по ка­налу ПДП. В 14 младших разрядов регистра числа циклов РЧЦ заносится число N – 1, где N — число циклов. Два старших разряда этого регистра используются для управления обменом по каналу ПДП.

В каждом цикле из РНА считывается два байта адреса. Триггер обеспечивает порядок считывания: старший байт выводится через буфер данных на ШД, а младший байт адреса — по шинам А0…А3, А4…А7. По завершении цикла схема инкремента–декремента содержимое РНА увеличивает, а содержимое РЧЦ умень­шает на единицу. Следует отметить, что выводы контроллера А4…А7 всегда ис­пользуются как выходы для разрядов А4…А7 кода адреса, а выводы А0…А3 ис­пользуются:

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

Блок логики чтения–записи

осуществляет прием, формирование и выда­чу сигналов, обеспечивающих обмен информацией между процессором и контрол­лером ПДП, памятью и ВУ. Блок логики чтения–записи имеет следующие выводы:

● ¯Зп — двунаправленный управляющий трехстабильный вход/выход, использу­емый:

• как вход для получения сигнала из процессора на запись данных во внут­ренние регистры контроллера ПДП при его начальной установке;

• как выход,на котором формируется сигнал, разрешающий внешнему уст­ройству запись данных из памяти;

● ¯Чт — двунаправленный трехстабильный управляющий вход/выход, использу­емый:

• как вход для получения из процессора сигнала, разрешающего чтение (вы­вод) содержимого внутренних регистров контроллера;

• как выход длявыдачи сигнала на разрешение считывания данных из ВУ в память;

● ¯ЧтП, ¯ЗпП — выходы для управления чтением из памяти и записью в память;

● ¯ВК — вход (выбор кристалла), на который подается нулевой сигнал выбора микросхемы после того, как установлены сигналы записи или чтения. Сигнал ¯ВК инициирует обмен данными между процессором и внутренними регистра­ми контроллера ПДП при программировании; автоматически блокируется в режиме прямого доступа. Вход ¯ВК подключается к ША микропроцессорной системы непосредственно или через дешифратор.

Выводы ¯Зп, ¯Чт подключаются к процессору как входы и к ВУ как выходы, а вы­ходы ¯ЗпП, ¯ЧтП — к памяти микропроцессорной системы.

На этапе начальной установки в формирователе адреса дешифрируются младшие разряды А3…А0 кода адреса и после поступления от процессора сигна­лов ¯Зп, ¯Чт организуется запись или чтение программно доступных регистров устройства ПДП. При работе в цикле ПДП логические цепи блока чтения–записи формируют пары сигналов ¯Чт, ¯ЗпП и ¯Зп, ¯ЧтП на выходах устройства, обеспечи­вающих тактирование процесса обмена данными между ВУ и памятью.

регламентирует последовательность операций в течение всех циклов ПДП с помощью управляющих сигналов, а также осуществляет пере­ход контроллера из состояния ожидания в состояние обслуживания по сигналу подтверждения захвата (ПЗх). Блок управления имеет следующие выводы:

  • ЗЗх (Н RQ ) — выход, с которого снимается сигнал запроса захвата для микро­процессора;
  • ПЗх ( HLDA) — вход, на который поступает сигнал подтверждения захвата от микропроцессора;
  • Гт (RDY )— управляющий вход готовности. Сигнал Гт = 1 от ВУ активизирует работу контроллера ПДП; сигнал Гт = 0 переводит контроллер в состояние ожидания;
  • М128 — выход маркер 128–го цикла: М128 = 1 свидетельствует о том, что те­кущий цикл ПДП является по счету 128–м циклом от конца массива данных;
  • КС (ТС) — выход конец счета: КС = 1 указывает ВУ, что текущий цикл обмена по каналу ПДП является последним при передаче массива данных. Если раз­ряд «КС–стоп» в регистре установки режимов установлен в 1, то канал будет запрещен. Выход КС активизируется (КС = 1), когда содержимое 14–разряд­ного регистра числа циклов в данном канале устанавливается в 0;
  • РА (АЕ) — выход разрешения адреса: РА = 1 указывает системе, что происхо­дят циклы прямого доступа. При этом все шины отключаются от микропроцес­сора. Сигнал может быть использован для блокировки адресной шины в уст­ройствах, не участвующих в прямом доступе, а также для записи старших восьми разрядов кода адреса в буферный регистр адреса и отключения схе­мы выборки устройства. В режиме ПДП выборка устройства осуществляется сигналами ¯ППДП0 – ¯ППДП3;
  • СтА ( STBA ) — выходстроб адреса,сигнал которого стробирует старший байт адреса памяти, передаваемый через ШД в дополнительный буфер данных. Через этот буфер старший байт адреса поступает на адресную шину микро­процессорной системы;
  • ТИ — вход для тактовых импульсов;
  • Сброс —вход начальной установки устройства. Подача единичного сигнала на этот вход обнуляет содержимое всех программно доступных регистров, что приводит к отключению каналов К0 – К3.

Буфер данных представляет собой 8–разрядную двунаправленную шину с тремя состояниями, соединяющую контроллер ПДП с системной шиной дан­ных ШД.

Через буфер данных:

  • при программировании в режиме записи восемь бит данных D 7… D 0 из мик­ропроцессора передаются в контроллер ПДП для записи в регистр начально­го адреса, регистр числа циклов или регистр установки режима; при чтении процессором из устройства ПДП выводится содержимое регистра начально­го адреса, регистра числа циклов и регистра состояния каналов;
  • приработе канала ПДП в начале каждого цикла старшие восемь разрядов ад­реса передаются из адресного регистра соответствующего канала в память. Затем ШД освобождается для непосредственного обмена данными между па­мятью и ВУ в течение оставшейся части цикла. Эти данные через устройство ПДП не проходят.

Регистр установки

табл. 3.11.1

режимов хранит информацию о запрограммированных режимах автозагрузки, удлиненной и обычной записи, фиксированного приорите­та и циклического сдвига приоритетов, КС–стоп и др. В него при программировании контроллера ПДП записывается 8–разрядное управляющее слово. Регистр установки режима обычно загружается после того, как установлены регистр ад­реса (РгА) и регистр циклов (РгЦ). Назначение разрядов регистра установки режимов приведено в табл. 3.11.1.

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

табл. 3.11.2

указывает, в каком из четырех каналов окончился процесс передачи массива. Для этого в младшие разряды РС0–РС3 (флаги завершения обслуживания) записывается значение сигнала КС = 1 конца счета, появляющегося на выходе КС и указывающего на конец массива по соот­ветствующему каналу. Назначение разрядов регистра установки режимов приве­дено в табл. 3.11.2.

Основные состояния и режимы работы устройства.

Основными состояния­ми являются исходное состояние, программирование, ожидание и обслуживание.

Исходное состояние.

При поступлении на вход Сброс единичного сигна­ла устройство переходит в исходное состояние. В этом состоянии маскируются запросы всех каналов ПДП (Р0 = Р1 = Р2 = Р3 = 0), буферные схемы шины А0…А3 переводятся в состояние приема информации.

Программирование.

В состоянии программирования устройства микро­процессор по шине данных (ШД — D 0… D 7) осуществляет запись начального ад­реса, числа циклов и других данных в соответствующие регистры, адрес которых задается кодом А3А2А1А0на шинах А0–А3 (табл. 3.11.3). Старший разряд А3 кода позволяет различать при А3 = 0 —регистры каналовК0…К3; при А3 = 1 —регистр установки режимов(работает только на запись) ирегистр состояния каналов(ра­ботает только на чтение). Младший разряд А0 выбирает регистры начального ад­реса (А0 = 0) и числа циклов (А0 = 1). Два средних разряда А2А1указывают номера регистров (или каналов) в двоичном коде. Например, код 0101 соответствует РЧЦ2 — регистру числа циклов канала 2. Регистры контроллера ПДП загружаются или из них считывается информация, если микропроцессор выполняет команду записи или чтения путем обращения к устройству и его регистрам. Для этого микропроцессору необходимо выдать соответствующие сигналы записи ¯Зп или чтения ¯Чт и на системные адресные шины ША выставить адрес регистра в виде кода А3А2А1А0. В это время на шину данных ШД подается необходимая информа­ция D 7… D 0 для записи в регистры или через шину данных ШД читается информа­ция из котроллера ПДП. Для установки состояния программирования необходимо также подать сигнал выборки устройства ¯ВК = 0. В связи с тем, что регистры кана­лов являются 16–разрядными, для их загрузки или чтения требуется два про­граммных командных цикла. Вформирователе адресаконтроллера имеется триг­гер, который автоматически переключает цепи во время операции чтения или за­писи. Этот триггер определяет доступ к старшему или младшему байтам регистра. Сбрасывается триггер подачей единичного сигнала на входСброса,а также вся­кий раз при загрузке регистра установки режима.

табл. 3.11.3

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

Ожидание.

В состоянииожиданияконтроллер принимает от ВУ сигнал за­проса на получение цикла ПДП (ЗПДП0–ЗПДП3) и вырабатывает для микропро­цессора сигнал запроса захвата (ЗЗх). В этом состоянии системные шины нахо­дятся под управлением микропроцессора.

Обслуживание.

После поступления от микропроцессора сигнала подтверж­дения захвата (ПЗх) при наличии сигнала запроса (ЗПДП0 – ЗПДП3) от ВУ контрол­лер вырабатывает сигнал подтверждения запроса (¯ППДП0 – ¯ППДП3) для одного из ВУ и переходит всостояние обслуживания. Вэтом состоянии системные шины находятся под управлением контроллера, и реализуется один из запрограм­мированных режимов ПДП:

  • младшие 8 разрядов адреса памяти поступают на шины А0–А3, А4–А7, стар­шие 8 разрядов — на шину данных ШД;
  • формируются соответствующие сигналы управления ¯ЧтП и ¯ЗпП, ¯Зп и ¯Чт, ко­торые позволяют ВУ получить из ячейки памяти или передать в ячейку памя­ти за один цикл байт данных.

В первом цикле работы устройство ПДП передает начальный адрес ячейки па­мяти. В последующих циклах адрес увеличивается, а число циклов уменьшается на единицу, до тех пор, пока содержимое регистра числа циклов (вернее его 14 раз­рядов) не станет равным нулю. После этого формируется сигналконца счетаКС и возможны следующие режимы работы:

Related Posts