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

[复制链接]
 楼主| 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传输。
image001.png
DMA传输数据时,会从起点将数据搬运至终点,每搬运一次,传输数据量减1,当传输数据量为0时,DMA停止搬运,也就完成了一次数据传输。
这里也介绍一下DMA两种数据传输模式,普通模式和循环模式
    普通模式: 当DMA完成一次数据传输后,需手动重载传输数据量才可开始下一次传输。
    循环模式: 当DMA完成一次数据传输后,会自动设置传输数据量,这样设置后,数据即可不断进行传输。
image002.png
在使用 DMA 的情况下,由于DMA允许数据在内存和外设之间直接传输,而无需CPU的干预。这消除了CPU执行传输所需的时间,从而大大提高了数据传输的速率。在没有DMA的情况下,数据传输可能需要CPU中断以通知传输状态。使用DMA可以减少这种中断频率,从而降低了CPU的负担。

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

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

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

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


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

评论

点赞,要是有代码下载和运行结果就更好了  发表于 2023-9-7 10:30
tpgf 发表于 2023-10-7 14:32 | 显示全部楼层
那是不是就是说还有一种dma的双缓冲机制呢
nawu 发表于 2023-10-7 15:52 | 显示全部楼层
如何启用dma的这种单缓冲机制呢
aoyi 发表于 2023-10-7 16:50 | 显示全部楼层
所有芯片的dma都有这种缓冲机制吗
zljiu 发表于 2023-10-7 17:25 | 显示全部楼层
这种方式的话 节省了cpu而且提高了效率
gwsan 发表于 2023-10-9 14:47 | 显示全部楼层
这两种缓冲机制通过配置寄存器就可以容易实现吗
tfqi 发表于 2023-10-9 15:23 | 显示全部楼层
如果能存储器直接到存储器 也就可以解放一下cpu了 呵呵
caigang13 发表于 2023-10-10 07:51 来自手机 | 显示全部楼层
DMA已经提升了很大一部分效率了
 楼主| DKENNY 发表于 2023-10-11 09:06 | 显示全部楼层
nawu 发表于 2023-10-7 15:52
如何启用dma的这种单缓冲机制呢

在APM32F407中,DMA使能后,默认开启的就是单缓冲机制
 楼主| DKENNY 发表于 2023-10-11 09:09 | 显示全部楼层
gwsan 发表于 2023-10-9 14:47
这两种缓冲机制通过配置寄存器就可以容易实现吗

是的,APM32F407有DMA双缓冲模式的配置寄存器,配置对应的寄存器,就能使能DMA的双缓冲机制
 楼主| DKENNY 发表于 2023-10-11 09:10 | 显示全部楼层
caigang13 发表于 2023-10-10 07:51
DMA已经提升了很大一部分效率了


YK1 发表于 2024-9-23 21:28 | 显示全部楼层
请问一下,使用DMA双缓冲区进行数据的发送的时候,出现了重复发送数据的情况,请问该怎么解决呢?感觉主要是剩余文件数量达不到BUFFER_SIZE要求,带来的重复发送数据。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

59

主题

104

帖子

16

粉丝
快速回复 在线客服 返回列表 返回顶部

59

主题

104

帖子

16

粉丝
快速回复 在线客服 返回列表 返回顶部