DMA的基本介绍什么是DMA(DMA)的作用

DMA基本介绍什么是DMA(DMA的基本定义)

DMA,全称Direct Memory Access,就是直接内存访问。

DMA 传输将数据从一个地址空间复制到另一个地址空间,从而在外设和内存之间或内存和内存之间提供高速数据传输。

我们知道CPU具有数据传输、计算、控制程序传输等多种功能。系统运行的核心是CPU。

CPU一直在处理大量的事务,但有些事情并不那么重要,比如数据复制和数据存储。如果我们把这部分 CPU 资源拿出来,让 CPU 来处理其他复杂的计算事务,是不是可以更好地利用 CPU 资源呢?

因此:传输数据(尤其是传输大量数据)可以在没有 CPU 参与的情况下完成。例如,如果要将外设A的数据复制到外设B,只要为两个外设提供一条数据通路,就可以直接将数据从A复制到B,无需CPU处理。

DMA就是基于以上假设设计的,其作用是解决大量数据传输过度消耗CPU资源的问题。拥有 DMA 可以让 CPU 专注于更实际的操作——计算、控制等。

DMA定义:

DMA 用于提供外设与内存之间或内存与内存之间的高速数据传输。无需 CPU 干预即可通过 DMA 快速移动数据。这可以为其他操作释放 CPU 资源。

DMA传输方式

DMA的作用是实现数据的直接传输,去掉了传统数据传输需要CPU寄存器参与的环节。主要涉及四种情况下的数据传输,但本质是一样的,都是从内存的某个区域传输到内存中(外设的数据寄存器本质上就是内存的一个存储单元)。数据传输的四种情况如下:

DMA 传输参数

我们知道,对于数据传输,首先需要的是1数据的源地址,2数据传输位置的目的地址,3数据传输量,传输多少数据,4需要的核心参数由传输模式 DMA 决定执行了多少次传输。四

当用户设置参数时,主要涉及源地址、目标地址和要传输的数据量,DMA 控制器将开始数据传输。当剩余传输数据量为0时,到达传输结束,结束DMA传输。当然,DMA还有一种循环传输模式,当到达传输结束时重新启动DMA传输。

也就是说,只要剩余传输数据量不为0,且DMA处于使能状态,就会发生数据传输。

DMA的主要特点

每个通道直接连接一个专用的硬件 D​​MA 请求,每个通道还支持软件触发。这些功能是通过软件配置的;

STM32有多少DMA资源?

对于大容量的STM32芯片,有2个DMA控制器和2个DMA控制器,DMA1有7个通道,DMA2有5个通道。

每个通道都可以配置一些外设的地址。

①DMA1控制器

从机外设(TIMx[x=1、2、3、4]、ADC1、SPI1、SPI/I2S2、I2Cx[x= < @1、2] 和 USARTx[x=1、2、3]) 生成 7 个 DMA 请求,通过逻辑或输入到 DMA1 控制器,其中每个通道对应一个特定的外部假设:

② DMA2 控制器

从机外设(TIMx[5、6、7、8]、ADC3、SPI/I2S3、UART4、DAC 通道1、@ > 2 和 SDIO),它们被逻辑或运算到 DMA2 控制器中,其中每个通道对应一个特定的外设:

1、0@>

1、1@>

这些在DMA工作系统框图下方的系统框图中也可以很清楚的看到

1、2@>

在上面的框图中,我们可以看到 STM32 内核、内存、外设和 DMA 的连接。这些硬件最终通过各种线路连接到总线矩阵。硬件结构之间的数据传输由总线矩阵协调。使各个外设和谐地使用总线传输数据。

让我们一点一点地分析他:

让我们看看ADC收集的数据是如何存储在SRAM中的,有没有DMA?

没有 DMA

1、3@>如果没有DMA,CPU需要使用内核作为中转站来传输数据。例如,要将ADC采集的数据传输到SRAM,流程如下:

内核通过DCode由总线矩阵协调,获取外设ADC采集的数据,存储在AHB中。

然后内核通过DCode通过总线矩阵协调将数据存储在内存SRAM中。

1、4@>

使用 DMA 传输

如果有DMA,

外设在 DMA 传输期间向 DMA 控制器发出请求。

DMA 控制器接收到请求并触发 DMA 工作。

DMA 控制器从 AHB 外设获取 ADC 采集的数据,并将其存储在 DMA 通道中

DMA控制器的DMA总线与总线矩阵相协调,外围ADC采集的数据使用AHB通过DMA通道存储在SRAM中。在数据传输过程中,完全不需要内核的参与,即不需要CPU的参与。

1、5@>

让我们专业地介绍一下上述步骤:

