STM Урок 155. HAL. DMA. MEM2MEM



Пришло время познакомиться поближе с технологией DMA, её реализацией в контроллере STM32F1, пока поверхностно, без подробного рассмотрения регистров и их битов, которые мы рассмотрим в одном из следующих занятий, когда будем работать с DMA, применяя функционал библиотеки LL. Также в данном уроке мы остановимся пока на алгоритме передачи данных из одного участка памяти в другой или MEM2MEM без использования какой-либо периферии.

Использовать для изучения возможностей DMA мы будем на данном уроке библиотеку HAL.

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

Сначала давайте повторим, что же такое DMA, хотя я об этом говорил уже в уроке 15, когда мы применяли DMA совместно с шиной USART.

DMA (direct memory access) — прямой доступ к памяти. То есть мы из одной области памяти копируем данные в другую область памяти, либо из периферии в область памяти, либо из области памяти в периферию, но копируем не по одному байту, применяя при этом обязательно регистры АЛУ, а напрямую, без использования АЛУ. Этим самым достигается большее быстродействие (правда не всегда, да и смысл не в быстродействии), разгружается процессорное время. АЛУ лишь получает информацию о том, сколько определенных информационных единиц нужно переслать, а также адрес памяти источника и приемника, всё остальное уже происходит без его участия. Мы либо знаем приблизительно, сколько для этого требуется времени и исходя из этого уже строим свой алгоритм, либо пользуемся прерываниями и обрабатываем там событие окончания передачи через DMA.

В микроконтроллере STM32F1 модуль DMA имеет следующую структурную схему

 

 

Мы видим здесь, что блок-схема модуля DMA у контроллера STM32F1 содержит два контроллера DMA, в первом из которых присутствует семь независимых каналов, а во втором — пять, тем самым мы имеем 12 независимых каналов передачи данных.

Оба контроллера DMA связаны с шиной AHB, также имеют блоки Arbiter, которые отвечают за распределение приоритетов в случае возникновения одновременно двух запросов.

Приоритеты мы можем назначить каждому запросу следующие:

  • Very high priorityочень высокий,
  • High priorityвысокий,
  • Medium priorityсредний,
  • Low priorityнизкий.

Каждый канал передачи данных может работать с определённой периферией контроллера STM32

 

 

Из данных схем мы видим, что для копирования из памяти в память (MEM2MEM) мы можем использовать любой канал

При инициализации DMA мы указываем кроме адресов ещё и размерность данных, которая может быть 8, 16 и 32 бита. Размерность данных отправителя и получателя может не совпадать. На этот случай контроллер всё выровняет и в Reference Manual существует таблица, как именно данные выравниваются.

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

В DMA нашего контроллера мы также можем программировать количество данных для передачи до 65536.

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

Для оценки и мониторинга скопированных данных мы будем использовать 8-разрядный индикатор на драйвере MAX7219, поэтому, чтобы нам не писать библиотеку для работы с данным драйвером, мы возьмём за основу проект, например, урока 153 с именем SPI_MASTER и создадим из него проект с именем DMA_MEM_TO_MEM.

Откроем наш проект в Cube MX и отключим там периферию SPI1

 

 

Также отключим и ножку PA4

 

 

 

Перейдём в раздел System view и откроем там периферию DMA

 

 

Добавим в список канал, выбрав тип передачи MEM2MEM, оставим канал 1, только размер передаваемых данных изменим на полуслова (Half Word), инкрементирование также оставляем и у приёмника, и у источника

 

 

Откроем теперь NVIC (модуль вложенных векторных прерываний)

 

 

И включим прерывания от DMA, приоритеты не трогая

 

 

Сгенерируем проект, откроем его в Keil, подключим файл библиотеки max7219.c, включим программатору атоперезагрузку, а также уберём оптимизацию.

В файле main.c удалим объявление буфера

 

uint8_t RxBuf[2] = {0};

 

Вместо него добавим два больших буфера, один из которых будет являться источником передаваемых данных, а другой — их приёмником

 

 

В функции main() удалим инициализацию переменной и поднятие ножки

 

i=0;

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);

 

Наполним буфер-источник данными

 

 

Удалим вот это

 

Number_7219(87654321);

HAL_Delay(2000);

 

 

Подключим строчную библиотеку для работы функции memcpy

 

 

Вернёмся в main() и скопируем содержимое буфера-приёмника в буфер-источник после вызова функции инициализации индикатора пока с помощью стандартного копирования без использования DMA

 

 

2048, а не 1024 потому, что функция исчисляет объём в байтах, а у нас полуслова.

В бесконечном цикле удалим весь пользовательский код.

Покажем постепенно содержимое нашего приёмного буфера

 

 

Подключим нашу схему. Схему возьмём ту же, как и в уроке 153 для ведомого устройства

 

 

Соберём код, прошьём контроллер, и мы увидим, как у нас появится число 1024 и начнёт постепенно декрементироваться

 

 

Если немного подождать, то счётчик наш досчитает в обратную сторону до 1.

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

Поставим одну точку останова на функции копирования, а другую — на следующей строке

 

 

Запустим отладку и запомним время

 

 

Возобновим выполнение проекта и посмотрим изменённое время

 

 

Посчитаем разницу — будет 0.0005667 или 566.7 микросекунд. Запомним эту цифру.

Теперь попробуем передать данные посредством DMA.

Закомментируем функцию копирования, скопируем данные с помощью DMA

 

 

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

 

 

Добавим функцию-обработчик для прерывания по флагу окончания передачи данных, в которой установим наш флаг

 

 

Добавим прототип для этой функции

 

 

Затем мы вернёмся в функцию main() и зарегистрируем нашу функцию

Теперь перед выводом на индикатор скопированных данных подождём установки нашего флага

 

 

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

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

 

 

А вторую точку поставим уже в обработчике, так как именно в этот момент у нас заканчивается процесс копирования

 

 

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

 

 

Возобновим процесс и запомнив время на точке в обработчике

 

 

Разница получится 0,0006974 или 697,4 микросекунды, что даже несколько медленнее, чем при использовании обычного копирования, но зато в эти 700 микросекунд у нас спокойно может выполняться любой код, будь то какая-то обработка или ещё что-то. Вот в этом-то и прелесть DMA.

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

 

Всем спасибо за внимание!

 

 

Предыдущий урок Программирование МК STM32 Следующий урок

Исходный код

 

 

Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6

Программатор недорогой можно купить здесь ST-Link V2

Индикатор светодиодный семиразрядный с драйвером MAX7219

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

STM HAL. DMA. MEM2MEM

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

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

*