В соседний статье мы познакомились с основными понятиями и принципами работы 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. На плате соединим два модуля 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, указатель на массив данных для передачи, размер передаваемых/принимаемых данных.