事件发生后,外设向 DMA 控制器发送请求信号。DMA 控制器根据通道的优先级处理请求。当 DMA 控制器开始访问请求外设时,DMA 控制器立即向它发送一个确认信号。当它从 DMA 控制器获得确认信号时,外设立即释放其请求。一旦外设释放请求,DMA 控制器也会取消确认确认信号。DMA 传输完成,如果有更多请求,外设可以开始下一个周期。

简而言之,每次 DMA 传输由 3 个操作组成: DMA 传输模式

方法一:DMA_Mode_Normal,普通模式,

当一个 DMA 数据传输完成后,停止 DMA 传输,即只传输一次

方法二:DMA_Mode_Circular,循环传输模式

传输结束后,硬件会自动重新加载传输数据量寄存器,用于下一轮数据传输。多传输模式仲裁器

1、6@>

仲裁器的作用是确定每次 DMA 传输的优先级

仲裁器根据通道请求的优先级启动外设/内存访问。

优先级管理分为2个阶段:

软件:每个通道的优先级可以在 DMA_CCRx 寄存器中设置,有 4 个级别:

硬件:如果 2 个请求具有相同的软件优先级,则编号较低的通道比编号较高的通道具有更高的优先级。例如:如果软件优先级相同,则通道 2 优先于通道 4。

注意:在批量产品和互连产品中,DMA1 控制器的优先级高于 DMA2 控制器。DMA 数据流(仅在 STM32F4 /M4 内核上)

DMA通道设置好后,还必须选择外设对应的数据流。

八个 DMA 控制器流中的每一个都可以在源和目标之间提供单向传输链路。每个数据流都可以配置为执行:

● 一般类型的事务:内存到外设、外设到内存或内存到内存的传输。

