STM32DMA实验

1.1STM32F4 DMA简介

DMA,全称Direct Memory Access ,即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送的同类,能使CPU的效率大大提高。

STM32F4 最多有 2 个 DMA 控制器(DMA1 和 DMA2),共 16 个数据流(每个控制器 8 个),每一个 DMA 控制器都用于管理一个或多个外设的存储器访问请求。每个数据流总共可以有多达 8个通道(或称请求)。每个数据流通道都有一个仲裁器,用于处理 DMA 请求间的优先级。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

介绍一下仲裁器:

仲裁器为两个 AHB 主端口(存储器和外设端口)提供基于请求优先级的 8 个 DMA 数据流请
求管理,并启动外设/存储器访问序列。
优先级管理分为两个阶段:
           ● 软件:每个数据流优先级都可以在 DMA_SxCR 寄存器中配置。分为四个级别:
— 非常高优先级
— 高优先级
— 中优先级
— 低优先级
           ● 硬件:如果两个请求具有相同的软件优先级,则编号低的数据流优先于编号高的数据
流。例如,数据流 2 的优先级高于数据流 4。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

DMA数据流

8 个 DMA 控制器数据流都能够提供源和目标之间的单向传输链路。每个数据流配置后都可以执行:
            ● 常规类型事务:存储器到外设、外设到存储器或存储器到存储器的传输。
           ● 双缓冲区类型事务:使用存储器的两个存储器指针的双缓冲区传输(当 DMA 正在进行自/至缓冲区的读/写操作时,应用程序可以进行至/自其它缓冲区的写/读操作)。要传输的数据量(多达 65535)可以编程,并与连接到外设 AHB 端口的外设(请求 DMA 传输)的源宽度相关。每个事务完成后,包含要传输的数据项总量的寄存器都会递减。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

扫描二维码关注公众号,回复: 3343677 查看本文章

指针递减

根据 DMA_SxCR 寄存器中 PINC 和 MINC 位的状态,外设和存储器指针在每次传输后可以自动向后递增或保持常量。
通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用。如果使能了递增模式,则根据在 DMA_SxCR 寄存器 PSIZE 或 MSIZE 位中编程的数据宽度,下一次传输的地址将是前一次传输的地址递增 1(对于字节)、2(对于半字)或 4(对
于字)。为了优化封装操作,可以不管 AHB 外设端口上传输的数据的大小,将外设地址的增量偏移大小固定下来。DMA_SxCR 寄存器中的 PINCOS 位用于将增量偏移大小与外设 AHB 端口或32 位地址(此时地址递增 4)上的数据大小对齐。PINCOS 位仅对 AHB 外设端口有影响。如果将 PINCOS 位置 1,则不论 PSIZE 值是多少,下一次传输的地址总是前一次传输的地址递增 4(自动与 32 位地址对齐)。但是,AHB 存储器端口不受此操作影响。如果 AHB 外设端口或 AHB 存储器端口分别请求突发事务,为了满足 AMBA 协议(在固定地址模式下不允许突发事务),则需要将 PINC 或 MINC 位置 1。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

循环模式

循环模式可用于处理循环缓冲区和连续数据流(例如 ADC 扫描模式)。可以使用 DMA_SxCR寄存器中的 CIRC 位使能此特性。当激活循环模式时,要传输的数据项的数目在数据流配置阶段自动用设置的初始值进行加载,并继续响应 DMA 请求。

 


STM32F4 的 DMA 有以下一些特性:
           ● 双 AHB 主总线架构,一个用于存储器访问,另一个用于外设访问
           ● 仅支持 32 位访问的 AHB 从编程接口
           ● 每个 DMA 控制器有 8 个数据流,每个数据流有多达 8 个通道(或称请求)
           ● 每个数据流有单独的四级 32 位先进先出存储器缓冲区(FIFO),可用于 FIFO 模式或直
接模式。
           ● 通过硬件可以将每个数据流配置为:
         1,支持外设到存储器、存储器到外设和存储器到存储器传输的常规通道
         2,支持在存储器方双缓冲的双缓冲区通道
           ● 8 个数据流中的每一个都连接到专用硬件 DMA 通道(请求)
           ● DMA 数据流请求之间的优先级可用软件编程(4 个级别:非常高、高、中、低),在
