Atmel SAMD21 DMA 中止问题

Posted

技术标签:

【中文标题】Atmel SAMD21 DMA 中止问题【英文标题】:Atmel SAMD21 DMA Abort issue 【发布时间】:2015-11-17 06:18:45 【问题描述】:

由于一些设计要求,我需要在运行时更改 DMA 描述符。为此,我遵循以下步骤:

    中止 DMA 通道。然后 DMA 硬件将当前正在执行的描述符保存在 同一 DMA 通道的 write_back RAM 位置。 等到中止完成 修改 write_back RAM 位置上的 DMA 描述符。 再次启用 DMA 通道

这是我正在使用的代码 sn-p:

//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;

上述步骤可以正常工作,没有任何问题,但是从长远来看,我面临着描述符损坏问题。

当执行 DMA 中止时,DMA 硬件将当前正在执行的描述符存储在另一个 DMA 通道的 write_back RAM 位置(而不是自己的 write_back RAM 位置)。

如果有人知道出了什么问题,或者知道如何完全避免描述符损坏问题,我想尝试一下。

【问题讨论】:

【参考方案1】:

为什么不使用 atmel 软件框架的 dma 驱动程序?以下是他们如何进行中止。

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 通道。

【讨论】:

我也尝试在 DMAC 寄存器访问期间禁用中断,但没有解决问题

以上是关于Atmel SAMD21 DMA 中止问题的主要内容,如果未能解决你的问题,请参考以下文章

SAMD21时钟配置

为 SAMD21 DAC 使用外部 Vref

在 SAMDG55 上读取输出引脚电平

在 Atmel SAM L21 Xplained Pro 上通过 UART 发送请求和接收响应的代码问题

Eclipse J-Link ATMEL ARM ATSAME70Q21。当代码从不同于 0x00400000 的地址开始时进行调试

GPIO 引脚控制 SAMC21