【STM32-DMA简介】

STM32-DMA简介


一、DMA是什么?

DMA,全称Direct Memory Access,即直接存储器访问。
        DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。(从一个地址搬移到另外一个地址)
       我们知道CPU有转移数据、计算、控制程序转移等很多功能,系统运作的核心就是CPU,CPU无时不刻的在处理着大量的事务,但有些事情却没有那么重要,比方说数据的复制和存储数据,如果我们把这部分的CPU资源拿出来,让CPU去处理其他的复杂计算事务,是不是能够更好的利用CPU的资源呢?
        因此:转移数据(尤其是转移大量数据)是可以不需要CPU参与。比如希望外设A的数据拷贝到外设B,只要给两种外设提供一条数据通路,直接让数据由A拷贝到B 不经过CPU的处理。(外设地址:就是外设存储数据的寄存器地址,如串口(u32)&USART1->DR)
在这里插入图片描述
DMA定义:
        DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。

二、DMA传输方式

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

1.外设到内存
2.内存到外设
3.内存到内存
4.外设到外设

1.DMA传送参数

        我们知道,数据传输,首先需要的是1 数据的源地址 2 数据传输位置的目标地址 ,3 传递数据多少的数据传输量 ,4 进行多少次传输的传输模式 ,所以DMA所需要的核心参数,便是这四个。

2.DMA的主要特征

DMA的主要特征
        每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
o 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和         低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推);
o 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址     必须按数据传输宽度对齐;
o 支持循环的缓冲器管理;
o 每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成     为一个单独的中断请求;
o 存储器和存储器间的传输、外设和存储器、存储器和外设之间的传输;
o 闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标;
o 可编程的数据传输数目:最大为65535。

3.DMA映射表

以stm32F4系列为例

在这里插入图片描述
在这里插入图片描述

4.DMA工作的系统框图

在这里插入图片描述
        上方的框图,我们可以看到STM32内核,存储器,外设及DMA的连接,都是通过各种各样的线连接到总线矩阵中,他们之间的数据转移都经过总线矩阵的协调,使各个外设和谐的使用总线来传输数据。
        我们对他来进行一点一点的分析:
        下面看有与没有DMA的情况下,ADC采集的数据是怎样存放到SRAM中的
Δ没有DMA
1.如果没有DMA,CPU传输数据还要以内核作为中转站,比如要将ADC采集的数据转移到到SRAM中,这个过程是这样的:
o 内核通过DCode经过总线矩阵协调,从获取AHB存储的外设ADC采集的数据(红色线)
o 然后内核再通过DCode经过总线矩阵协调把数据存放到内存SRAM中。
在这里插入图片描述
Δ有DMA传输
有DMA的话:
o DMA传输时外设对DMA控制器发出请求。
o DMA控制器收到请求,触发DMA工作。
o DMA控制器从AHB外设获取ADC采集的数据,存储到DMA通道中
o DMA控制器的DMA总线与总线矩阵协调,使用AHB把外设ADC采集的数据经由DMA通道存放到      SRAM中,这个数据的传输过程中,完全不需要内核的参与,也就是不需要CPU的参与。
在这里插入图片描述
我们把上面的步骤使用专业一点介绍:
在发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。DMA传输结束,如果有更多的请求时,外设可以启动下一个周期。

5.DMA传输方式

方法1:DMA_Mode_Normal,正常模式,
当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次 
方法2:DMA_Mode_Circular ,循环传输模式
当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。也就是多次传输模式。

仲裁器
在这里插入图片描述
    仲裁器的作用是确定各个DMA传输的优先级。
    仲裁器根据通道请求的优先级来启动外设/存储器的访问。
优先权管理分2个阶段:
软件:每个通道的优先权可以在DMA_CCRx寄存器中设置,有4个等级:
o 最高优先级
o 高优先级
o 中等优先级
o 低优先级;
硬件:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优先权。比如:如果软件优先级相同,通道2优先于通道4。

注意:在大容量产品和互联型产品中,DMA1控制器拥有高于DMA2控制器的优先级。

DMA数据流(仅存在于STM32F4 /M4 内核上)
        在设置了DMA的通道之后,还要选择通道对应外设的数据流。
        8 个 DMA 控制器数据流都能够提供源和目标之间的单向传输链路。每个数据流配置后都可以执行:
o 常规类型事务:存储器到外设、外设到存储器或存储器到存储器的传输。
o 双缓冲区类型事务:使用存储器的两个存储器指针的双缓冲区传输(当 DMA 正在进行自/至缓冲区的读/写操作时,应用程序可以进行至/自其它缓冲区的写/读操作)。要传输的数据量(多达 65535)可以编程,并与连接到外设 AHB 端口的外设(请求 DMA 传输)的源宽度相关。每个事务完成后,包含要传输的数据项总量的寄存器都会递减。
        DMA_SxCR 寄存器控制数据流到底使用哪一个通道,每个数据流有 8 个通道可供选择,每次只能选择其中一个通道进行 DMA 传输。接下来,我们看看 DMA2 的各数据流通道映射表,如表:
