本帖最后由 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 双缓冲的理解与使用啦,如有问题,欢迎大家讨论交流。 |
点赞,要是有代码下载和运行结果就更好了