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

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
Аватара пользователя
uldemir
Друг Кота
Сообщения: 7359
Зарегистрирован: Пт авг 28, 2009 21:34:30
Откуда: 845-й км.

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

Сообщение uldemir »

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

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

Т.е. приходит сигнал от таймера по переполнению, и мне надо послать одно 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/(2 * 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 - ничего нет
Реклама
Аватара пользователя
uldemir
Друг Кота
Сообщения: 7359
Зарегистрирован: Пт авг 28, 2009 21:34:30
Откуда: 845-й км.

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

Сообщение uldemir »

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

Проблемы были следующие:
Как пересылать 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 сделает только один трансфер и следующий будет только в следующем цикле таймера.
Реклама
Ответить

Вернуться в «ARM»