Инструменты пользователя

Инструменты сайта


prog:spec:dma_req

DMA + периферийное устройство

В соседний статье мы познакомились с основными понятиями и принципами работы DMA в процессорах цифровой обработки сигналов серии 1967ВНхх. Теперь рассмотрим работу связки блока прямого доступа к памяти с использованием периферийного устройства. В качестве последнего будем использовать link port. В случае с 28 –ым процессором это единственный модуль периферии, и, следовательно, запросы к dma при использовании каналов на передачу (4-7) и каналов на приём (8-11), выставляет только он. С 44 ым процессором периферия сильно расширена. И здесь уже необходимо разобраться, какое устройство выставляет запрос к DMA. Попробуем это сделать на примере работы с всё тем же link- портом на процессоре 1967ВН044.

Связь каналов и запросов

В процессоре 1967ВН044 конкретному каналу DMA на приём/передачу указывается источник запроса. Для этого введены два специальных регистра DMACFGL и DMACFGH.

Номер канала Биты DMACFGx
0 H[16].L[3:0]
1 H[17].L[7:4]
2 H[18].L[11:8]
3 H[19].L[15:12]
4 H[20].L[19:16]
5 H[21].L[23:20]
6 H[22].L[27:24]
7 H[23].L[31:28]
8 H[24].H[3:0]
9 H[25].H[7:4]
10 H[26].H[11:8]
11 H[27].H[15:12]

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

Таким образом, к примеру, чтобы нам присвоить 4-му каналу источник запроса SPI0, нам необходимо записать номер устройства (запроса) spi0 (в соответствии с таблицей ниже - это 3) в биты [19:16] = 011 и регистра DMACFGL и обнулить 20 бит регистра DMACFGH. Таблицы с номерами устройств в документации разделены на две: одна описывает устройства приёмники, другая устройства передатчики. Мы же объединим это всё в одну таблицу, поскольку номер у каждого устройства для DMA совпадает, разница лишь в том работает оно на приём или передачу. Поэтому раздельные таблицы можно найти в документации.

Номер устройства (запроса) Обслуживаемый запрос
0 Определяется номером канала
0 – nDMAR0
1 – nDMAR1
2 – nDMAR2
3 – nDMAR3
4 - LINK 0
5 - LINK 1
6 -
7 -
8 - LINK 0
9 - LINK 1
10 -
11 -
1 UART_1
2 UART_2
3 SPI0
4 LCD/видеокамера
5 SSI0
6 SSI1
7 NANDF
8 UP/DOWN0
9 UP/DOWN1
10 UP/DOWN2
11 UP/DOWN3
12 SPI1
13 SPI2
14 H264_RQ1
15 H264_RQ0
16 Внешний запрос nDMAR[4] (каналы 4-7) или NDMAR[8] (каналы 8-11)
17 Внешний запрос nDMAR[5] (каналы 4-7) или NDMAR[9] (каналы 8-11)
18 Внешний запрос nDMAR[6] (каналы 4-7) или NDMAR[10] (каналы 8-11)
19 Внешний запрос nDMAR[7] (каналы 4-7) или NDMAR[11] (каналы 8-11)
20 Запрос 0 от таймера 0 ШИМ
21 Запрос 1 от таймера 0 ШИМ
22 Запрос 2 от таймера 0 ШИМ
23 Запрос 3 от таймера 0 ШИМ
24 Запрос 4 от таймера 0 ШИМ
25 Запрос 0 от таймера 1 ШИМ
26 Запрос 1 от таймера 1 ШИМ
27 Запрос 2 от таймера 1 ШИМ
28 Запрос 3 от таймера 1 ШИМ
29 Запрос 4 от таймера 1 ШИМ
30 Запрос от таймера 0 контроллера прерываний
31 Запрос от таймера 1 контроллера прерываний

Итак присвоение источника запросу конкретному каналу в HAL делается с помощью следующий функции HAL_DMA_RqstSet (…)