软件优先级相同的情况下可以通过硬件决定优先级(例如,请求 0 的优先级高于请求 1)
           ● 每个数据流也支持通过软件触发存储器到存储器的传输(仅限 DMA2 控制器)
           ● 可供每个数据流选择的通道请求多达 8 个。此选择可由软件配置,允许几个外设启动
DMA 请求
           ● 要传输的数据项的数目可以由 DMA 控制器或外设管理:
         1,DMA 流控制器:要传输的数据项的数目是 1 到 65535,可用软件编程
         2,外设流控制器:要传输的数据项的数目未知并由源或目标外设控制,这些外设通过硬
件发出传输结束的信号
           ● 独立的源和目标传输宽度(字节、半字、字):源和目标的数据宽度不相等时,DMA
自动封装/解封必要的传输数据来优化带宽。这个特性仅在 FIFO 模式下可用。
           ● 对源和目标的增量或非增量寻址
           ● 支持 4 个、8 个和 16 个节拍的增量突发传输。突发增量的大小可由软件配置,通常等
于外设 FIFO 大小的一半
          ● 每个数据流都支持循环缓冲区管理
          ● 5 个事件标志(DMA 半传输、DMA 传输完成、DMA 传输错误、DMA FIFO 错误、
直接模式错误),进行逻辑或运算,从而产生每个数据流的单个中断请求

DMA 控制器执行直接存储器传输:因为采用 AHB 主总线,它可以控制 AHB 总线矩阵来
启动 AHB 事务。它可以执行下列事务:
1,外设到存储器的传输
2, 存储器到外设的传输
3,存储器到存储器的传输

这里特别注意一下,存储器到存储器需要外设接口可以访问存储器,而仅 DMA2 的外设接
口可以访问存储器,所以仅 DMA2 控制器支持存储器到存储器的传输,DMA1 不支持。

从上图可以看出,DMA_SxCR 控制数据流到底使用哪一个通道,每个数据流有 8 个通道可
供选择,每次只能选择其中一个通道进行 DMA 传输。接下来,我们看看 DMA2 的各数据流通
道映射表,如表 28.1.1 所示:

接下来,我们介绍一下 DMA 设置相关的几个寄存器:

第一个是 DMA 中断状态寄存器,该寄存器总共有 2 个:DMA_LISR 和 DMA_HISR,每个寄存器管理 4 数据流(总共 8 个),DMA_LISR 寄存器用于管理数据流 0~3,而 DMA_HISR 用于管理数据流 4~7。这两个寄存器各位描述都完全一模一样,只是管理的数据流不一样。

这里,我们仅以 DMA_LISR 寄存器为例进行介绍,DMA_LISR 各位描述如图:

如果开启了 DMA_LISR 中这些位对应的中断,则在达到条件后就会跳到中断服务函数里面去,即使没开启,我们也可以通过查询这些位来获得当前 DMA 传输的状态。这里我们常用的是 TCIFx位,即数据流 x 的 DMA 传输完成与否标志。注意此寄存器为只读寄存器,所以在这些位被置位之后,只能通过其他的操作来清除。DMA_HISR 寄存器各位描述通 DMA_LISR 寄存器各位描述完全一样,只是对应数据流 4~7,这里我们就不列出来了。

第二个是 DMA 中断标志清除寄存器, 该寄存器同样有 2 个:DMA_LIFCR 和 DMA_HIFCR,同样是每个寄存器控制 4 个数据流,DMA_LIFCR 寄存器用于管理数据流 0~3,而 DMA_ HIFCR 用于管理数据流 4~7。这两个寄存器各位描述都完全一模一样,只是管理的数据流不一样。

以 DMA_LIFCR 寄存器为例进行介绍:

DMA_LIFCR 的各位就是用来清除 DMA_LISR 的对应位的,通过写 1 清除。在 DMA_LISR 被置位后,我们必须通过向该位寄存器对应的位写入 1 来清除。DMA_HIFCR 的使用同 DMA_LIFCR 类似,这里就不做介绍了。

第三个是 DMA 数据流 x 配置寄存器(DMA_SxCR)该寄存器的我们在这里就=不贴出来了,见《STM32F4xx 中文参考手册》第 223 页 9.5.5 一节。

