Одной из не обязательных но тем не менее весьма полезных функций в микроконтроллерах STM32 является DMA (Direct Memory Access) т.е. дословно "прямой доступ к памяти". В микроконтроллерах такую функцию обычно выполняет специальный контроллер. Также таких контроллеров может быть больше одного. DMA контроллер может выполнять передачу данных из одной области памяти в другую область минуя центральный процессор, который в этот момент может выполнять какие то более сложные функции. Поскольку передача данных это очень частая в практике и очень ресурсозатратная операция то наличие DMA контроллера позволяет значительно повысить производительности микроконтроллера в целом. Даже с учетом того что шина данных используемая и процессором и DMA контроллером одна и это отчасти несколько ограничивает эффективность такого распараллеливания, в целом всё равно использование DMA имеет смысл. Например нам нужно чтобы данные поступающие из UART или SPI попадали в массив расположенный в оперативной памяти. Возьмем для начала UART1 и попробуем через DMA передать данные в GPIO для наглядности. Как обычно сперва добавляем заголовочные файлы библиотек и макросы. Использовать макросы не рекомендуется для лучшей удобочитаемости кода. Однако они всё таки могут быть полезны. Например можно использовать макросы для простой и быстрой настройки каких либо часто используемых режимов какой либо периферии. Ниже пример макросов для настройки на самые часто используемые режимы GPIO и включение тактирования.
Данные будем брать из UART1 и передавать в порт B а чтобы было видно результат, можно подключить трехцветный светодиод на свободные пины порта B
Порт B используется потому что на порту A работает UART1 и передача данных туда, может помешать работе UART1. Далее, как обычно, настраиваем пины и uart (о том как это делается см. предыдущие уроки по STM32) но только на этот раз дополнительно устанавливаем 6й бит третьего управляющего регистра UART чтобы включить DMA приемник.
USART1->CR3 |= 1<<6;
В используемом микроконтроллере имеется 2 DMA контроллера. У первого есть 7 каналов у второго 5. Каждый канал может проводить только определенные запросы от определенной периферии. Для того чтобы выяснить какая периферия каким каналам соответствует есть две таблицы в мануале. Одна для первого DMA вторая для второго. Т.к. UART1 есть на DMA1 то нас интересует первая таблица. Из неё мы находим нужный нам канал. Это пятый канал первого DMA по тому что на нем uart1 приемник.
DMA1_Channel5
Также ещё важно не забыть включить тактирование DMA1.
RCC->AHBENR |= 1;
В регистр периферии DMA1 передаем адрес регистра данных UART1. В регистр памяти DMA1 передаем адрес порта B. Т.о. обозначив направление передачи данных.
// 2. Указываем адрес периферии (регистр данных UART1)
DMA1_Channel5->CPAR = (uint32_t)&(USART1->DR);
// 3. Указываем адрес назначения (регистр ODR порта GPIOB)
DMA1_Channel5->CMAR = (uint32_t)&(GPIOB->ODR);
В регистр количества пересылок записываем единицу по тому что больше пока не требуется.
DMA1_Channel5->CNDTR = 1;
В конфигурационном регистре устанавливаем только биты 5 для циклического режима т.е. неоднократной передачи и бит 1 для включения DMA1. Остальное установлено по умолчанию так как нужно.
DMA1_Channel5->CCR = 1<<5 | 1;
Основной цикл пуст (при желании его можно заполнить), прерывания тоже не используются. Можно сказать что всю нужную работу, в данном случае, выполняет только DMA и если всё заработает то значит работает DMA. Полный код:
В итоге всё заработало. После загрузки прошивки, визуально светодиод начал реагировать на поступающие на плату символы.
Адрес биткоин кошелька для поддержки блога - bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g







