打印
[APM32F4]

APM32F4xx DMA双缓冲:超级效率的数据传输策略

[复制链接]
926|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
DKENNY|  楼主 | 2023-9-1 22:11 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 DKENNY 于 2023-9-1 22:12 编辑

我们在使用传统的数据传输时,也就是说,在没有使用DMA的介入的情况下,CPU在执行一个任务时,它需要参与到数据传输的每个步骤,包括读取和写入,这种情况下,
    1. CPU可以对此次数据传输过程精确控制。
    2. 适用于小量的数据传输,此时传输的开销相对较小。

但当 DMA 传输大量数据时,这种方式有一定缺陷。
    1. CPU将花费大量时间在数据传输上,而CPU对于此次数据传输后数据的处理时间并未得到缩减,其结果是CPU的处理时间和资源被消耗在低级的传输细节上,从而降低了传输效率。
    2. 会有一定的中断负担。CPU在进行数据传输时,会触发传输完成和传输错误中断,CPU在执行任务时频繁的响应中断,会影响正常指令执行的流程。
    3. CPU会一直等待传输数据完成,其他任务的执行可能会受到延迟。

DMA单缓冲机制:
顾名思义,DMA单缓冲机制就是只使用一个缓冲区作为DMA传输的媒介,也就是普通的DMA传输,设置DMA的起点、终点以及数据传输量即可开始DMA传输。
DMA传输数据时,会从起点将数据搬运至终点,每搬运一次,传输数据量减1,当传输数据量为0时,DMA停止搬运,也就完成了一次数据传输。
这里也介绍一下DMA两种数据传输模式,普通模式和循环模式
    普通模式: 当DMA完成一次数据传输后,需手动重载传输数据量才可开始下一次传输。
    循环模式: 当DMA完成一次数据传输后,会自动设置传输数据量,这样设置后,数据即可不断进行传输。
在使用 DMA 的情况下,由于DMA允许数据在内存和外设之间直接传输,而无需CPU的干预。这消除了CPU执行传输所需的时间,从而大大提高了数据传输的速率。在没有DMA的情况下,数据传输可能需要CPU中断以通知传输状态。使用DMA可以减少这种中断频率,从而降低了CPU的负担。

那么普通DMA有什么缺点呢?
由于普通DMA只有一个数据目标缓冲区,试想一下,如果在DMA的缓冲区已经存满的情况下(也就是说,CPU还没来得及开始处理这些数据),而这时,又有新的数据传输过来了,那么原来的旧数据将会被新数据所覆盖,造成数据丢失的情况。

DMA双缓冲机制
DMA 双缓冲机制,即 DMA 使用两个内存缓存区进行数据传输,当 DMA 完成一次完整的数据传输后,会自动切换至另一块缓冲区,这样 CPU 就有足够的时间处理这一次接受的数据。

那么 APM32F407 如何开启双缓冲模式呢?
APM32F407官方手册有这么一段描述:
其中,APM32F407 官方库已经给我们封装好了相关函数,我们直接调用 API 即可。
APM32F407 DMA 具有 MEMORY_0 MEMORY_1 两个缓冲区,我这里使用的是基于官方例程DMA_ADC的修改。