在这里插入图片描述

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

可编程的数据量:
外设和存储器的传输数据量可以通过DMA_CCRx寄存器中的PSIZE和MSIZE位编程。

指针递增模式
        根据 DMA_SxCR 寄存器中 PINC 和 MINC 位的状态,外设和存储器指针在每次传输后可以自动向后递增或保持常量。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值。
        通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用。
        如果使能了递增模式,则根据在 DMA_SxCR 寄存器 PSIZE 或 MSIZE 位中编程的数据宽度,下一次传输的地址将是前一次传输的地址递增 1个数据宽度、2个数据宽度或 4个数据宽度。

存储器到存储器模式
        DMA通道的操作可以在没有外设请求的情况下进行,这种操作就是存储器到存储器模式。
        当设置了DMA_CCRx寄存器中的MEM2,MEM位之后,在软件设置了DMA_CCRx寄存器中的EN位启动DMA通道时,DMA传输将马上开始。当DMA_CNDTRx寄存器变为0时,DMA传输结束。存储器到存储器模式不能与循环模式同时使用。
这里要注意仅 DMA2 的外设接口可以访问存储器,所以仅 DMA2 控制器支持存储器到存储器的传输,DMA1 不支持。
        存储器到存储器模式不能与循环模式同时使用。
DMA中断
        每个DMA通道都可以在DMA传输过半、传输完成和传输错误时产生中断。为应用的灵活性考虑,通过设置寄存器的不同位来打开这些中断。
在这里插入图片描述

三,DMA结构体简介

结构体:

void LL_DMA_StructInit(LL_DMA_InitTypeDef *DMA_InitStruct)
{
  /* Set DMA_InitStruct fields to default values */
  DMA_InitStruct->PeriphOrM2MSrcAddress  = 0x00000000U;
  DMA_InitStruct->MemoryOrM2MDstAddress  = 0x00000000U;
  DMA_InitStruct->Direction              = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
  DMA_InitStruct->Mode                   = LL_DMA_MODE_NORMAL;
  DMA_InitStruct->PeriphOrM2MSrcIncMode  = LL_DMA_PERIPH_NOINCREMENT;
  DMA_InitStruct->MemoryOrM2MDstIncMode  = LL_DMA_MEMORY_NOINCREMENT;
  DMA_InitStruct->PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
  DMA_InitStruct->MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
  DMA_InitStruct->NbData                 = 0x00000000U;
  DMA_InitStruct->Channel                = LL_DMA_CHANNEL_0;
  DMA_InitStruct->Priority               = LL_DMA_PRIORITY_LOW;
  DMA_InitStruct->FIFOMode               = LL_DMA_FIFOMODE_DISABLE;
  DMA_InitStruct->FIFOThreshold          = LL_DMA_FIFOTHRESHOLD_1_4;
  DMA_InitStruct->MemBurst               = LL_DMA_MBURST_SINGLE;
  DMA_InitStruct->PeriphBurst            = LL_DMA_PBURST_SINGLE;
}
  DMA_InitStruct->PeriphOrM2MSrcAddress          外设地址
  DMA_InitStruct->MemoryOrM2MDstAddress          存储器地址
  DMA_InitStruct->Direction                      传输方向
  DMA_InitStruct->Mode
  * @arg @ref LL_DMA_MODE_NORMAL
  * @arg @ref LL_DMA_MODE_CIRCULAR         循环模式 (Circular mode)
  * @arg @ref LL_DMA_MODE_PFCTRL           外设流控制器 (Peripheral flow controller)
  DMA_InitStruct->PeriphOrM2MSrcIncMode    外设递增模式 (Peripheral increment mode)
  DMA_InitStruct->MemoryOrM2MDstIncMode    存储器递增模式 (Memory increment mode)
  DMA_InitStruct->PeriphOrM2MSrcDataSize   外设数据大小 (Peripheral data size)
* @arg @ref   字节(8 位) 
* @arg @ref   半字(16 位) 
* @arg @ref   字(32 位)
  DMA_InitStruct->MemoryOrM2MDstDataSize   存储器数据大小 (Memory data size)
  DMA_InitStruct->NbData                   要传输的数据项数目 (Number of data items to transfer)
  DMA_InitStruct->Channel                  通道选择 (Channel selection)
  DMA_InitStruct->Priority                 优先级 (Priority level)
  DMA_InitStruct->FIFOMode                 DMDIS:直接模式禁止 (Direct mode disable)
  DMA_InitStruct->FIFOThreshold            FIFO 阈值选择 (FIFO threshold selection)
  DMA_InitStruct->MemBurst                 MBURST:存储器突发传输配置 (Memory burst transfer configuration)