● 双缓冲区类型事务:使用内存的两个内存指针进行双缓冲区传输(当DMA 对缓冲区进行读/写时,应用程序可以对其他缓冲区进行写/读操作)。要传输的数据量(最多可以编程 65531、7@> 并且与连接到外设的 AHB 端口的外设(请求 DMA 传输)的源宽度有关。每个事务完成后,包含数据项to be transfer 寄存器总数将递减。

DMA_SxCR 寄存器控制数据流使用哪个通道。每个数据流有 8 个通道。

对于选择,一次只能选择一个通道进行 DMA 传输。接下来我们看一下DMA2的数据流向

通道映射表,如表21、8@>1、3@>1:

DMA传输通道

每个通道都可以在外设寄存器和具有固定地址的内存地址之间执行 DMA 传输。DMA 传输的数据量是可编程的,最大为 65535。包含要传输的数据项数量的寄存器,每次传输后递减。

可编程数据量:

外设和存储器的传输数据量可以通过 DMA_CCRx 寄存器中的 PSIZE 和 MSIZE 位进行编程。指针递增模式

根据 DMA_SxCR 寄存器中 PINC 和 MINC 位的状态,外设和存储器指针可以在每次传输后自动反向递增或保持不变。当设置为增量模式时在dma方式中有没有中断请求,下一个要传输的地址将是前一个地址加上增量值

通过单个寄存器访问外设源或目标数据时,禁用增量模式很有用。

如果启用了递增模式,则下一次传输的地址将是上一次传输的地址,增加 1 个数据宽度、2 个数据宽度或 4 个数据宽度,具体取决于在 DMA_SxCR 的 PSIZE 或 MSIZE 位中编程的数据宽度登记。内存到内存模式

DMA 通道的操作可以在没有外设请求的情况下进行,这种操作是内存到内存模式。

当 DMA_CCRx 寄存器中的 MEM2MEM 位置位时,当软件设置 DMA_CCRx 寄存器中的 EN 位以启动 DMA 通道时,DMA 传输将立即开始。当 DMA_CNDTRx 寄存器变为 0 时,DMA 传输结束。内存到内存模式不能与循环模式同时使用。

这里需要注意的是只有DMA2的外设接口可以访问内存,所以只有DMA2控制器支持内存到内存的传输,DMA1不支持。

内存到内存模式不能与循环模式同时使用。DMA中断

每个 DMA 通道可以在 DMA 传输进行到一半时、传输完成时以及出现传输错误时产生中断。为了应用程序的灵活性,这些中断是通过设置寄存器的不同位来打开的。

即使没有开启,我们也可以通过轮询这些位来获取当前的 DMA 传输状态。我们这里常用的是TCIFx位,即数据流x的DMA传输完成标志。

可编程数据传输宽度、对齐和数据字节序

当 PSIZE 和 MSIZE 不同时,DMA 模块按照下图进行数据对齐。

注意:在大批量产品中,DMA2 通道 4 和 DMA2 通道 5 中断映射到同一个中断向量上。在互连产品中,DMA2 通道 4 和 DMA2 通道 5 的中断具有独立的中断向量。所有其他 DMA 通道都有自己的中断向量 DMA 内存占用

在STM32控制器中,芯片采用Cortex-MX架构,总线结构做了很大优化。DMA占用另一条地址总线,不会与CPU的系统总线发生冲突。即使用DMA不影响CPU的运行速度

但请注意:

DMA 控制器和 Cortex-M3 内核共享系统数据总线以执行直接内存数据传输。当 CPU 和 DMA 同时访问同一个目标(RAM 或外设)时,DMA 请求可能会在几个周期内停止 CPU 访问系统总线,总线仲裁器执行循环调度以确保 CPU 可以获得至少一半的系统总线(内存或外围设备)带宽。DMA 配置部分

这部分我们将其分为DMA寄存器和DMA库函数,分别介绍:

DMA寄存器

DMA配置参数包括:通道地址、优先级、数据传输方向、内存/外设数据宽度、内存/外设地址是否递增、循环方式、数据传输量。

DMA 中断状态寄存器 (DMA_ISR)

如果我们在 DMA_ISR 中开启这些中断,我们会在达到条件后跳转到中断服务函数。即使没有开启,我们也可以通过查询这些位来获取当前的 DMA 传输状态。这里我们常用的是TCIFx,即通道DMA传输是否完成的标志。

注意这个寄存器是只读的,所以这些位被置位后,只能被其他操作清零。DMA 中断标志​​清除寄存器 (DMA_IFCR)

DMA_IFCR的位用来清零DMA_ISR的对应位,可以通过写0来清零。DMA_ISR置位后,我们必须向该位寄存器的对应位写0来清零。

DMA 通道 x 配置寄存器 (DMA_CCRx)

这个寄存器控制了很多DMA相关的信息,包括数据宽度、外设和内存宽度、通道优先级、增量模式、传输方向、中断使能、使能等都是通过这个寄存器来设置的。所以DMA_CCRx是DMA传输的核心控制寄存器

DMA 通道 x 传输编号寄存器 (DMA_CNDTRx) (x = 1…7)

该寄存器控制每次 DMA 通道 x 传输的数据量。其设置范围为 0~65535。并且这个寄存器的值会随着传输的进行而减少。当该寄存器的值为0时,表示数据传输已经完成。所以可以通过这个寄存器的值知道当前DMA传输的进度

DMA 通道 x 外设地址寄存器 (DMA_CPARx) (x = 1…7)

该寄存器用于存储 STM32 外设的地址。比如我们使用串口1,那么这个寄存器必须写入0x40013804(实际上是&USART1_DR)。如果使用其他外设,只需修改对应外设的地址即可。

DMA 通道 x 配置寄存器 (DMA_CMARx)

,这个寄存器和DMA_CPARx类似,不过是用来放内存地址的。例如,我们使用 SendBuf[5200] 数组作为内存,那么我们可以在 DMA_CMARx 中写入 &SendBuff。

DMA寄存器配置过程

通道配置流程 下面是配置DMA通道x的流程(x代表通道号):

在 DMA_CPARx 寄存器中设置外设寄存器的地址。当发生外设数据传输请求时,该地址将是数据传输的源或目标。

在 DMA_CMARx 寄存器中设置数据存储器的地址。当发生外设数据传输请求时,传输的数据将从该地址读取或写入。

在 DMA_CNDTRx 寄存器中设置要传输的数据量。该值在每次数据传输后递减。

通道的优先级在 DMA_CCRx 寄存器的 PL[1:0] 位中设置。

在 DMA_CCRx 寄存器中,设置数据传输的方向、循环模式、外设和内存的增量模式、外设和内存的数据宽度、传输一半或传输完成时产生中断。

设置 DMA_CCRx 寄存器中的 ENABLE 位以启用通道。

启动 DMA 通道后,它可以响应来自连接到该通道的外设的 DMA 请求。当传输了一半数据时,半传输标志 (HTIF) 设置为 1,当半传输允许中断位 (HTIE) 设置时,将产生中断请求。在数据传输结束时,传输完成标志 (TCIF) 被设置为 1,当传输完成中断位 (TCIE) 被设置时在dma方式中有没有中断请求,将产生一个中断请求。

DMA 库函数

1、3@>DMA初始化函数

DMA_DeInit(DMAX_ChannelX);

功能:将DMAyChannelx寄存器初始化为默认值

注意:在RCC_ResetCmd中没有定义DMA,所以采用直接操作DMA寄存器的方法

无效 DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx,DMA_InitTypeDef* DMA_InitStruct)

功能:设置要打开的通道,以及一些参数,包括外设基地址、内存基地址、传输的数据量、增量模式、数据宽度等。

详细结构代码见下面:

