2015-11-17 3 views
2

Из-за некоторых требований к дизайну мне нужно изменить дескриптор DMA во время выполнения. Для этого я выполняю следующие шаги:Atmel SAMD21 DMA Abort issue

  1. Abort DMA channel. Затем аппаратное обеспечение DMA сохранит исполняемый дескриптор на месте записи write_back RAM того же канала DMA.
  2. Дождитесь завершения Abort
  3. Измените дескриптор DMA на место записи write_back.
  4. Включить DMA канал снова

Это фрагмент кода я использую:

//Select DMA channel 
DMAC->CHID.reg = DMAC_CHID_ID(cSPIDMAResource0.channel_id); 

//Abort Selected DMA channel 
DMAC->CHCTRLA.reg &= ~DMA_CHANNEL_ENABLE_BIT_POS; 

//Wait until Abort completed 
while((DMAC->CHCTRLA.reg & DMA_CHANNEL_ENABLE_BIT_POS) == DMA_CHANNEL_ENABLE_BIT_POS); 

/* 
    Modify Descriptor here 
*/ 

//Enable DMA channel 
DMAC->CHCTRLA.reg |= DMA_CHANNEL_ENABLE_BIT_POS; 

Вышеуказанные шаги прекрасно работают без каких-либо проблем, но я столкнулся вопрос Descriptor коррупции во время долгосрочной перспективе.

Аппаратное обеспечение DMA хранит в настоящее время исполняемый дескриптор в местоположении записи write_back другого канала DMA (вместо собственного местоположения ОЗУ записи), когда выполняется прерывание DMA.

Если у кого-то есть представление о том, что пошло не так, или у меня есть идея, как я могу полностью избежать проблемы с дескриптором, я бы хотел попробовать.

ответ

1

Почему бы не использовать драйвер dma фреймворка atmel? Вот как они делают прерывание.

void dma_abort_job(struct dma_resource *resource) 
{ 
    uint32_t write_size; 
    uint32_t total_size; 

    Assert(resource); 
    Assert(resource->channel_id != DMA_INVALID_CHANNEL); 

    system_interrupt_enter_critical_section(); 

    DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id); 
    DMAC->CHCTRLA.reg = 0; 

    system_interrupt_leave_critical_section(); 

    /* Get transferred size */ 
    total_size = descriptor_section[resource->channel_id].BTCNT.reg; 
    write_size = _write_back_section[resource->channel_id].BTCNT.reg; 
    resource->transfered_size = total_size - write_size; 

    resource->job_status = STATUS_ABORTED; 
} 

Одно из различий, которые могли бы объяснить проблемы является отключение прерываний с помощью system_interrupt_leave_critical_section() во время записи в регистр для прерывания канала DMA.

+0

Я также попытался отключить прерывания во время доступа регистра DMAC, но это не решило проблему – GOKU