* @arg @ref   单次传输 
* @arg @ref   INCR4(4 个节拍的增量突发传输) 
* @arg @ref   INCR8(8 个节拍的增量突发传输) 
* @arg @ref   INCR16(16 个节拍的增量突发传输)
  DMA_InitStruct->PeriphBurst              外设突发传输配置 (Peripheral burst transfer configuration)       

FIFOMode,FIFOThreshold ,MemBurst ,PeriphBurst都与FIFO配置有关,只有在FIFO模式下,参数起作用
DMA传输类型分为直接模式和突发模式
FIFO一共16个字节
MBURST = INCR8意思是每次传输8*MSIZE数据,且FIFO必须是此乘积的整数倍,
比如:
MBURST = INCR8
MSIZE = 字节
一次传输的数据量 = 8 x 字节 = 8字节
此时FIFO有两种可能, 8 x 1 = 8 或者 8 x 2 = 16
如果选择FIFO = 8 ,那么一次突发就刚好满足FIFO大小
如果选择FIFO = 16, 那么需要两次突发 2 x 8 = 16,因此需要两次突发
注意:
1.这里突发后的目的地是FIFO,当FIFO达到对应的阈值后开始发送。
2.突发模式需要配合FIFO使用!
3.这里的MSIZE不会决定最后输出数据的宽度,输出的数据宽度由数组的类型确定,此MSIZE只是用来确定突发的次数。

在这里插入图片描述
FIFO和直接模式
在这里插入图片描述
直接模式
默认情况下, FIFO 以直接模式操作,不使用 FIFO阈值级别。如果在每次 DMA 请求之后,系统需要至/自存储器的立即和单独传输,这种模式非常有用。因为把FIFO填满也是需要一点时间的。
当在直接模式(禁止 FIFO)下将 DMA 配置为以存储器到外设模式传输数据时, DMA 会将一个数据从存储器预加载到内部 FIFO,从而确保一旦外设触发 DMA 请求时则立即传输数据。这里的一个数据,指的应该是一个字节。
为了避免 FIFO 饱和,建议使用高优先级配置相应的数据流。
注意:不允许源与目标的传输数据宽度不同,二者必须相等!
当实现存储器到存储器传输时不得使用直接模式。
FIFO模式
FIFO用于在源数据传输到目标之前临时存储这些数据。
FIFO是四级32位的缓冲区 = 16字节缓冲区
FIFO可以调节缓冲区可用大小,可以调节成1/4(4字节)、 1/2(8字节)、 3/4(12字节)和满(16字节)。当数据存储量大于设定值时, FIFO内容将传输到目标中。
FIFO对于要求源地址和目标地址数据宽度不同时非常有用,比如源数据是源源不断的字节数据, 而目标地址要求输出字宽度的数据, 即在实现数据传输时同时把原来4个8位字节的数据拼凑成一个32位字数据。此时使用FIFO功能先把数据缓存起来, 分别根据需要输出数据。这个也就是数据封装和解封的功能。

四,STM32cubeMX DMA配置(LL库)

以F411(ADC-DMA)为例,STM32cubeMX配置后DMA还不能工作,需要加上部分代码才可以,
界面上选择Mode ,Increment Address 地址 ,Data Width ,Use Fifo 不使能
在这里插入图片描述

 LL_DMA_SetChannelSelection(DMA2, LL_DMA_STREAM_0, LL_DMA_CHANNEL_0);

  LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_STREAM_0, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

  LL_DMA_SetStreamPriorityLevel(DMA2, LL_DMA_STREAM_0, LL_DMA_PRIORITY_MEDIUM);

  LL_DMA_SetMode(DMA2, LL_DMA_STREAM_0, LL_DMA_MODE_CIRCULAR);

  LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_STREAM_0, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_STREAM_0, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA2, LL_DMA_STREAM_0, LL_DMA_PDATAALIGN_HALFWORD);

  LL_DMA_SetMemorySize(DMA2, LL_DMA_STREAM_0, LL_DMA_MDATAALIGN_HALFWORD);

  LL_DMA_DisableFifoMode(DMA2, LL_DMA_STREAM_0);
//加上代码
	LL_DMA_SetDataLength(DMA2,LL_DMA_STREAM_0,5);//设置数据项宽度
	LL_DMA_SetPeriphAddress(DMA2,LL_DMA_STREAM_0,LL_ADC_DMA_GetRegAddr(ADC1,LL_ADC_DMA_REG_REGULAR_DATA));//设置外设地址
	LL_DMA_SetMemoryAddress(DMA2,LL_DMA_STREAM_0,(uint32_t)ADC1_Value);//设置存储器地址
    LL_DMA_EnableStream(DMA2,LL_DMA_STREAM_0);//使能DMA

或者直接给DMA结构体直接赋值

    DMA_InitStructure.DMA_Channel = DMA_Channel_0;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&(ADC1_Value);
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_InitStructure.DMA_BufferSize = 5;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA2_Stream0, &DMA_InitStructure);
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值