void HAL_DMA_RqstSet( uint32_t ch_number, DMA_Requester_type dmaRqster )
{
	if( ch_number < 8 )
	{
		*( uint32_t * ) base_DMACFGL &= ~( 0xF << ( ch_number * 4 ) );
		*( uint32_t * ) base_DMACFGH &= ~( 0x10000 << ch_number );
		*( uint32_t * ) base_DMACFGL |= ( ( dmaRqster & 0xF ) << ( ch_number * 4 ) );
		*( uint32_t * ) base_DMACFGH |= ( ( ( dmaRqster & 0x10 ) << 12 ) << ch_number );
	}
	else if( ch_number <= 11 )
	{
		*( uint32_t * ) base_DMACFGH &= ~( 0x10000 << ch_number );
		*( uint32_t * ) base_DMACFGH |= ( ( ( dmaRqster & 0x10 ) << 12 ) << ch_number );
		ch_number *= 4;
		ch_number &= 0x1F;
		*( uint32_t * ) base_DMACFGH &= ~( 0xF << ch_number );
		*( uint32_t * ) base_DMACFGH |= ( ( dmaRqster & 0xF ) << ch_number );
	}
}

Рассмотрим пример передачи данных по link порту с использованием dma. Листинг кода приводить не будем, пример доступен в описании к библиотеке HAL. Остановимся только на важных моментах. Стоит отметить то, что сейчас мы будем работать с dma по принципу передачи внутренняя память→ периферия и согласно документации и таблице, для этого мы должны использовать передающие каналы DMA 4-7 и заполнять квадрослово TCB только передающей структуры DMA:

tcbTx[0] = (unsigned int) data_tx32;
tcbTx[1] = (DATA_SIZE<<16) | 4;
tcbTx[2] = 0;
tcbTx[3] = TCB_INTMEM | TCB_QUAD;

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

#define TCB_QUAD (0x06000000)

В качестве приёмника данных от DMA у нас выступает передатчик link-порта. Соответственно его мы связываем с нашим каналом с помощью регистров DMACFGx, как показано пунктом выше. Итак после того как мы записали соответствующие значения в DMACFGx, затем сразу после управляющего квадрослова TCB, DMA сразу начнёт отправлять данные по link-порту. Получается, напрашивается вывод о том, что периферия выставляет запросы к DMA по классическому сценарию: пустота передатчика или наличие данных в приёмнике.

Выставление запросов к DMA от периферии инициирует пустота передатчика или наличие данных в приёмнике.

Пример отправки данных по SPI с использованием DMA

Попробую теперь воспользоваться dma для отправки и приёма данных по SPI. На плате соединим два модуля SPI на отладочной плате. Плата, как известно, состоит из отладочного модуля и модуля процессора. У нас на руках оказалось:

"Модуль отладочный для микросхемы 1967ВН044" - версия 2.4.1

"Модуль процессора для микросхемы 1967ВН044" - версия 2.2

Мы соединяем SPI2 в режиме Master и SPI0 в режиме Slave следующим образом:

Master Slave
SPI2_CS ←–> SPI0_CS1
SPI2_DI ←–> SPI0_DI
SPI2_DO ←–> SPI0_DO
SPI2_CLK ←–> SPI0_CLK

Отметим пару моментов:

1) Согласно спецификации, в режиме Slave выходом является вывод SPI_DI.

2) У SPI0 доступна до 6 адресуемых устройств в режиме ведущий, на плате CS0 занят микросхемой Flash, поэтому используем CS1.

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

void HAL_SPI_DMA_SendAndReceive( SPI_type *SPI, uint32_t ulChannelRx, uint32_t *pusBuffRx, uint32_t ulChannelTx, uint32_t *pusBuffTx, uint16_t usSize )
{...}

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

prog/spec/dma_req.txt · Последнее изменение: 2022/04/03 23:09 (внешнее изменение)