Всем доброго времени суток! никогда такого не было и вот опять. С моей прошлой статьи прошло уже достаточно времени, а оно ставит новые задачи. И если раньше я передавал данные на скорости 100 Mbps, то теперь пришлось замахнутся на 1600 Mbps…
На КДПВ — герой нашего романа — он смог читать данные на такой скорости!
Итак, мой очередной проект потребовал читать поток данных вида 32-бита на скорости 50 МГц (это и будет, кстати, тот самый 1.6 Gbps) в количестве заранее известном — пусть будет 10000. Было бы просто отлично читать сразу с помощью DMA из одного порта — но вот, к сожалению, подходящих процессоров не нашлось (надеюсь, в комментариях это дело кто-нибудь поправит), у всех подходящих по скорости порты почему-то 16-битные.
Но такая мелочь нас не остановит — будем читать из двух портов сразу! Правда, это в общем случае не всегда удастся делать с нужной степенью контроля и синхронности, но в нашем случае все не так печально — есть сигнал, после которого 20 ns данные будут на порту держаться.
А так как процессор у нас — stm32h750 на 400 Мгц, а шина и таймеры на 200 Мгц, то все должно получиться.
Казалось бы простой случай — по сигналу запускать одну пересылку DMA. Но вот только у DMA нет такой возможности — порт может выдать прерывание, но не может управлять DMA. Но зато у нашего процессора есть хорошая штука — DMAMUX, в котором есть генератор событий для канала DMA, а вот в этом генераторе есть две подходящие возможности — или использовать прерывание EXTIT0 или сигнал от таймера TIM12 (такая вот странная фантазия была у разработчиков чипа).
По прерыванию мы не успеем по времени — на даже пустую отработку нужно порядка 47 тактов, а такт у нас — 2.5 ns…
Зато успеем по таймеру. Осталось только затактировать таймер от внешнего сигнала в 100 МГц, причем длину таймера ставим в 1 и по его выходу TRGO будет срабатывать генератор DMAMUX, а уж он-то задаст команду на пересылку DMA и тот прочитает порт и перешлет данные в память.
Но стоп! Порт-то 16-бит, а у нас 32… Что же, можно попробовать читать еще второй порт… Только вот для этого нужен второй канал DMA, и еще это займет ту же шину — то есть прочитать-то мы успеем, но вот можем не успеть записать данные в память. Что-же, теоретически, у этого процессора есть разные типы памяти, и на большом рисунке структуры процессора можно видеть, что и DMA и память RAM_D1 сидят на одной и той-же шине с частотой 200 МГц. Осталось проверить практически.
DMA1->LIFCR |= ~0;
DMA1_Stream0->CR = (0b11 << DMA_SxCR_PL_Pos) | (0b01 << DMA_SxCR_MSIZE_Pos)
| (0b01 << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC;
DMA1_Stream0->M0AR = (uint32_t) data;
DMA1_Stream0->PAR = (uint32_t) &(GPIOE->IDR);
DMA1_Stream0->NDTR = 10000;
DMA1_Stream1->CR = (0b11 << DMA_SxCR_PL_Pos) | (0b01 << DMA_SxCR_MSIZE_Pos)
| (0b01 << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC;
DMA1_Stream1->M0AR = (uint32_t) data2;
DMA1_Stream1->PAR = (uint32_t) &(GPIOD->IDR);
DMA1_Stream1->NDTR = 10000;
DMAMUX1_Channel0->CCR = DMAMUX_CxCR_EGE | (1);
DMAMUX1_Channel1->CCR = DMAMUX_CxCR_EGE | (2);
DMAMUX1_RequestGenerator0->RGCR = DMAMUX_RGxCR_GE
| (0b01 << DMAMUX_RGxCR_GPOL_Pos) | (7);
DMAMUX1_RequestGenerator1->RGCR = DMAMUX_RGxCR_GE
| (0b01 << DMAMUX_RGxCR_GPOL_Pos) | (7);
DMA1_Stream0->CR |= DMA_SxCR_EN;
DMA1_Stream1->CR |= DMA_SxCR_EN;
TIM12->CNT = 0;
TIM12->CCMR1 |= TIM_CCMR1_CC2S_0;
TIM12->CR2 = (0b010 << TIM_CR2_MMS_Pos);
TIM12->CR1 |= TIM_CR1_CEN;
while (DMA1_Stream0->NDTR)
i++;
TIM12->CR1 &= ~TIM_CR1_CEN;
__attribute__((section(".dma_buffer"))) uint16_t data[10240],data2[10240];
.dma_buffer : {
*(.dma_buffer)
} >RAM_D1
uint16_t * ptr = cpudata;
volatile uint16_t * src = &(GPIOE->IDR);
volatile uint16_t * src2 = &(GPIOD->IDR);
for (register int i = 0; i < 10000; i++) {
*ptr++ = *src;
*ptr++ = *src2;
}
К сожалению, не доступен сервер mySQL