Материал из настоящей статьи, относящийся к микросхемам К1986ВЕ92QI и К1986ВЕ1QI, распространяется в том числе на микроконтроллеры К1986ВЕ92FI, К1986ВЕ92F1I, К1986ВЕ94GI и К1986ВЕ1FI, К1986ВЕ1GI
DMA (Direct Memory Access) - специализированный контроллер, предназначенный для пересылки данных без использования ядра микроконтроллера. Использование DMA позволяет не тратить процессорное время на отслеживание готовности данных в периферийных блоках и последующую их пересылку. Ядро инициализирует DMA и переходит к выполнению других задач, пока не возникнет прерывание от DMA, сигнализирующее о том, что заданное количество данных было передано.
Как и в любой функции копирования данных для запуска обмена с использованием DMA необходимы следующие параметры:
- адреса источника и приёмника;
- количество передаваемых данных (цикл DMA);
- размерность передаваемых данных;
- инкремент адресов источника и приёмника после передачи каждого значения.
После передачи очередной порции данных, адреса источника и приемника данных могут либо инкрементироваться, либо оставаться неизменными. Инкремент необходим, когда передается/принимается массив данных. Неизменный адрес необходим при считывании/записи в регистр, например, когда выбран адрес регистра данных АПЦ или ЦАП.
Пересылаемые данные могут быть:
- 8-разрядные (байт);
-
16-разрядные (полуслово);
-
32-разрядные (слово).
1. Каналы DMA
Для выполнения нескольких «параллельных» задач по пересылке данных в контроллере DMA реализовано 32 независимых канала. Одновременно активен только один канал. Каждый канал настраивается отдельно на выполнение конкретной задачи. Таким образом, возможно одновременно запустить и копирование массивов, и считывание данных из АЦП, и запись данных в ЦАП и многое другое. Все это будет работать как бы "параллельно".
Для реализации совместной работы нескольких каналов в контроллере DMA предусмотрен механизм смены текущего канала при исполнении цикла обмена - арбитраж. Активный канал при арбитраже меняется не тогда, когда текущий канал полностью закончил свой цикл обмена, т.е. выполнит все заданные передачи, а внутри цикла. Для этого в настройках канала используется параметр
R_power - количество передач до процедуры арбитража. Значение этого параметра является степенью 2, т.е. возможны 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 и 1024 непрерывные передачи, которые не могут прерваться процедурой арбитража.
Например, необходимо передать 100 значений из массива в массив. Если выставить параметр
R_power = 6 (2
6 = 64), то канал передаст 64-е значения и разрешит провести арбитраж. Далее:
- если нет запросов от более приоритетных каналов, то текущий канал закончит передачу оставшихся 36-х значений. После этого канал разрешит арбитраж и вызовет аппаратное прерывание от DMA - завершение цикла DMA;
-
если есть запросы от более приоритетных каналов, то DMA обработает эти приоритетные каналы. Канал, настроенный на передачу 100 значений, выставит запрос на обработку и будет ожидать арбитраж, в ходе которого DMA вернется к исполнению канала и будут переданы оставшиеся 36 значений.
Канал номер 0 имеет высший уровень приоритета, при этом уровень приоритета снижается с увеличением номера канала. Задавая параметр
R_power необходимо учитывать, что слишком большое значение ускоряет исполнение одного канала в ущерб исполнению других.
1.1 Источники и приемники данных
В отличие от простого цикла копирования, DMA может не просто выполнять пересылку данных, а делать это по определённому событию.
Например, можно настроить, чтобы одно слово данных копировалось из адреса источника по событию готовности АЦП. Тогда, если адресом источника выбрать регистр "ADC_RESULT", то DMA может без участия ядра считывать готовые значения из АЦП и складывать их, например, в массив. Затем, когда DMA закончит передавать заданное количество данных (цикл DMA), будет сгенерировано прерывание от DMA, и ядро может обработать весь массив полученных данных от АЦП. Отличие от обычного копирования здесь в том, что измерение АЦП занимает некоторое количество времени, поэтому считывать результат с АЦП нужно только по событию готовности.
Аналогично, для контроллера ЦАП, адресом назначения DMA выбирается регистр "DAC_DATA", а событием, по которому происходит передача слова, выбирается событие таймера CNT == ARR (запрос к DMA по событию совпадения счётчика CNT и основания счёта ARR). Тогда с помощью DMA получится равномерный (по таймеру) вывод сигнала в ЦАП. Форма сигнала для вывода в ЦАП в данном случае обычно прописывается массивом, который выбирается в качестве источника данных для DMA. Размер массива указывается в параметре - "количество передаваемых данных". По окончанию передачи массива возникает прерывание от DMA - окончание цикла DMA.
1.2 Запросы к DMA от периферийных устройств
Передача одного значения называется одиночной передачей, а передача 2
R_power данных - блочной передачей.
Периферийные блоки могут запрашивать блочные и одиночные передачи, используя внутренние сигналы
dma_req[С] и
dma_sreq[С]. С - номер канала, т то есть каждый канал имеет отдельные сигналы от периферии.
dma_req[C] - запрос на блочную передачу DMA. Когда от периферии приходит запрос dma_req[С], контроллер DMA выполняет 2
R_power передач, далее контроллер DMA ожидает новый запрос dma_req[С] или dma_sreq[С] от периферии. Если в цикле DMA осталось выполнить меньше количество передач, чем 2
R_power, то выполняется оставшееся количество передач, чтобы закончить цикл DMA.
dma_sreq[C] - запрос на одиночную передачу DMA. Когда от периферии приходит запрос dma_sreq, контроллер DMA выполняет 1 передачу, далее контроллер DMA ожидает новый запрос dma_sreq[С] от периферии.
Обработку одиночных запросов от dma_sreq[C] можно отключить/включить - для этого используются регистры CHNL_USEBURST_SET / CHNL_USEBURST_CLR. При выключенной обработке одиночных запросов от dma_sreq[C] будут обрабатываться только запросы от dma_req[C].
В МК К1986ВЕ92QI, К1986ВЕ1QI, К1986ВК234, К1986ВК025 и MDR1206FI сигналы dma_req[С] и dma_sreq[С] от определенных периферийных контроллеров жестко привязаны к каналам DMA. Например, подключение сигналов запросов dma_req[С] и dma_sreq[С] от периферии к каналам DMA для микроконтроллера К1986ВЕ92QI приведено в таблице 1.
Таблица 1 – Распределение запросов от периферийных контроллеров по каналам DMA
Номер канала
|
Источник dma_sreq
|
Источник dma_req
|
0
|
UART1_TX
|
UART1_TX
|
1
|
UART1_RX
|
UART1_RX
|
2
|
UART2_TX
|
UART2_TX
|
3
|
UART2_RX
|
UART2_RX
|
4
|
SSP1_TX
|
SSP1_TX
|
5
|
SSP1_RX
|
SSP1_RX
|
6
|
SSP2_RX
|
SSP2_TX
|
7
|
SSP2_RX
|
SSP2_RX
|
8
|
ADC1_EC
|
-
|
9
|
ADC2_EC
|
-
|
10
|
TIMER1
|
-
|
11
|
TIMER2
|
-
|
12
|
TIMER3
|
-
|
13
|
-
|
-
|
…
|
-
|
-
|
31
|
-
|
-
|
Каналы 13 - 31 являются программными, поэтому для установления запроса на выполнение передачи должен быть установлен соответствующий каналу бит в регистре CHNL_SW_REQUEST. Необходимо отметить, что запрос на передачу аппаратного канала также можно выставить программный путём с помощью регистра CHNL_SW_REQUEST.
У каждого канала есть свой приоритет, и он определяется индексом канала. 0-ой канал имеет самый высокий приоритет, далее приоритет следует в порядке убывания. Если посмотреть распределение каналов DMA для К1986ВЕ92QI, то самый приоритетный - это UART1_TX. Приоритет по умолчанию можно увеличить, задав каналу высокий приоритет. Для задания высокого приоритета используется регистр CHNL_PRIORITY_SET, в который необходимо записать "1" в бит по индексу канала. Для возвращения приоритета в обычное состояние (низкий приоритет), используется регистр CHNL_PRIORITY_CLR - запись "1" в бит по индексу канала. Чтение регистра CHNL_PRIORITY_SET возвращает текущее состояние приоритета канала: 0 - приоритет по умолчанию (низкий приоритет), 1 – высокий приоритет.
В МК К1986ВК01GI сигналы запросов dma_sreq[C] и dma_req[C] от периферийных устройств могут мультиплексироваться между каналами, что позволяет гибко настроить приоритет работы DMA с периферийными контроллерами.
1.3 Арбитраж каналов DMA
На рисунке 1 показана структурная схема работы каналов DMA с осуществлением арбитража.
Рисунок 1 – Структурная схема работы каналов DMA с осуществлением арбитража
Пояснения к рисунку 1:
- всего есть 32 канала DMA;
-
приоритет каналов убывает от 0-го канала к 31-му;
-
каналы передают данные блоками по 2R_power, между которыми осуществляется арбитраж - смена канала;
-
DMA передача - это передача одного слова данных заданной размерности;
-
цикл DMA - это общее количество данных, которое необходимо передать. Задается параметром N_minus_1;
-
для передачи следующей порции данных необходим запрос на обслуживание канала DMA - DMA Request. По этой причине более приоритетный канал С-1 отдает управление каналу С – так как запрос к С-1 еще не установлен к моменту арбитража. Поэтому исполнение переходит к каналу С, и только на следующем арбитраже канал С-1 передаст оставшееся слово;
-
под DMA Request подразумевается запрос по сигналам sreq[C], req[C] или программный запрос;
-
каждый канал управляется своей структурой, состоящей из 4-х 32-битных слов;
-
последнее слово резервное и не используется;
-
два слова в структуре задают адрес источника и приемника данных;
-
все настройки обмена задаются в слове Control:
- общее количество передач задается полем N_minus_1 при запуске. Далее это значение используется для вычисления текущего адреса передаваемых данных и уменьшается контроллером DMA при передаче каждого слова;
- младшие 3 бита Control задают режим работы канала. По окончанию цикла DMA это поле сбрасывается контроллером DMA в 0, что означает режим STOP - канал работу закончил.
1.4 Управляющие данные канала DMA
Каждый канал управляется своей структурой, которая состоит из 4-х 32-разрядных слов:
- Source End Pointer - конечный адрес источника данных;
-
Destination End Pointer - конечный адрес приемника данных;
-
Control - параметры обмена;
-
Reserved - не используется.
Параметры цикла DMA канала находятся в слове
Control. Это слово меняется в процессе работы канала, и по этому слову можно узнать, сколько осталось данных для передачи и закончил ли работу канал. Именно это слово должно быть восстановлено начальными значениями при перезапуске цикла DMA. В таблице 2 приведено описание слова Control.
Таблица 2 – Описание полей слова Control
Биты
|
Поле
|
Значения
|
Описание
|
31..30
|
dst_inc
|
b00 - байт
b01 - полуслово
b10 - слово
b11 - ноль/нет инкремента
|
Инкремент адреса приемника
|
29..28
|
dst_size
|
Размерность данных приемника, всегда равна src_size!
|
27..26
|
src_inc
|
Инкремент адреса источника
|
25..24
|
src_size
|
Размерность данных источника
|
23..21
|
dst_prot_ctrl
|
b001 - Привилегированый доступ
b010 - Буферизируется
b100 - Кэшируется
|
Флаги привилегий приемника
|
20..18
|
src_prot_ctrl
|
Флаги привилегий источника
|
17..14
|
R_power
|
b000 - 1 передача
|
Передач до арбитража
|
b001 - 2 передачи
|
b010 - 4 передачи
|
…
|
b1010 - 1024 передачи
|
13..4
|
N_minus_1
|
0 - 1024
|
Передач в цикле DMA
|
3
|
next_useburst
|
b0 - sreq и req
|
Переопределение CHNL_USEBURST_SET[C] в режиме
с изменением конфигурации
|
b1 - req
|
2..0
|
cycle_ctrl
|
b000 - STOP
|
Режим работы
|
b001 - Основной
|
b010 - Авто-запрос
|
b011 - Пинг-понг
|
b100/b101 - Реконфигурация с памятью
|
b110/b111 - Реконфигурация с периферией
|
Контроллер DMA вычисляет адрес текущей передачи из конечного адреса по следующей формуле:
Addr = data_end_ptr - (n_minus_1 << src_inc), где src_inc - инкремент данных источника
Размерность данных приемника всегда должна быть равна размерности данных источника: src_size == dst_size
Управляющие структуры каналов должны быть расположены в памяти друг за другом и представлять собой массив структур. Когда DMA будет обрабатывать канал, например, 5-й, то он будет считывать и изменять 5-ю структуру в массиве. Указатель на начало массива структур должен быть прописан в регистре CTRL_BASE_PTR.
При использовании SPL этот массив структур уже определен в файле «MDR32F9Qx_dma.c» как массив структур DMA_ControlTable[64]. Массив DMA_ControlTable[64] имеет вдвое большую длину - 64 структуры, а каналов всего 32. Вторая половина (с 32-го индекса) является альтернативным массивом структур, и адрес этого массива можно считать в регистре ALT_CTRL_BASE_PTR. Регистр ALT_CTRL_BASE_PTR доступен только на чтение и устанавливается автоматически при задании регистра CTRL_BASE_PTR.
Альтернативная структура может использоваться, например, в режиме работы ping-pong. В этом режиме по окончании цикла основной структуры DMA переключится на исполнение альтернативной структуры, затем, по окончании цикла альтернативной структуры DMA снова перейдёт к выполнению основной структуры и так далее. Получается, что один и тот же канал работает то с одной управляющей структурой, то с другой. По окончании цикла каждой структуры будет вызвано прерывание, и пока DMA выполняет обмен по текущей структуре, необходимо заново проинициализировать следующую структуру. Такой подход позволяет организовать непрерывную пересылку данных с помощью DMA с минимальными затратами процессорного времени.
Режим ping-pong может пригодиться, например, для вывода сигнала в ЦАП. Пока DMA выводит сигнал по основной структуре, ядро высчитывает и заполняет данные сигнала для альтернативной структуры. По завершении цикла DMA переключится на альтернативную структуру, и ядро может заполнять сигнал для основной структуры и так далее.
2. Регистры блока DMA
Полное описание регистров представлено в спецификации. В данной статье будут рассмотрены только основные моменты работы с регистрами DMA. Особое внимание следует уделить параметрам ReadOnly (RO) и WriteOnly (WO). Далее по тексту ниже, где атрибуты не указаны, доступ разрешен и на запись, и на чтение - Read-Write (RW).
Доступ к большинству регистров блока DMA реализованы с помощью дополнительных регистров с приставками SET и CLR, которые позволяют осуществлять установку и сброс отдельных бит в основном регистре. Запись нулей в регистры SET и CLR не влияют на состояние регистра.
- Регистр SET: - RW, при записи 1-ниц устанавливаются биты в основном регистре;
- Регистр CLR: - RO, при записи 1-ниц сбрасываются биты в основном регистре.
Перечень регистров DMA приведён в таблице 3. В таблице указано только название основного регистра, обозначением (S/C) показано, что доступ обеспечивается через регистры-маски SET и CLR.
Таблица 3 - Перечень регистров контроллера DMA
Название регистра
|
Описание
|
STATUS
|
Отражает состояние контроллера, RO
|
CFG
|
Бит Master_enable - включает работу блока DMA. Но весь регистр WO, при чтении возвращается 0.
|
CTRL_BASE_PTR
|
Задает указатель на массив первичных и вторичных управляющих структур каналов.
|
ALT_CTRL_BASE_PTR
|
Возвращает указатель на массив альтернативных управляющих структур каналов, RO. Значение высчитывается из CTRL_BASE_PTR и количества каналов в блоке DMA.
|
WAITONREG_STATUS
|
Состояние сигналов запроса на обработку к каналам DMA, RO
|
CHNL_SW_REQUEST
|
Запись 1 в бит канала вызывает программный запрос на обработку канала, WO.
|
CHNL_USEBURST (S/C)
|
Бит канала: 0 - обработка запросов sreq и req, 1 - только req.
|
CHNL_REQ_MASK (S/C)
|
Бит канала: 0 - работа канала разрешена, 1 - работа канала запрещена.
|
CHNL_ENABLE (S/C)
|
Запись 1 в бит канала запускает работу канала. После окончания цикла, бит аппаратно сбрасывается в 0.
|
CHNL_PRI_ALT (S/C)
|
Бит канала: 0 - канал использует первичную структуру, 1 - альтернативную. Можно указать вручную. В режимах работы, использующих обе структуры, бит переключается аппаратно.
|
CHNL_PRIORITY (S/C)
|
Бит канала: 0 - обычный приоритет, 1 - повышенный.
|
ERR_CLR
|
Бит 0: Возвращает наличие ошибки при работе DMA. Запись 1 сбрасывает ошибку.
|
3. Пример программного копирования данных в FIFO контроллера Ethernet
В данном пункте приводится пример работы с DMA из библиотечного файла «MDR32FxQI_eth.c». В данном примере пакет данных с помощью DMA записывается в FIFO контроллера Ethernet.
3.1 Инициализация DMA
Перед использованием DMA необходимо его предварительно настроить. Функция предварительной инициализации DMA приведена в фрагменте кода 1. В данной функции задаются только основные параметры DMA. Далее, при вызове функции обмена параметры управляющей структуры перенастраиваются. Для пересылки данных используется 15 канал DMA - DMA_Channel_SW2, запросы выставляются программным путём с помощью регистра CHNL_SW_REQUEST.
Фрагмент кода 1 – Функция предварительной инициализации контроллера DMA
#define DMA_Channel_SW2 ((uint8_t)(15))
void ETH_DMAPrepare(void)
{
// Локальная управляющая структура канала DMA
DMA_CtrlDataInitTypeDef DMA_PriCtrlStr;
// Структура инициализации DMA
DMA_ChannelInitTypeDef DMA_InitStr;
// Подача тактирования на DMA
RST_CLK_PCLKcmd(RST_CLK_PCLK_DMA, ENABLE);
// Сброс настроек DMA
DMA_DeInit();
// Инициализация управляющей структуры по умолчанию
DMA_StructInit(&DMA_InitStr);
// Настройка DMA - выбор основной управляющей структуры
DMA_InitStr.DMA_PriCtrlData = &DMA_PriCtrlStr;
DMA_InitStr.DMA_Priority = DMA_Priority_High;
DMA_InitStr.DMA_UseBurst = DMA_BurstClear;
DMA_InitStr.DMA_SelectDataStructure = DMA_CTRL_DATA_PRIMARY;
// Инициализация DMA
DMA_Init(DMA_Channel_SW2, &DMA_InitStr);
}
3.2 Функция записи в FIFO через DMA
Адрес FIFO (адрес назначения), как и адрес массива данных (источника), задается вызывающим кодом вместе с количеством передаваемых данных. Функция записи в FIFO контроллера Ethernet приведена во фрагменте кода 2, далее приведена дополнительная информация к коду:
- запись в FIFO осуществляется всегда по одному адресу, поэтому для приемника не задается смещение адреса;
-
для обмена выбран канал 15;
-
для того чтобы передать весь пакет максимально быстро повышается приоритет данного канала и значение R_power выбирается максимальное - арбитраж через 1024 передачи. Размер же пакета может быть меньше данного значения, но не больше (контролируется вызывающим кодом);
-
выставляется режим Авто-запроса для автоматического формирования запросов передачи данных к DMA;
-
прерывание от DMA не используется, поскольку так организован вызывающий код.
Фрагмент кода 2 – Функция записи в FIFO контроллера Ethernet с использованием DMA
#define DMA_Channel_SW2 ((uint8_t)(15))
// Доступ к глобальному массиву управляющих данных каналов
extern DMA_CtrlDataTypeDef DMA_ControlTable[DMA_Channels_Number * (1 + DMA_AlternateData)];
void ETH_DMAFrameTx(uint32_t * DstBuf, uint32_t BufferSize, uint32_t * SrcBuf)
{
__IO uint32_t * ptrControltable;
uint32_t tmpval;
// Высший приоритет
MDR_DMA->CHNL_PRIORITY_SET |= 1 << DMA_Channel_SW2;
// Настройка управляющей структуры
DMA_ControlTable[DMA_Channel_SW2].DMA_SourceEndAddr = (uint32_t)SrcBuf + 4*(BufferSize-1);
DMA_ControlTable[DMA_Channel_SW2].DMA_DestEndAddr = (uint32_t)DstBuf;
DMA_ControlTable[DMA_Channel_SW2].DMA_Control = DMA_DestIncNo | DMA_SourceIncWord |
DMA_MemoryDataSize_Word |
DMA_Mode_AutoRequest |
DMA_Transfers_1024 |
((BufferSize - 1) << 4);
// Разрешение работы канала - DMA ожидает запрос для запуска
DMA_Cmd(DMA_Channel_SW2, ENABLE); // MDR_DMA->CHNL_ENABLE_SET = (1 << DMA_Channel);
// Подача DMA Request - старт цикла DMA
DMA_Request(DMA_Channel_SW2); // MDR_DMA->CHNL_SW_REQUEST = (1 << DMA_Channel);
// Получение указателя на поле Control канала
ptrControltable = (uint32_t *) &DMA_ControlTable[DMA_Channel_SW2].DMA_Control;
// Ожидание окончания цикла
while( 1 )
{
tmpval = (*ptrControltable) & 0x7;
if(tmpval == 0) // режим STOP?
break;
}
// Выключение канала DMA
DMA_Cmd(DMA_Channel_SW2, DISABLE); // MDR_DMA->CHNL_ENABLE_CLR = (1 << DMA_Channel);
}
4. Работа с DMA в К1986ВЕ1QI
Следует обратить внимание, что в микроконтроллере К1986ВЕ1QI не вся память ОЗУ доступна для контроллера DMA, а только AHB-Lite SRAM 16KB, начинающаяся с адреса 0x2010_0000 (спецификация, раздел «Организация памяти»). Это необходимо учитывать при выборе источника и приёмника, а также при размещении массива управляющих структур.
В SPL массив управляющих структур DMA_ControlTable[] объявлен с атрибутом __attribute__((section("EXECUTABLE_MEMORY_SECTION"))), позволяющим разместить данный массив в определённую область памяти МК. Данный атрибут необходимо указать в конфигурационном файле компоновщика для соответствующего региона памяти.
Например, в среде Keil конфигурационный файл компоновщика (scatter file) автоматически создается при сборке проекта, если в опциях проекта установлена галочка «Linker -> Use Memory Layout from Target Dialog». Конфигурационный файл создается в поддиректории «Objects» и называется именем проекта с расширением *.sct. Для работы с DMA необходимо убрать галочку с опции «Use Memory Layout from Target Dialog», чтобы данный файл не обновлялся при каждой сборке (рисунок 2), а также в сам файл дописать строку с указанием атрибута массива управляющих структур для региона памяти ОЗУ AHB-Lite SRAM 16KB, как показано в фрагменте кода 3.
Фрагмент кода 3 – Размещение структуры с атрибутом "EXECUTABLE_MEMORY_SECTION" в регион памяти IRAM2
RW_IRAM2 0x20100000 0x00004000 {
*.o (EXECUTABLE_MEMORY_SECTION)
.ANY (+RW +ZI)
}
Рисунок 2 - Настройка конфигурационного файла компоновщика в опциях проекта
Подробная информация про конфигурационный файл компоновщика приведена на
официальном сайте Keil, а также в статье «
[i] Расположение функций в ОЗУ, программирование EEPROM».
5. Первоначальная инициализация DMA для работы с прерываниями
После включения МК некоторые периферийные блоки могут выставить запросы к DMA. При этом, если канал DMA, к которому установлен запрос, будет выключен, то контроллер DMA оповестит об этом ядро путём выставления запроса прерывания (спецификация, правила осуществления DMA передач при «запрещенных» каналах, правило №19). Таким образом DMA показывает запросы от периферии, которые поступают на выключенные каналы. Если данный механизм оповещения не требуется в проекте, то его можно отключить. Для этого необходимо:
- запретить обработку запросов по dma_req[C] и dma_sreq[C] во всех каналах DMA, кроме требуемых, установив соответствующие биты в регистре «CHNL_REQ_MASK_SET»;
- разрешить работу всех каналов DMA, установив все биты в регистре «CHNL_ENABLE_SET»;
- разрешить работу контроллера DMA путём установки бита «master_enable» в регистре «DMA_CFG»;
-
сбросить отложенное прерывание от DMA с помощью вызова функции NVIC_ClearPendingIRQ(DMA_IRQn).