======DMA + периферийное устройство====== В [[prog:spec: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 будет ждать запроса. Особенность в том что старший бит для каждого канала вынесен отдельно от основного поля. Ниже приведём картинку с разбиением полей данных регистров. {{:dsp:tests:dma:dmacfg.png}} Таким образом, к примеру, чтобы нам присвоить 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===== Рассмотрим пример передачи данных по link порту с использованием dma. Листинг кода приводить не будем, пример доступен в описании к библиотеке HAL. Остановимся только на важных моментах. Стоит отметить то, что сейчас мы будем работать с dma по принципу передачи внутренняя память-> периферия и согласно документации и [[prog:spec: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. {{:dsp:tests:dma:spi_rx_tx.png}} {{:dsp:tests:dma:master.png}} {{:dsp:tests:dma:slave.png}} Пример проекта доступен в 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, указатель на массив данных для передачи, размер передаваемых/принимаемых данных.