该寄存器控制着 DMA 的很多相关信息,包括数据宽度、外设及存储器的宽度、优先级、增量模式、传输方向、中断允许、使能等都是通过该寄存器来设置的。所以 DMA_ SxCR 是 DMA 传输的核心控制寄存器。

第四个是 DMA 数据流 x 数据项数寄存器(DMA_SxNDTR)。这个寄存器控制 DMA 数据流 x 的每次传输所要传输的数据量。其设置范围为 0~65535。并且该寄存器的值会随着传输的进行而减少,当该寄存器的值为 0 的时候就代表此次数据传输已经全部发送完成了。所以可以通过这个寄存器的值来知道当前 DMA 传输的进度。特别注意,这里是数据项数目,而不是指的字节数。比如设置数据位宽为 16 位,那么传输一次(一个项)就是 2 个字节。

第五个是 DMA 数据流 x 的外设地址寄存器(DMA_SxPAR)。该寄存器用来存储 STM32F4 外设的地址,比如我们使用串口 1,那么该寄存器必须写入 0x40011004(其实就是&USART1_DR)。如果使用其他外设,就修改成相应外设的地址就行了。最后一个是 DMA 数据流 x 的存储器地址寄存器,由于 STM32F4 的 DMA 支持双缓存,所以存储器地址寄存器有两个:DMA_SxM0AR 和 DMA_SxM1AR,其中 DMA_SxM1AR 仅在双缓冲模式下,才有效。本章我们没用到双缓冲模式,所以存储器地址寄存器就是:DMA_SxM0AR,该寄存器和DMA_CPARx 差不多,但是是用来放存储器的地址的。比如我们使用 SendBuf[8200]数组来做存储器,那么我们在 DMA_SxM0AR 中写入&SendBuff 就可以了。

参考《STM32F4xx中文参考手册》第 9.5 节

范例:

串口一的发送,串口 1 的发送,属于 DMA2 的数据流 7,通道 4,接下来我们就介绍下使用库函数的配置步骤和方法。首先这里我们需要指出的是,DMA 相关的库函数支持在文件 stm32f4xx_dma.c 以及对应的头文件 stm32f4xx_dac.h 中。具体步骤如下:

1 )使能 2 DMA2  时钟,并等待数据流可配置 。

DMA 的时钟使能是通过 AHB1ENR 寄存器来控制的,这里我们要先使能时钟,才可以配置 DMA相关寄存器。所以先要使能 DMA2 的时钟。另外,要对配置寄存器(DMA_SxCR)进行设置,必须先等待其最低位为 0(也就是 DMA 传输禁止了),才可以进行配置。库函数使能 DMA2 时钟的方法为:

等待 DMA 可配置,也就是等待 DMA_SxCR 寄存器最低位为 0 的方法为:

2)  初始化 2 DMA2  数据流 7  ,包括配置通道,外设地址,存储器地址,传输数据量等DMA 的某个数据流各种配置参数初始化是通过 DMA_Init 函数实现的

函数的第一个参数是指定初始化的 DMA 的数据流编号,这个很容易理解。入口参数范围为:DMAx_Stream0~ DMAx_Stream7(x=1,2)。下面我们主要看看第二个参数。跟其他外设一样,同样是通过初始化结构体成员变量值来达到初始化的目的,下面我们来看看 DMA_InitTypeDef
结构体的定义:

这个结构体的成员比较多,但是每个成员变量的意义我们在前面基本都已经讲解过,这里
我们一一做个简要的介绍。
           第一个参数 DMA_Channel 用来设置 DMA 数据流对应的通道。前面我们已经讲解过,可供每个数据流选择的通道请求多达 8 个,取值范围为:DMA_Channel_0~ DMA_Channel_7。
           第二个参数 DMA_PeripheralBaseAddr 用来设置 DMA 传输的外设基地址,比如要进行串口DMA 传输,那么外设基地址为串口接受发送数据存储器 USART1->DR 的地址,表示方法为&USART1->DR。
           第三个参数 DMA_Memory0BaseAddr 为内存基地址,也就是我们存放 DMA 传输数据的内存地址。
           第四个参数 DMA_DIR 设置数据传输方向,决定是从外设读取数据到内存还送从内存读取数据发送到外设,也就是外设是源地还是目的地,这里我们设置为从内存读取数据发送到串口,所以外设自然就是目的地了,所以选择值为 DMA_DIR_PeripheralDST。
           第五个参数 DMA_BufferSize 设置一次传输数据量的大小,这个很容易理解。
           第六个参数 DMA_PeripheralInc 设置传输数据的时候外设地址是不变还是递增。如果设置为递增,那么下一次传输的时候地址加 1,这里因为我们是一直往固定外设地址&USART1->DR发送数据,所以地址不递增,值为 DMA_PeripheralInc_Disable;
           第七个参数 DMA_MemoryInc 设置传输数据时候内存地址是否递增。这个参数和DMA_PeripheralInc 意思接近,只不过针对的是内存。这里我们的场景是将内存中连续存储单元的数据发送到串口,毫无疑问内存地址是需要递增的,所以值为 DMA_MemoryInc_Enable。
           第八个参数 DMA_PeripheralDataSize 用来设置外设的数据长度是为字节传输(8bits),半字传输(16bits)还是字传输(32bits),这里我们是 8 位字节传输,所以值设置为DMA_PeripheralDataSize_Byte。

第九个参数 DMA_MemoryDataSize 是用来设置内存的数据长度,和第七个参数意思接近,这里我们同样设置为字节传输 DMA_MemoryDataSize_Byte。

第十个参数 DMA_Mode 用来设置 DMA 模式是否循环采集,也就是说,比如我们要从内存中采集 64 个字节发送到串口,如果设置为重复采集,那么它会在 64 个字节采集完成之后继续从内存的第一个地址采集,如此循环。这里我们设置为一次连续采集完成之后不循环。所以设置值为 DMA_Mode_Normal。在我们下面的实验中,如果设置此参数为循环采集,那么你会看到串口不停的打印数据,不会中断,大家在实验中可以修改这个参数测试一下。
           第十一个参数 DMA_Priority 是用来设置 DMA 通道的优先级,有低,中,高,超高三种模式,这个在前面讲解过,这里我们设置优先级别为中级,所以值为 DMA_Priority_Medium。优先级可以随便设置,因为我们只有一个数据流被开启了。假设有多个数据流开启(最多 8 个),那么就要设置优先级了,DMA 仲裁器将根据这些优先级的设置来决定先执行那个数据流的 DMA。优先级越高的,越早执行,当优先级相同的时候,根据硬件上的编号来决定哪个先执行(编号越小越优先)。
           第十二个参数 DMA_FIFOMode 用来设置是否开启 FIFO 模式。这里我们不开启所以选择DMA_FIFOMode_Disable。
          第十三个参数 DMA_FIFOThreshold 用来选择 FIFO 阈值。根据前面讲解可以为 FIFO 容量的1/4,1/2,3/4 以及 1 倍。这里我们实际并没有开启 FIFO 模式,所以可以不关心。
      第十四个参数 DMA_MemoryBurst 用来配置存储器突发传输配置。可以选择为 4 个节拍的增量突发传输 DMA_MemoryBurst_INC4,8 个节拍的增量突发传输 DMA_MemoryBurst_INC8,16 个街拍的增量突发传输 DMA_MemoryBurst_INC16 以及单次传输 DMA_MemoryBurst_Single。
           第十五个参数 DMA_PeripheralBurst 用来配置外设突发传输配置。跟前面一个参数DMA_MemoryBurst 作用类似,只不过一个针对的是存储器,一个是外设。这里我们选择单次传输 DMA_PeripheralBurst_Single。

3 )使能串口 1  的 DMA  发送

进行 DMA 配置之后,我们就要开启串口的 DMA 发送功能,使用的函数是:

如果是要使能串口 DMA 接受,那么第二个参数修改为 USART_DMAReq_Rx 即可

4 )使能  DMA2数据流 7 ,启动传输。

使能 DMA 数据流的函数为:

使能 DMA2_Stream7,启动传输的方法为:

通过以上 4 步设置,我们就可以启动一次 USART1 的 DMA 传输了。

5 )查询  DMA 

在 DMA 传输过程中,我们要查询 DMA 传输通道的状态,使用的函数是:

比如我们要查询 DMA 数据流 7 传输是否完成,方法是:

附加一些相关知识:

 

猜你喜欢

转载自blog.csdn.net/qq_38721302/article/details/82754284