Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

EFM32 / EFR32 вопросы про LDMA

Ср окт 27, 2021 22:10:15

Не будет ли многоуважаемое сообщество дать мне пинка в нужном направлении, чтобы разобраться, как им пользоваться.

Простенький трансфер я делать научился: из памяти в регистр и обратно байтики пересылаю без проблем по запросу устройства.
А теперь хочу пересылать данные по таймеру. Но у меня ничего не получается.

Т.е. приходит сигнал от таймера по переполнению, и мне надо послать одно 16-битное слово.

Спойлер
Код:
volatile uint16_t videobuff[4];

LDMA_TransferCfg_t dispXferCfg = LDMA_TRANSFER_CFG_PERIPHERAL(ldmaPeripheralSignal_TIMER0_UFOF);

LDMA_Descriptor_t
  disp_descriptor 
= LDMA_DESCRIPTOR_SINGLE_M2P_BYTE(videobuff, &(USART0->TXDOUBLE), sizeof(videobuff)/sizeof(videobuff[0]) );

void InitTransfer(void) {

// USART0 сконфигурирован...

  TIMER0->CFG = TIMER_CFG_MODE_UP | TIMER_CFG_PRESC_DIV2 |
                      TIMER_CFG_SYNC_DISABLE | TIMER_CFG_DISSYNCOUT_EN ;

  TIMER_DEVICE->EN = TIMER_EN_EN;
  timer_freq = CMU_ClockFreqGet(cmuCLOCK_TIMER0);
  divider = (usart_freq/(* 4* 125)) - 1; // 500us

  TIMER0->TOP = divider;

  // Enable LDMA clock
  CMU_ClockEnable(cmuClock_LDMA, true);
  LDMA_Init_t init = LDMA_INIT_DEFAULT;

  // Initialize LDMA
  LDMA_Init(&init);
  // Enable LDMA Interrupt
  NVIC_ClearPendingIRQ(LDMA_IRQn);
  NVIC_SetPriority(LDMA_IRQn, 3);
  NVIC_EnableIRQ(LDMA_IRQn);

  
disp_descriptor.xfer.size ldmaCtrlSizeHalf;
  
disp_descriptor.xfer.blockSize 1;


  LDMA_StartTransfer(DYNAMIC_DISPLAY_LDMA_CH, &dispXferCfg, &disp_descriptor);
  TIMER_DEVICE->CMD = TIMER_CMD_START;
}

И ничего не происходит. Если я включаю прерывание от таймера по переполнению и делаю посылку в прерывании - всё работает (т.е. предполагаю USART и TIMER сконфигурированы правильно. А вот подключая LDMA - ничего нет

Re: EFM32 / EFR32 вопросы про LDMA

Пт окт 29, 2021 09:52:37

Итак, после недельного слепого тыканья по даташитам и тырнету задачка решена.

Проблемы были следующие:
Как пересылать 16 бит слова, если нет темплейта дескриптора для Half, а есть только для Byte?
Надо не стесняться менять поля. Т.е. берется темплейт LDMA_Descriptor_t disp_descriptor = LDMA_DESCRIPTOR_SINGLE_M2P_BYTE() и потом делаем его модификацию
Код:
  disp_descriptor.xfer.size = ldmaCtrlSizeHalf;
Теперь обмен будет не байтами, а 16 битными словами.

Как зациклить трансфер?
Есть, конечно, в темплейте поле loopCount, но это даёт только конечное число повторов. это не то. Во втором воркшопе в прерывании по окончании трансфера просто всё переинициализировалось по новой - можно, но не изящно. Правильное решение - использовать букву "L" в слове LDMA - Linked. Этот DMA можно связать с другим трансфером, т.е. по окончании этого трансфера начинается другой, который может быть либо следующий в списке, либо еще где. Тут же оказалось, можно указать следующим самого себя используя Relative Link и этот Relative указать как 0.
Код:
LDMA_Descriptor_t
  disp_descriptor = LDMA_DESCRIPTOR_LINKREL_M2P_BYTE(videobuff, // откуда передавать данные
                                       &(USART_DEVICE->TXDOUBLE),     // куда передавать данные
                                       sizeof(videobuff)/sizeof(videobuff[0]),    // сколько передавать данных
                                       0);   // относительный указатель на следующий дескриптор

И кстати, если не нужно, чтобы по окончании каждого трансфера в данном случае вызывалось прерывание, в дескрипторе поле doneIfs можно обнулить.

Как передать один трансфер за один запрос?
С этим я долго боролся, но решение оказалось простым. То что у меня запрос DMA делал таймер - это правильно. Но, если таймер делает запрос, то он не будет снят, пока не будет прочитан какой-нибудь регистр того же таймера! А так как передача идёт из памяти в USART - таймер, в данном случае не читается и запрос не снимается и передача продолжается и продолжается. Оказывается, запрос можно снять автоматически, установив в конфиге таймера бит DMACLRACT. Тогда по событию возникнет запрос и он сразу же снимется. Таким образом LDMA сделает только один трансфер и следующий будет только в следующем цикле таймера.
Ответить