一、什么是DMA?
DMA(Direct Memory Access,直接存储器访问)是一种数据传输方式,可以在不同的地址空间之间快速搬运数据,而无需CPU的直接参与。简单来说,DMA就像是一个“数据搬运工”,CPU只需要告诉它数据从哪里搬到哪里,然后DMA就会自动完成整个传输过程。
DMA可以用于外设与存储器之间的数据传输,比如从串口接收数据存入内存,或者从内存读取数据发送到外设;也可以用于存储器之间的数据搬运,比如将数据从一个内存区域复制到另一个区域。它的优势在于能够减少CPU的负担,提高数据传输效率,特别适用于大批量数据的高速传输。
在实际使用中,我们只需要正确配置DMA的参数,比如源地址、目标地址、数据长度等,然后启动使能DMA通道,数据就会自动完成搬运,无需CPU一直干预,大幅提升系统的运行效率。
●没有DMA参与的UART数据收发
在没有使用DMA的情况下,UART的数据发送和接收通常采用查询或中断方式。对于发送,CPU需要不断检测TXE(发送数据寄存器空)标志位,等待USART准备好后才能写入数据。这种方式不仅占用CPU资源,还可能导致较高的延迟。而在接收数据时,CPU同样需要不断轮询RXNE(接收数据寄存器非空)标志位,或者依赖接收中断,在数据到达时由中断服务程序进行读取。无论是查询还是中断方式,CPU都需要频繁介入数据的搬运,影响系统的实时性,特别是在数据量较大或速率较高的情况下,CPU负担会明显增加。
●有DMA参与的UART数据收发:核心配置好DMA控制器即可。
而使用DMA进行UART收发时,数据搬运的过程可以完全由DMA硬件完成。对于发送数据,CPU只需要将数据缓冲区地址和传输长度配置到DMA控制器,然后启动传输,DMA会自动将数据逐字节搬运到USART数据寄存器,无需CPU干预。接收数据时,DMA同样可以将USART接收到的数据直接存入指定缓冲区,CPU只需在数据传输完成后进行处理,而不需要时刻关注数据到达的情况。这种方式大幅降低了CPU的负担,提高了数据传输的效率,使得系统可以处理更高的数据速率,同时也减少了因中断或查询带来的性能损耗。
二、DMA的作用?
DMA的作用就是解决大量数据转移过度消耗CPU资源的问题,有了DMA得CPU可以更加专注的实用的的操作——计算、控制等。
DMA技术的出现,使得外围设备可以通过DMA控制器直接访问内存,与此同时,CPU可以继续执行程序。
DMA传输期间,DMA控制器接管了总线的控制权。在DMA传输结束后,DMA控制器将总线的控制权交给CPU。通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。
三、DMA的传输方向
- 外设和存储器
- 存储器和外设的传输。
- 存储器和存储器间的传输。
四、DMA中断
每个通道都有 3 个事件标志(DMA 半传输,DMA 传输完成和 DMA 传输出错),这 3 个事件标志逻辑或成为一个单独的中断请求。
五、DMA传输的模式
- 正常模式(DMA Mode Normal)
一次DMA数据传输完后,停止DMA传送,也就是只传输一次。要开始新的DMA传输,需要在关闭DMA通道的情况下,在DMA CNDTRx寄存器中重新写入传输数目。- 循环传输模式(DMA Mode Circular)
当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输模式。主要用于处理循环缓冲区和连续的数据传输。- 指针增量模式
外设和存储器指针在每次传输后可以自动向后递增或保持常量。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值。一般情况下存储器需要设置递增,而外设设置为保持常量。
六、DMA映射
以STM32F4开发板为例(不同型号映射不同)。DMA控制器包含了DMA1和DMA2,其中DMA1和DMA2均有8个数据流,每个数据流有8个通道。这里的通道可以理解为传输数据的一种管道。每个通道都有一个仲裁器,用于处理DMA 请求间的优先级。
如:UART使用DMA传输,就将DMA映射到下面的数据流通道中。
七、以串口为例,使用DMA1实现框图
(1)配置阶段:通过AHB从器件编程接口配置DMA控制器,设定源地址、目标地址、优先级等参数。
(2)请求阶段:数据源通过数据流请求通道向DMA控制器发送请求信号。
(3)传输阶段:DMA控制器根据优先级和仲裁结果,从源地址读取数据,经过FIFO缓冲区暂存,然后传输到目标外设的数据寄存器。
(4)处理阶段:外设接收到数据后进行进一步处理或发送。
八、DMA功能
1. 通道选择
每个数据流都与一个DMA请求相关联,此 DMA请求可以从8个可能的通道请求中选出。此选择由DMA_SxCR寄存器中的CHSEL[2:0] 位控制。芯片不同,DMA映射通道也不同。
2. 仲裁器(决定优先级,类似于NVIC控制器)
仲裁器为两个AHB主端口(存储器和外设端口)提供基于请求优先级的8个DMA 数据流请求管理,并启动外设/存储器访问序列。
优先级管理分为软件配置和硬件配置:
软件:每个数据流优先级都可以在 DMA_SxCR寄存器中的PL[1:0] 位控制配置。分为四个级别:
硬件:如果两个请求具有相同的软件优先级,则编号低的数据流优先于编号高的数据流。例如,数据流2的优先级高于数据流4。
3. FIFO直接模式和阈值突发模式
(1)FIFO简介
FIFO用于在源数据传输到目标之前临时存储这些数据。每个数据流都有一个独立的(总容量16字节)FIFO,FIFO临时存储数据最多为16字节,FIFO的存储阈值级别可由软件配置为1/4(4字节)、1/2(8字节)、3/4(12字节)或满(16字节)。
为了使能FIFO阈值级别,必须通过将DMA_SxFCR寄存器中的 DMDIS位置1来禁止直接模式。
(2)直接模式(相当于写多少个字节就输出多少个字节)
默认情况下,FIFO 以直接模式操作(将 DMA_SxFCR中的DMDIS位清0,不使用FIFO阈值级别 )。
在直接模式下,不使用FIFO 的阈值级别控制。每完成一次从外设到FIFO 的数据传输后,相应的数据立即就会移出并存储到目标中。当实现存储器到存储器传输时不得使用直接模式!!!
当在直接模式(禁止 FIFO)下将DMA配置为以存储器到外设模式传输数据时,DMA 会将一个数据从存储器预加载到内部 FIFO,从而确保一旦外设触发DMA请求时则立即传输数据。(为了避免 FIFO饱和,建议使用高优先级配置相应的数据流),该模式仅限以下方式的传输:
●源和目标传输宽度相等,并均由 DMA_SxCR中的 PSIZE[1:0] 位定义(MSIZE[1:0]位的状态是“无关”)
不可能进行突发传输(DMA_SxCB 中的 PBURST[1:0]和MBURST[1:0]位的状态是“无关”)。
(3)FIFO阈值突发模式
使能这种模式(将DMA_SxCR寄存器中的位EN置1)时,每次产生外设请求.数据流都会启动数据源到FIFO的传输。达到FIFO的阈值级别时,FIFO的内容移出并存储到目标中。
如果DMA_SxNDTR 寄存器达到零、外设请求传输终止(在使用DMA流控制器的情况下)或 DMA_SxCR寄存器中的EN位由软件清零,传输即会停止。
选择FIFO阈值(DMA_SxFCB寄存器的位 FTH[1:0] )和存储器突发大小(DMA_SxCR寄存器的MBURST[1:0] 位)时需要小心,FIFO阈值指向的内容必须与整数个存储器业发传输完全匹配。如果不是这样,当使能数据流时将生成一个FIFO错误(DMA_HISR或 DMA_LISR寄存器的标志FEIEx),然后将自动禁止数据流。允许的和禁止的配置在表41:FIFO阈值配置中介绍。
所有这些情况下,突发大小与数据大小的乘积不得超过FlFO容量大小。如果发生下列情况,会导致 DMA 传输结界出现不完整的冲突传输。如:31个byte,使用4节拍的1次突发,则最多突发7次,还剩3个byte(不完整的冲突传输)。这3个byte就使用单次传输模式进行传输。
4. 源、目标和传输模式
源传输和目标传输在整个4GB区域(地址在0x0000 0000和OxFFFF FFFF 之间)都可以寻址外设和存储器。
传输方向使用DMA_SxCR寄存器中的 DIR[1:0] 位进行配置,有三种可能的传输方向:存储器到外设、外设到存储器或存储器到存储器。表 37介绍了相应的源和目标地址。
只有DMA2控制器能够执行存储器到存储器的传输。
使用存储器到存储器模式时,不允许循环模式和直接模式。
使用示例:如果从UART的DR寄存器(外设)读取到buff[](存储器)中,则需要把DMA_SxCR寄存器的位DIR[1:0] 位写入00,并把UART的DR寄存器的地址放入DMA_SxPAR中,把buff[]的地址放入DMA_SxMOAR中,即可进行DMA传输。
5. 指针递增
根据DMA_SxCR寄存器中 PINC和 MINC位的状态,外设和存储器指针在每次传输后可以自动向后递增或保持常量。
使用示例:如果从UART的DR寄存器(外设)读取到buff[](存储器)中,则需要将buff[](存储器)设置为递增模式,UART的DR寄存器(外设)设置为固定模式。
通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用。
如果使能了递增模式,则根据在DMA.SxCR寄存器PSIZE或 MSIZE位中编程旳数据宽度,下一次传输的地址将是前一次传输的地址递增1(对于字节)、2(对于半字)或4(对于字)。
为了优化封装操作,可以不管AHB 外设端口上传输的数据的大小,将外设地址的增量偏移大小固定下来。DMA.SxCB寄存器中的 PINCOS位用于将增量偏移大小与外设AHB端口或32位地址(此时地址递增4)上的数据大小对齐。PINCOS位仅对AHB外设端口有影响。
如果将PINCOS位置1,则不论 PSIZE值是多少,下一次传输的地址总是前一次传输的地址递增4(自动与32位地址对齐)。但是,AHB存储器端口不受此操作影响。
如果 AHB外设端口或AHB存储器端口分别请求突发事务,为了满足AMBA 协议(在固定地址模式下不允许突发事务),则需要将PINC或MINC位置1。
6. 流控制器
决定谁可以控制整个数据传输的终点。
控制要传输的数据数目的实体称为流控制器,此流控制器使用DMA_SxCR寄存器中的 PFCTRL位针对每个数据流独立配置。
DMA 控制器: 在这种情况下,要传输的数据项的数目在使能DMA数据流之前由软件编程到DMA_SxNDTR寄存器。
外设源或目标:当要传输的数据项的数目未知时属于这种情况。当所传输的是最后的数据时,外设通过硬件向DMA 控制器发出指示。仅限能够发出传输结束信号的外设支持此功能,也就是: SDIO。
九、示例实验
如何使用:一旦你配置好DMA通道并使能它,DMA会自动在源地址和目标地址之间传输数据。 你不需要在每次传输时手动调用数据操作。DMA的优势就在于它可以极大地减少CPU的负担,允许你在传输过程中继续执行其他任务。
实验一:串口使用DMA发送/接收数据
软件分析
流程:
(1)配置串口功能,并打开串口空闲中断,使能串口DMA发送和接收功能。
(2)配置DMA参数,如源地址、目标地址、大小、方向等。
(3)DMA发送和接收
① 使用DMA发送数据时,要先关闭发送通道(数据流),然后才能写入数据,写完以后使能通道就可以自动完成内容搬运。
② 使用DMA接收数据时,要先关闭接收通道,防止还没处理完,新的数据就又来了,处理完后再打开通道。
1. 寄存器版本(STM32F4)
发送数据代码如下,这里我们假设串口已经被配置好了,且该代码使用的是F4开发板。
void USART1_DMAT_Init (u32 sAddr,u32 rAddr,u32 num) // sAddr:源地址(发送方) rAddr:目标地址(接收方),num:数量
{
RCC->AHB1ENR|=(0x1 <<22); //1.打开DMA2时钟
DMA2_Stream7->CR &= ~(0x1<<0);//2.关闭数据流7
USART1->CR3 |=(0x1<< 7); //3.使能USART1_TX的DMA传输
DMA2_Stream7->FCR &= ~(0x1<<2);//4.使能直接模式
//5.配置CR
DMA2_Stream7->CR = 0;//整体清零
DMA2_Stream7->CR |=(0x4 <<25); //选择通道4
DMA2_Stream7->CR |=(0x2<<16); //高优先级
DMA2_Stream7->CR |=(0x1 <<10);//存储器地址递增 1byte
DMA2_Stream7->CR |=(0x1<<6);//存储器到外设方向
/*
*PSIZE=MSIZE =8bit = lbyte
*外设地址固定
*禁止循环模式
*DMA作为流控制器 */
DMA2_Stream7->NDTR = num;//6.设置传输项数
DMA2_Stream7->MOAR = sAddr;//7.设置源地址
DMA2_Stream7->PAR= rAddr;//8.设置目标地址
DMA2_Stream7->CR |=(0x1<<0);//9.使能数据流
}
//---------------------------------------以下为主函数main.c使用内容
u8 buff[20]="hello world";
void mian()
{
printf("Reset!!!\r\n");
USART1_DMAT_Init((u32)buff, (u32)&USART1->DR ,strlen(buff));
//printf("DMA!!!\r\n");
}
接收数据代码
void USART1_DMAR_Init (u32 sAddr,u32 rAddr,u32 num) // sAddr:源地址(发送方) rAddr:目标地址(接收方),num:数量
{
RCC->AHB1ENR|=(0x1 <<22); //1.打开DMA2时钟
DMA2_Stream2->CR &= ~(0x1<<0);//2.关闭数据流2
USART1->CR3 |=(0x1<< 6); //3.使能USART1_RX的DMA传输
DMA2_Stream2->FCR &= ~(0x1<<2);//4.使能直接模式
//5.配置CR
DMA2_Stream2->CR = 0