参考资源
http://bbs.21ic.com/icview-210875-1-1.html
http://www.openedv.com/thread-78694-1-1.html
http://www.openedv.com/forum.php?mod=viewthread&tid=63890&highlight=dma%2B%CD%E2%B2%BFsram
http://blog.csdn.net/sunjiajiang/article/details/7945057
http://www.openedv.com/forum.php?mod=viewthread&tid=70062
参考野火《STM32库开发实战》 P195-P215 “DMA—直接存储区访问 ”章节讲解。
每个数据流都独立拥有四级32位FIFO。DMA传输具有FIFO模式和直接传输模式。
FIFO用于源数据传输到目标地址之间临时存储这些数据。
DMA2 支持全部三种传输模式,而 DMA1 只有外设到存储器和存储器到外设两种模
式。
DMA1 的存储区端口相比 DMA2 的要减少 AHB2 外设的访问权,同时 DMA1 外设端口是没有连接至总线矩阵的,只有连接到 APB1 外设,所以 DMA1 不能实现存储器到存储器传输。
流控制器主要涉及到一个控制 DMA 传输停止问题。 DMA 传输在 DMA_SxCR 寄存器的 EN 位被置 1 后就进入准备传输状态,如果有外设请求 DMA 传输就可以进行数据传输。
很多情况下,我们明确知道传输数据的数目,比如要传 1000 个或者 2000 个数据,这样我们就可以在传输之前设置 DMA_SxNDTR 寄存器为要传输数目值, DMA 控制器在传输完
这么多数目数据后就可以控制 DMA 停止传输。
DMA 数据流 x 数据项数 DMA_SxNDTR(x 为 0~7)寄存器用来记录当前仍需要传输数目,它是一个 16 位数据有效寄存器,即最大值为 65535,这个值在程序设计是非常有用也是需要注意的地方。我们在编程时一般都会明确指定一个传输数量,在完成一次数目传输
后 DMA_SxNDTR 计数值就会自减,当达到零时就说明传输完成。
如果某些情况下在传输之前我们无法确定数据的数目,那 DMA 就无法自动控制传输停止了,此时需要外设通过硬件通信向 DMA 控制器发送停止传输信号。这里有一个大前提就是外设必须是可以发出这个停止传输信号,只有 SDIO 才有这个功能,其他外设不具备此功能。
理解1
看库函数中
DMAy_Channelx->CNDTR = DMA_InitStruct->DMA_BufferSize;
而CNDTR即数据传输数量 (Number of data to transfer)
数据传输数量为0至65535。这个寄存器只能在通道不工作(DMA_CCRx的EN=0)时写入。通道开启后该寄存器变为只读,指示剩余的待传输字节数目。寄存器内容在每次DMA传输后递减。
数据传输结束后,寄存器的内容或者变为0;或者当该通道配置为自动重加载模式时,寄存
器的内容将被自动重新加载为之前配置时的数值。
当寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输。理解2
这个DMA_BufferSize 就是要传输的次数。
这个并不是指buf的字节大小,而是指DMA的传输次数,一次传输可以是:1字节,2字节,4字节。理解3
那为啥取名叫buffer 呢,我猜是为了和DMA_MemoryInc_Enable配合。虽然传输计数是减少的,但是内存地址是增长的。在声明DMA_MemoryBaseAddr时候,其实DMA的就指向(是指向,不是开辟!)一个大小为DMA_BufferSize的数组,数组的开始地址为DMA_MemoryBaseAddr。这个数组就是缓冲区。
每次ADC采样后,就通过DMA把数据放在内存中,
从DMA_MemoryBaseAddr+0地址开始放,
直到DMA_MemoryBaseAddr+DMA_BufferSize-1,此时CNDTR变成0,传输结束。
理解4
如果你有32字节数据:
datasize=1 ==> buffersize = 32
datasize=2 ==> buffersize = 16
datasize=4 ==> buffersize = 8
这个理解,说明了buffer_size设置时需要注意的事项,
1. 外设数据宽度、内存数据宽度
2. 我们设置了外部存储区的大小
**外设:DCMI_DR_ADDRESS
内存:FSMC_LCD_ADDRESS #液晶屏地址或数组首地址**
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; #外设数据宽度为32位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; #内存数据宽度为16位
上述此情况下,假设传输2个字节,则buffer_size = 16/32 = 0.5
假设传输32个字节,则buffer_size = 32/2*0.5=8
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; #外设数据宽度为16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; #内存数据宽度为32位
上述此情况下,假设传输4个字节,则buffer_size = 32/16= 2
假设传输32个字节,则buffer_size = 32/4*2=16
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; #外设数据宽度为16
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; #内存数据宽度为16位
上述此情况下,假设传输2个字节,则buffer_size = 16/16= 1
假设传输32个字节,则buffer_size = 32/2*1=16
DMA传输类型有单次(single)传输和突发(Burst)传输。
burst:
dma实际上是一次一次的申请总线,把要传的数据总量分成一个一个小的数据块。比如要传64个字节,那么dma内部可能分为2次,一次传 64/2=32个字节,这个2(a)次呢,就叫做burst。这个burst是可以设置的。这32个字节又可以分为32位 * 8或者16位*16来传输。
突发传输与 FIFO 密切相关,突发传输需要结合 FIFO 使用,具体要求 FIFO 阈值一定要是内存突发传输数据量的整数倍。 FIFO 阈值选择和存储器突发大小必须配合使用,
默认情况下, DMA 工作在直接模式,不使能 FIFO 阈值级别。
直接模式在每个外设请求都立即启动对存储器传输的单次传输。直接模式要求源地址和目标地址的数据宽度必须一致,所以只有 PSIZE 控制,而 MSIZE 值被忽略。突发传输是基于 FIFO 的所以直接模式不被支持。另外直接模式不能用于存储器到存储器传输。
在直接模式下,如果 DMA 配置为存储器到外设传输那 DMA 会见一个数据存放在FIFO 内,如果外设启动 DMA 传输请求就可以马上将数据传输过去