类型定义结构{

uint32_t DMA_PeripheralBaseAddr;

/*设置DMA源地址*/

uint32_t DMA_MemoryBaseAddr;

/*设置DMA目的地址*/

uint32_t DMA_DIR;

/* 设置数据传输方向,决定是否从外设读取数据到内存以及从内存读取的数据发送到外设,即外设是源还是目的

*/

uint32_t DMA_BufferSize;

/* 设置传输大小 */

uint32_t DMA_PeripheralInc;

/*设置ReceiveBuff地址是否自增*/

uint32_t DMA_MemoryInc;

/* 设置传输数据时是否传递内存地址,需要开启*/

uint32_t DMA_PeripheralDataSize;

/* 外设的数据长度是字节传输(8bits)、半字传输(16bits)还是字传输(32bits) */

uint32_t DMA_MemoryDataSize;

/*设置内存的数据长度*/

uint32_t DMA_Mode;

/*设置DMA的模式,普通模式/循环模式是否循环发送*/

uint32_t DMA_Priority;

/*设置DMA通道的优先级,有四种模式:低、中、高和超高*/

uint32_t DMA_M2M;

/* 设置是否为内存到内存的方式传输,*/ }

DMA_InitTypeDef;

2.DMA 使能函数

无效 DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx,FunctionalState NewState);

功能:启用或禁用 DMA 外设

例如:DMA_Cmd(DMA1_Channel1 , ENABLE);

3.DMA中断使能函数

无效 DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx,uint32_t DMA_IT,FunctionalState NewState);

功能:配置指定DMAy通道x的中断

注释: DMA_IT_TC:传输完成 DMA_IT_HT:半传输 DMA_IT_TE:传输错误

例如:DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

4.设置CNDTRx和读取CNDTRx函数(通道传输的数据量)

无效 DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);

uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

功能:前者设置DMA通道传输的数据量(DMA关闭);后者获取当前DMA通道传输的剩余数据量(DMA开启)。

DMA库函数配置流程:

启用 DMA 时钟:RCC_AHBPeriphClockCmd();

初始化 DMA 通道:DMA_Init();

//设置频道;转账地址;转移方向;传输数据的数量;传输数据宽度;传输模式; 优先事项; 是否打开内存到内存。

启用外设 DMA;

以串口为例:开启串口DMA传输,串口DMA开启功能。调用函数:USART_DMACmd();

启用 DMA 通道传输;函数:DMA_Cmd();

查询 DMA 传输状态。函数:DMA_GetFlagStatus();

获取当前剩余数据大小函数:DMA_GetCurrDataCounter(DMA1_Channel4);

UART DMA 传输

DMA 是将数据从一个位置移动到另一个位置的移动器。

以UART为例,如果要接收数据,会触发UART中断,然后CPU进行干预。中断期间,UART输入寄存器的值会被CPU读出并存入内存;

DMA方式中,UART中断产生后,DMA直接参与,将UART输入寄存器的值传送到内存中。CPU只需要检查内存的值,提高了CPU的效率。

DMA 代码配置

① DMA初始化配置

无效 dma_init()

{

DMA_InitTypeDef DMA_InitStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

/*DMA 配置*/

DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;//串口数据寄存器地址

DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff; //内存地址(要传输的变量指针)

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//方向(从内存到外设)

DMA_InitStructure.DMA_BufferSize = 500; //传输内容的大小

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不增加

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址递增

DMA_InitStructure.DMA_PeripheralDataSize =

DMA_PeripheralDataSize_Byte ; //外围数据单元

DMA_InitStructure.DMA_MemoryDataSize =

DMA_MemoryDataSize_Byte ; //内存数据单元

DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //DMA模式:一次传输,循环

DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级:高

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//禁用内存到内存传输

DMA_Init(DMA1_Channel4, &DMA_InitStructure); //配置DMA1的4个通道

DMA_Cmd(DMA1_Channel4,ENABLE);

DMA_SetCurrDataCounter(DMA_CH4,DMA1_MEM_LEN);//DMA通道的DMA缓冲区大小

DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//配置DMA传输后产生中断

}

DMA中断

无效 DMA1_Channel4_IRQHandler(无效)

{

如果(DMA_GetFlagStatus(DMA1_FLAG_TC4)==SET)

{

DMA_ClearFlag(DMA1_FLAG_TC4);

}

}

主功能

#define SEND_BUF_SIZE 500//发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍。

u8 SendBuff[SEND_BUF_SIZE];//发送数据缓冲区

const u8 TEXT_TO_SEND[]={“STM32F1 DMA串口实验”};

uint16_t 我;

诠释主要(无效)

{

uart_init(115200); //串口初始化为115200

对于(我=0;我{

SendBuff[i] =0xaf;

}

USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //开启串口dma传输

而(1);

=

} 关键词:STM32DMA直接内存访问编辑:什么鱼参考地址:

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论