STM Урок 156. LL. DMA. MEM2MEM. Часть 2



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

Схема наша не изменилась, осталась с урока 155

 

 

Вернёмся к нашему проекту.

Давайте для начала посмотрим, как происходит процесс инициализации DMA и включения режима MEM2MEM.

Зайдём в тело функции MX_DMA_Init в файле main.c.

Сначала заводится тактирование периферии.

Затем задаётся направление передачи данных

 

 

Здесь устанавливается бит 14 регистра CCR1 MEM2MEM, которым и задаётся режим передачи из памяти в память, а бит 4 DIR остаётся сброшенным.

Далее устанавливается приоритет канала

 

 

В нашем случае все биты поля PL сброшены, приоритет у нас низкий.

Затем устанавливается режим передачи данных. В нашем случае обычный, а не циклический

 

 

Потом задаём режим автоинкрементирования адреса и источника и приёмника, включая тем самым биты PINC и MINC регистра CCR1

 

 

Далее задаём ширину данных или размерность источника при помощи установки определённых битов битового поля PSIZE, в нашем случае настраиваем на 16 бит

 

 

Аналогичным образом задаётся ширина данных приёмника с помощью битового поля MSIZE

 

 

Затем настраиваются приоритет и субприоритет прерываний DMA, но это уже другая история. О менеджере NVIC будет, я думаю, отдельный разговор.

Вот, в принципе, и вся инициализация DMA.

Добавим глобальные буферы и флаг

 

 

В функции main() удалим объявление лишней локальной переменной, оставив только i.

 

uint16_t i, r;

 

Удалим также и её инициализацию

 

i=0; r=0;

 

 

Удалим установку высокого уровня ножки порта PA4

 

LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_4);

 

Удалим включение SPI1

 

LL_SPI_Enable(SPI1);

 

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

 

Number_7219(87654321);

LL_mDelay(2000);

 

Очистим табло индикатора

 

 

Заполним приёмный буфер декрементирующимися от 1024 до 1 значениями

 

 

Подключим строковую библиотеку

 

 

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

 

 

Так как у нас полуслова, то байтов будет вдвое больше (2048).

Отобразим скопированные данные из буфера-приёмника попеременно на индикаторе

 

 

Удалим весь пользовательский код из бесконечного цикла, соберём код, прошьём контроллер и посмотрим результат

 

 

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

 

 

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

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

 

 

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

 

 

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

 

 

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

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

Закомментируем пока функцию копирования

 

//memcpy(dst_dma_buf,src_dma_buf,2048);

 

Сначала отключим канал с помощью специальной функции библиотеки LL

 

 

Данная функция сбросит бит EN в регистре CCR1

Сбросим флаги TE и TC

 

 

Зададим количество передаваемых данных

 

 

При помощи функции LL_DMA_SetDataLength заносится значение количества передаваемых данных в регистр CNDTR канала.

С помощью следующей функции сконфигурируем адреса источника и приёмника, занеся соответствующие адреса буферов в регистры CPAR и CMAR канала

 

 

Включим прерывания по ошибке передаче и по полному окончанию передачи данных

 

 

Включим канал, тем самым дав команду DMA к началу копирования данных

 

 

В данном случае у нас включится бит EN в регистре CCR1.

Дальнейшая задача — обработать прерывание окончании передачи и выставить флаг fl. Также при возникновении прерываний мы обязаны сбросить флаги прерываний.

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

 

 

В файле stm32f1xx_it.c добавим на данную функцию прототип

 

 

Вызовем её в соответствующем обработчике, узнав, что у нас установился нужный флаг

 

 

В случаем установки флага ошибки передачи сбросим нужный флаг прерывания

 

 

В функции main() файла main.c дождёмся установки нашего флага и сбросим его тоже

 

 

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

Зайдя в отладку, удалим наши точки останова ибо они все передвинулись, и добавим новые.

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

 

 

Вторую точку ставим в обработчик прерывания. Именно там у нас закончится процесс копирования

 

 

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

 

 

Продолжим отладку и посмотрим время второй остановки

 

Посчитаем разницу. Она составит 744.3 микросекунды, что процентов на 30 больше, чем копирование с помощью memcpy.

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

Итак, на данном занятии мы ещё глубже познакомились с аппаратной реализацией периферии DMA в контроллере STM32F1, а также на практике подтвердили свои знания, воспользовавшись при этом функционалом библиотеки LL.

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

 

 

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

 

Исходный код

 

 

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

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

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

 

 

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

 

STM LL. DMA. MEM2MEM

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

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

*