首先我们可以先定义双缓冲的两个内存区域。
uint8_t  adcbuffer0[256];
uint8_t  adcbuffer1[256];
我们可以先配置DMA 其中一个缓冲区的地址。
dmaConfig.memoryBaseAddr = (uint32_t)adcBuffer0;
之后,我们调用 如下函数配置双缓冲模式。
DMA_ConfigBufferMode(DMA2_Stream0,(uint32_t)adcBuffer1,DMA_MEMORY_0);
我们配置DMA 的第二个缓冲区地址“adcBuffer1” ,“DMA_MEMORY_0” 这个变量代表的DMA使用的当前的缓冲区,也就是DMA 传输数据时先访问 MEMORY_0 缓冲区,当然,我们一般默认配置的都是 MEMORY_0 开始传输;如果我们配置成” DMA_MEMORY_1”,那么DMA 传输数据时先访问 MEMOTY_1缓冲区。
接下来,我们就可以开启DMA 双缓冲模式了。
DMA_EnableDoubleBufferMode(DMA2_Stream0);
同时开启 DMA 传输完成中断,查看当前 DMA 访问的缓冲区。
DMA_EnableInterrupt(DMA2_Stream0, DMA_INT_TCI**);//这里被屏蔽掉了,为传输完成标志位
NVIC_EnableIRQRequest(DMA2_STR0_IRQn,2,1);
调用标准库中的DMA_ReadCurrentMemoryTarget()函数,可以查看DMA 当前访问的缓冲区,如下为 DMA 中断函数实现。
void DMA2_STR0_IRQHandler(void)
{
    if(DMA_ReadStatusFlag(DMA2_Stream0,DMA_INT_TCI**0))//这里被屏蔽掉了,为传输完成标志位
    {
        // 访问 MEMORY0
        if(DMA_ReadCurrentMemoryTarget(DMA2_Stream0) == RESET)
        {
            printf("DMA adcbuffer0\r\n");
        }
        else
        {
            printf("DMA adcbuffer1\r\n");
        }
    }
}
最后,用户手册提到它并不支持存储器到存储器传输,那么它为什么不支持存储器到存储器传输呢?
    1. 存储器到存储器的数据传输通常涉及两个内存区域,这两个内存区域都被认为是主存储器的一部分。在这种情况下,CPU通常可以更轻松地执行数据传输,因为它具有足够的控制权和访问权限来直接访问主存储器中的数据。
    2. DMA的设计目的是通过允许外设或设备直接与主存储器交换数据,从而减少CPU的干预,提高系统性能。因此,DMA通常不支持存储器到存储器的传输模式,因为这种模式下的数据传输通常可以由CPU来管理和执行,而不需要DMA的介入。
    总之,DMA主要用于设备和主存储器之间的数据传输,以优化系统性能。存储器到存储器的传输通常由CPU来处理,因为CPU具有对主存储器的直接控制和访问权限,也就是说,在实现存储器与存储器传输的时候,我能直接使用 CPU 进行内存复制,为什么还要去使用 DMA 多开辟一个缓冲区呢,这毫无疑问又增加了系统资源开销。


以上就是我使用 APM32F407 的 DMA 双缓冲的理解与使用啦,如有问题,欢迎大家讨论交流。

使用特权

评论回复
评论
kai迪皮 2023-9-7 10:30 回复TA
点赞,要是有代码下载和运行结果就更好了 
沙发
tpgf| | 2023-10-7 14:32 | 只看该作者
那是不是就是说还有一种dma的双缓冲机制呢

使用特权

评论回复
板凳
nawu| | 2023-10-7 15:52 | 只看该作者
如何启用dma的这种单缓冲机制呢

使用特权

评论回复
地板
aoyi| | 2023-10-7 16:50 | 只看该作者
所有芯片的dma都有这种缓冲机制吗

使用特权

评论回复
5
zljiu| | 2023-10-7 17:25 | 只看该作者
这种方式的话 节省了cpu而且提高了效率

使用特权

评论回复
6
gwsan| | 2023-10-9 14:47 | 只看该作者
这两种缓冲机制通过配置寄存器就可以容易实现吗

使用特权

评论回复
7
tfqi| | 2023-10-9 15:23 | 只看该作者
如果能存储器直接到存储器 也就可以解放一下cpu了 呵呵

使用特权

评论回复
8
caigang13| | 2023-10-10 07:51 | 只看该作者
DMA已经提升了很大一部分效率了

使用特权

评论回复
9
DKENNY|  楼主 | 2023-10-11 09:06 | 只看该作者
nawu 发表于 2023-10-7 15:52
如何启用dma的这种单缓冲机制呢

在APM32F407中,DMA使能后,默认开启的就是单缓冲机制

使用特权

评论回复
10
DKENNY|  楼主 | 2023-10-11 09:09 | 只看该作者
gwsan 发表于 2023-10-9 14:47
这两种缓冲机制通过配置寄存器就可以容易实现吗

是的,APM32F407有DMA双缓冲模式的配置寄存器,配置对应的寄存器,就能使能DMA的双缓冲机制

使用特权

评论回复
11
DKENNY|  楼主 | 2023-10-11 09:10 | 只看该作者
caigang13 发表于 2023-10-10 07:51
DMA已经提升了很大一部分效率了


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

20

主题

35

帖子

4

粉丝