STM32F4 DMA

【 1. 简介 】

  • DMA 全称Direct Memory ,即直接存储器访问。
  • 特点
    DMA传输将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的,无需CPU直接控制传输 ,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。
  • 作用
    通过DMA控制器控制、硬件传输,为CPU减负 。
    在这里插入图片描述在这里插入图片描述

【 2. 系统框图 】

  • DMA 控制器执行直接存储器传输:因为采用 AHB 主总线,它可以控制 AHB 总线矩阵来启动 AHB 事务,执行下列事务:
    • 外设到存储器的传输
    • 存储器到外设的传输
    • 存储器到存储器的传输
  • DMA 控制器提供两个 AHB 主端口: AHB 存储器端口(用于连接存储器)和 AHB 外设端口(用于连接外设)。但是,要执行存储器到存储器的传输, AHB 外设端口必须也能访问存储器。而仅 DMA2 的外设接口可以访问存储器,所以 仅 DMA2 控制器支持 存储器到存储器 的传输, DMA1 不支持。
    在这里插入图片描述

在这里插入图片描述

【 3. DMA 事务 】

  • DMA 事务由给定数目的数据传输序列组成,要传输的数据项的数目、宽度( 8 位、 16 位或 32 位)可用软件编程。
  • 每个 DMA 传输包含三项操作:
    • 通过 DMA_SxPAR 或 DMA_SxM0AR 寄存器寻址,1. 从外设数据寄存器或存储器单元中加载数据
    • 通过 DMA_SxPAR 或 DMA_SxM0AR 寄存器寻址,2. 将加载的数据存储到外设数据寄存器或存储器单元
    • DMA_SxNDTR 3. 计数器在数据存储结束后递减,该计数器中包含仍需执行的事务数
  • 在产生事件后,外设会向 DMA 控制器发送请求信号,DMA 控制器根据通道优先级处理该请求。
    即:产生事件后---->外设---->发送请求信号---->DMA控制器(处理该请求)
    只要 DMA 控制器访问外设, DMA 控制器就会向外设发送确认信号。外设获得 DMA 控制器的确认信号后,便会立即释放其请求。
    即:DMA控制器---->发送确认信号---->外设(释放该请求)
    一旦外设使请求失效, DMA 控制器就会释放确认信号。如果有更多请求,外设可以启动下一个事务。
    即:外设使请求失效---->DMA控制器(释放该确认信号)

【 4. 通道选择 】

  • STM32F4 最多有2个DMA控制器。
  • 每个 DMA控制器 都用于管理一个或者多个外设的存储器访问请求,每个控制器有8个数据流。
  • 每个 数据流 有多达8个通道或请求,每次只能选择其中一个通道进行 DMA 传输。
  • 每个 通道 都有一个仲裁器,用于处理DMA请求间的优先级。
    在这里插入图片描述
  • 如下图所示,数据流的多通道选择,是通过 DMA_SxCR 寄存器控制。
    在这里插入图片描述
  • 如下图所示,为 DMA1和DMA2 的各数据流通道映射表 。每个表总共 64 种组合。
    比如要实现串口 1 的 DMA 发送,即 USART1_TX,就可以选择 DMA2 的数据流 7,通道 4,来进行 DMA 传输。另外,有的外设(比如 USART1_RX)可能有多个通道可以选择,随意选择一个就可以了。
    在这里插入图片描述在这里插入图片描述

【 5. 仲裁器 】

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

【 6. DMA数据流 】

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

【 7. 源、目标和传输模式 】

  • 源传输和目标传输在整个 4 GB 区域(地址在 0x0000 0000 和 0xFFFF FFFF 之间)都可以寻址外设和存储器。
  • 传输方向使用 DMA_SxCR 寄存器中的 DIR[1:0] 位进行配置,有三种可能的传输方向:存储器到外设、外设到存储器或存储器到存储器。 表 37 介绍了相应的源和目标地址。
    在这里插入图片描述
  • 当数据宽度(在 DMA_SxCR 寄存器的 PSIZE 或 MSIZE 位中编程)分别是半字或字时,写入 DMA_SxPAR 或 DMA_SxM0AR / DMA_SxM1AR 寄存器的外设或存储器地址必须分别在字或半字地址的边界对齐

外设到存储器模式

  • 使能这种模式(将 DMA_SxCR 寄存器中的位 EN 置 1) 时,每次产生外设请求,数据流都会启动数据源到 FIFO 的传输 ,达到 FIFO 的阈值级别时, FIFO 的内容移出并存储到目标中。
  • 令传输终止的方式:如果 DMA_SxNDTR 寄存器达到零、外设请求传输终止(在使用外设流控制器的情况下)或 DMA_SxCR 寄存器中的 EN 位由软件清零,传输即会停止。
  • 在直接模式下(当 DMA_SxFCR 寄存器中的 DMDIS 值为“0”时),不使用 FIFO 的阈值级别控制:每完成一次从外设到 FIFO 的数据传输后,相应的数据立即就会移出并存储到目标中。
  • 只有赢得了数据流的仲裁后,相应数据流才有权访问 AHB 源或目标端口,系统使用在 DMA_SxCR 寄存器 PL[1:0] 位中为每个数据流定义的优先级执行仲裁。
    在这里插入图片描述

存储器到外设模式

  • 使能这种模式(将 DMA_SxCR 寄存器中的 EN 位置 1)时,数据流会立即启动传输,从源完全填充 FIFO。
  • 每次发生外设请求, FIFO 的内容都会移出并存储到目标中。当 FIFO 的级别小于或等于预定义的阈值级别时,将使用存储器中的数据完全重载 FIFO。
  • 令传输终止的方式:如果 DMA_SxNDTR 寄存器达到零、外设请求传输终止(在使用外设流控制器的情况下)或 DMA_SxCR 寄存器中的 EN 位由软件清零,传输即会停止。
  • 在直接模式下(当 DMA_SxFCR 寄存器中的 DMDIS 值为“0”时),不使用 FIFO 的阈值级别。一旦使能了数据流, DMA 便会预装载第一个数据,将其传输到内部 FIFO。一旦外设请求数据传输, DMA 便会将预装载的值传输到配置的目标。然后,它会使用要传输的下一个数据再次重载内部空 FIFO。预装载的数据大小为 DMA_SxCR 寄存器中 PSIZE 位字段的值。
  • 只有赢得了数据流的仲裁后,相应数据流才有权访问 AHB 源或目标端口。系统使用在 DMA_SxCR 寄存器 PL[1:0] 位中为每个数据流定义的优先级执行仲裁。
    在这里插入图片描述

存储器到存储器

  • 通过将 DMA_SxCR 寄存器中的使能位 (EN) 置 1 来使能数据流时,数据流会立即开始填充 FIFO,直至达到阈值级别。达到阈值级别后, FIFO 的内容便会移出,并存储到目标中。
  • 令传输终止的方式:如果 DMA_SxNDTR 寄存器达到零或 DMA_SxCR 寄存器中的 EN 位由软件清零,传输即会停止。
  • 只有赢得了数据流的仲裁后,相应数据流才有权访问 AHB 源或目标端口。系统使用在 DMA_SxCR 寄存器 PL[1:0] 位中为每个数据流定义的优先级执行仲裁。
  • 注意: 使用存储器到存储器模式时,不允许循环模式和直接模式 只有 DMA2 控制器能够执行存储器到存储器的传输
    在这里插入图片描述

【 8. 指针递增 】

  • 根据 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。

【 9. 循环模式 】

  • 循环模式可用于处理循环缓冲区和连续数据流(例如 ADC 扫描模式),可以使用 DMA_SxCR 寄存器中的 CIRC 位使能此特性。
  • 当激活循环模式时,要传输的数据项的数目在数据流配置阶段自动用设置的初始值进行加载,并继续响应 DMA 请求。
  • 在循环模式下,如果为存储器配置了突发模式,必须遵循下列规则:
    如果不遵循此公式,则 DMA 行为和数据完整性得不到保证。
    NDTR 还必须是外设突发大小与外设数据大小乘积的倍数,否则会导致错误的 DMA 行为
    D M A — S x N D T R = ( ( M b u r s t 节 拍 ) × ( M s i z e ) / ( P s i z e ) ) 的 倍 数 DMA_{— }SxNDTR = ((Mburst 节拍) × (Msize)/(Psize)) 的倍数 DMASxNDTR=((Mburst)×(Msize)/(Psize))
    • DMA_SxNDTR
      AHB 外设端口上要传输的数据项的数目 (Number of data items to transfer)
      同时也是一个存储该数据的寄存器
    • Mburst
      存储器突发传输配置 (Memory burst transfer configuration)
      也叫节拍,其值= 4、 8 或 16
      是 DMA_SxCR 寄存器中的 MBURST[1:0] 位
    • Psize
      外设数据大小 (Peripheral data size)
      其值 = 1(00,字节,8 位)、2(01,半字,16 位)、4(10,字,32 位)
      DMA_SxCR 寄存器中的 Psize[1:0] 位
    • Msize
      存储器数据大小 (Memory data size)
      其值 = 1(00,字节,8 位)、2(01,半字,16 位)、4(10,字,32 位)
      DMA_SxCR 寄存器中的 MSIZE[1:0] 位
    • Msize / Psize
      其值 = 1、 2、 4、 1/2 或 1/4

    例如: Mburst 节拍 = 8 (INCR8), MSIZE =“00”(字节)和 PSIZE =“01”(半字),故 DMA_SxNDTR 必须是 (8 × 1/2 = 4) 的倍数。

【 10. 双缓冲区模式 】

  • 此模式可用于所有 DMA1 和 DMA2 数据流。

  • 通过将 DMA_SxCR 寄存器中的 DBM 位置 1,即可使能双缓冲区模式。

  • 除了有两个存储器指针之外,双缓冲区数据流的工作方式与常规(单缓冲区)数据流的一样。

  • 使能双缓冲区模式时,将自动使能循环模式( DMA_SxCR 中的 CIRC 位的状态是“无关”),并在每次事务结束时交换存储器指针。

  • 在此模式下, 每次事务结束时, DMA 控制器都从一个存储器目标交换为另一个存储器目标。这样,软件在处理一个存储器区域的同时, DMA 传输还可以填充/使用第二个存储器区域

  • 如表 38:双缓冲区模式下的源和目标地址寄存器 (DBM=1) 所述。
    在这里插入图片描述

  • 双缓冲区数据流可以双向工作(存储器既可以是源也可以是目标)。

  • 在双缓冲区模式下使能数据流时,可遵循下列条件,实时更新 AHB 存储器的基址( DMA_SxM0AR 或 DMA_SxM1AR):

    • 当 DMA_SxCR 寄存器中的 CT 位为“0”时,可以写入 DMA_SxM1AR 寄存器。当 CT=“1”时,试图写入此寄存器会将错误标志位 (TEIF) 置 1,并自动禁止数据流。
    • 当 DMA_SxCR 寄存器中的 CT 位为“1”时,可以写入 DMA_SxM0AR 寄存器。当 CT=“0”时,试图写入此寄存器会将错误标志位 (TEIF) 置 1,并自动禁止数据流。
      为避免出现任何错误状态,建议在 TCIF 标志位置位时立即更改基址。因为此时根据上述两个条件之一,目标存储器依据 DMA_SxCR 寄存器中 CT 值的情况,一定已从存储器 0 更改为存储器 1(或从存储器 1 更改为存储器 0)。
  • 对于所有其它模式(双缓冲区模式除外),一旦使能数据流,存储器地址寄存器即被写保护。

  • 由于存储器到存储器模式与循环模式不兼容,所以当使能双缓冲区模式时,不允许配置存储器到存储器模式。

【 11. 单次传输、突发传输 】

  • 单次传输:一次传输一个数据项。
  • 突发传输:一次传输 4 个、 8 个或 16 个数据项。
  • DMA 控制器可以产生 单次传输 或 4 个、 8 个或 16 个节拍增量的突发传输
  • 突发大小通过软件针对两个 AHB 端口独立配置,配置时使用 DMA_SxCR 寄存器中的 MBURST[1:0] 和 PBURST[1:0] 位,突发大小指示突发中的节拍数,而不是传输的字节数
  • 为确保数据一致性,形成突发的每一组传输都不可分割:在突发传输序列期间, AHB 传输会锁定,并且 AHB 总线矩阵的仲裁器不解除对 DMA 主总线的授权。
  • 根据单次或突发配置的情况,每个 DMA 请求在 AHB 外设端口上相应地启动不同数量的传输。
    • 当 AHB 外设端口被配置为 单次传输时,根据 DMA_SxCR 寄存器 PSIZE[1:0] 位的值,每个 DMA 请求产生一次字节、半字或字的数据传输
    • 当 AHB 外设端口被配置为 突发传输时,根据 DMA_SxCR 寄存器 PBURST[1:0] 和 PSIZE[1:0] 位的值,每个 DMA 请求相应地生成 4 个、 8 个或 16 个节拍的字节、半字或字的传输
    • 对于需要配置 MBURST 和 MSIZE 位的 AHB 存储器端口,必须考虑与上述相同的内容。
  • 在直接模式下,数据流只能生成单次传输,而 MBURST[1:0] 和 PBURST[1:0] 位由硬件强制配置。
  • 必须选择地址指针(DMA_SxPAR 或 DMA_SxM0AR 寄存器),以确保一个突发块内的所有传输在等于传输大小的地址边界对齐。
  • 选择突发配置必须要遵守 AHB 协议,即 突发传输不得越过 1 KB 地址边界 ,因为可以分配给单个从设备的最小地址空间是 1 KB。这意味着突发块传输不应越过 1 KB 地址边界,否则就会产生一个 AHB 错误,并且 DMA 寄存器不会报告这个错误。
  • 仅在使能指针递增模式时允许突发模式:
    当 PINC 位为“0”时,也应将 PBURST 位清为“00”
    当 MINC 位为“0”时,也应将 MBURST 位清为“00”

【 12. FIFO 】

FIFO 结构

  • FIFO 用于 在源数据传输到目标之前临时存储这些数据
  • 每个数据流都有一个独立的 4 字 FIFO,阈值级别可由软件配置为 1/4、 1/2、 3/4 或满。
  • 为了使能 FIFO 阈值级别,必须通过将 DMA_SxFCR 寄存器中的 DMDIS 位置 1 来 禁止直接模式
  • FIFO 的结构随源与目标数据宽度而不同,
    在这里插入图片描述

FIFO阈值与突发配置

  • 选择 FIFO 阈值(DMA_SxFCR 寄存器的位 FTH[1:0])和存储器突发大小(DMA_SxCR 寄存器的 MBURST[1:0] 位)时需要小心: FIFO 阈值指向的内容必须与整数个存储器突发传输完全匹配 。如果不是这样,当使能数据流时将生成一个 FIFO 错误( DMA_HISR 或 DMA_LISR寄存器的标志 FEIFx),然后将自动禁止数据流。
    在这里插入图片描述
  • 所有这些情况下, 突发大小与数据大小的乘积不得超过 FIFO 大小 (数据大小可以为: 1(字节)、 2(半字)或 4(字))。
  • 如果发生下列一种情况,会导致 DMA 传输结束时出现不完整的突发传输:
    在这些情况下,即使在 DMA 数据流配置期间请求突发事务,要传输的剩余数据也将由 DMA在单独模式下管理。
    • 对于 AHB 外设端口配置:数据项总数(在 DMA_SxNDTR 寄存器中设置)不是突发大小与数据大小乘积的倍数。
    • 对于 AHB 存储器端口配置:要传输到存储器的 FIFO 中的剩余数据项的数目不是突发大小与数据大小乘积的倍数。
  • 当在外设 AHB 端口上请求突发传输并且使用 FIFO(DMA_SxCR 寄存器中 DMDIS = 1)时,必须根据 DMA 数据流方向遵守下列规则来避免出现上溢或下溢情况:
    如果 (PBURST × PSIZE) = FIFO_SIZE( 4 字),则当 PSIZE = 1、 2 或 4, PBURST = 4、8 或 16 时,禁止 FIFO_Threshold = 3/4。
    此规则将确保一次释放足够的 FIFO 空间来处理外设的请求。

FIFO刷新

  • 当复位 DMA_SxCR 寄存器中的 EN 位来禁止数据流,以及配置数据流来管理外设到存储器或存储器到存储器的传输时,可以刷新 FIFO。因为如果禁止数据流时仍有某些数据存留在 FIFO 中, DMA 控制器会将剩余的数据继续传输到目标(即使已经有效禁止了数据流)。刷新完成时,会将 DMA_LISR 或 DMA_HISR 寄存器中的传输完成状态位 (TCIFx) 置 1。在这种情况下,剩余数据计数器 DMA_SxNDTR 保持的值指示在目标存储器现有多少可用数据项。
  • 请注意在 FIFO 刷新操作期间,如果 FIFO 中要传输到存储器的剩余数据项的数目(以字节为单位)小于存储器数据宽度(例如在 MSIZE 配置为字时 FIFO 中为 2 个字节),则会使用在 DMA_SxCR 寄存器中的 MSIZE 位设置的数据宽度发送数据。这意味着将使用非预期值写入存储器。软件可以读取 DMA_SxNDTR 寄存器来确定包含良好数据的存储器区域(起始地址和最后地址)。
  • 如果 FIFO 中剩余数据项的数目小于突发大小,并且数据流通过将 DMA_SxCR 寄存器 MBURST 位置 1 进行配置来管理在 AHB 存储器端口上的突发传输,则使用单次传输来完成 FIFO 的刷新。

直接模式

  • 默认情况下, FIFO 以直接模式操作(将 DMA_SxFCR 中的 DMDIS 位置 1),不使用 FIFO阈值级别。如果在每次 DMA 请求之后,系统需要至/自存储器的立即和单独传输,这种模式非常有用。
  • 当在直接模式(禁止 FIFO)下将 DMA 配置为以存储器到外设模式传输数据时, DMA 会将一个数据从存储器预加载到内部 FIFO,从而确保一旦外设触发 DMA 请求时则立即传输数据。
  • 为了避免 FIFO 饱和,建议使用高优先级配置相应的数据流,该模式仅限以下方式的传输:
    • 源和目标传输宽度相等,并均由 DMA_SxCR 中的 PSIZE[1:0] 位定义(MSIZE[1:0] 位的状态是“无关”)
    • 不可能进行突发传输(DMA_SxCR 中的 PBURST[1:0] 和 MBURST[1:0] 位的状态是“无关”)
  • 当实现存储器到存储器传输时不得使用直接模式。

【 13. 传输完成 】

以下各种事件均可以结束传输过程,并将 DMA_LISR 或 DMA_HISR 状态寄存器中的 TCIFx位置 1:

  • 在 DMA 流控制器模式下:
    • 在存储器到外设模式下, DMA_SxNDTR 计数器已达到零
    • 传输结束前禁止了数据流(通过将 DMA_SxCR 寄存器中的 EN 位清零),并在传输是外设到存储器或存储器到存储器的模式时,所有的剩余数据均已从 FIFO 刷新到存储器。
  • 在外设流控制器模式下:
    • 已从外设生成最后的外部突发请求或单独请求,并当 DMA 在外设到存储器模式下工作时,剩余数据已从 FIFO 传输到存储器
    • 数据流由软件禁止,并当 DMA 在外设到存储器模式下工作时,剩余数据已从 FIFO传输到存储器
  • 注意: 仅在外设到存储器模式下,传输的完成取决于 FIFO 中要传输到存储器的剩余数据 。这种情况不适用于存储器到外设模式。
  • 如果是在非循环模式下配置数据流,传输结束后(即要传输的数据数目达到零),除非软件重新对数据流编程并重新使能数据流(通过将 DMA_SxCR 寄存器中的 EN 位置 1),否则 DMA 即会停止传输(通过硬件将 DMA_SxCR 寄存器中的 EN 位清零)并且不再响应任何DMA 请求

【 14. 传输暂停 】

可以随时暂停 DMA 传输以供稍后重新开始;也可以在 DMA 传输结束前明确禁止暂停功能。
分为两种情况:

  • 数据流禁止传输,以后不从停止点重新开始暂停 。这种情况下,只需将 DMA_SxCR 寄存器中的 EN 位清零来禁止数据流,除此之外不需要任何其他操作。禁止数据流可能要花费一些时间(需要首先完成正在进行的传输),需要将传输完成中断标志(DMA_LISR 或 DMA_HISR 寄存器中的 TCIF)置 1 来指示传输结束。现在 DMA_SxCR 中的 EN 位的值是“0”,借此确认数据流已经终止传输。 DMA_SxNDTR 寄存器包含数据流停止时剩余数据项的数目,这样软件便可以确定数据流中断前已传输了多少数据项。
  • 数据流在 DMA_SxNDTR 寄存器中要传输的剩余数据项数目达到 0 之前暂停传输,目的是以后通过重新使能数据流重新开始传输。为了在传输停止点重新开始传输,软件必须在通过写入 DMA_SxCR 寄存器中的 EN 位(然后检查确认该位为‘0’)禁止数据流之后,首先读取 DMA_SxNDTR 寄存器来了解已经收集的数据项的数目。然后:
    • 必须更新外设和/或存储器地址以调整地址指针
    • 必须使用要传输的剩余数据项的数目(禁止数据流时读取的值)更新 SxNDTR 寄存器
    • 然后可以重新使能数据流,从停止点重新开始传输
  • 传输完成中断标志( DMA_LISR 或 DMA_HISR 中的 TCIF)置 1 将指示因数据流中断而结束传输。

【 15. 流控制器 】

控制要传输的数据数目的实体称为流控制器。此流控制器使用 DMA_SxCR 寄存器中的 PFCTRL 位针对每个数据流独立配置。

  • 流控制器的分类:
    • DMA 控制器:在这种情况下,要传输的数据项的数目在使能 DMA 数据流之前由软件编程到 DMA_SxNDTR 寄存器。
    • 外设源或目标:当要传输的数据项的数目未知时属于这种情况。当所传输的是最后的数据时,外设通过硬件向 DMA 控制器发出指示。仅限能够发出传输结束信号的外设支持此功能,也就是:SDIO
  • 当外设流控制器用于给定数据流时,写入 DMA_SxNDTR 的值对 DMA 传输没有作用。实际上,不论写入什么值,一旦使能数据流,硬件即会将该值强制置为 0xFFFF 来执行下列方案:
    • 预期的数据流中断: DMA_SxCR 寄存器中的 EN 位由软件重置为 0,以在外设发送最后的数据硬件信号(单独或突发)之前停止数据流。这样,在外设到存储器 DMA 传输的情况下,数据流即会关闭并触发 FIFO 刷新。状态寄存器中相应数据流的 TCIFx 标志置 1 以指示 DMA 完成传输。要了解 DMA 传输期间传输的数据项的数目,请读取 DMA_SxNDTR 寄存器并应用下列公式:
      传 输 的 数 据 数 目 = 0 x F F F F — D M A S x N D T R 传输的数据数目 = 0xFFFF — DMA_SxNDTR =0xFFFFDMASxNDTR
    • 因接收到最后的数据硬件信号而引起的正常数据流中断:当外设请求最后的传输(单独或突发)并当此传输完成时,自动中断数据流。相应流的 TCIFx 标志在状态寄存器中置 1 以指示 DMA 传输完成。要了解传输的数据项的数目,请读取 DMA_SxNDTR 寄存器并应用与上面相同的公式。
    • DMA_SxNDTR 寄存器达到 0:状态寄存器中相应数据流的 TCIFx 标志置 1 以指示强制的 DMA 传输完成。即使尚未置位最后的数据硬件信号(单独或突发),也会自动关闭数据流。已传输的数据不会丢失。这意味着即使在外设流控制模式下, DMA 在单独的事务中最多处理 65535 个数据项。
  • 当在存储器到存储器模式下配置时, DMA 始终是流控制器,而 PFCTRL 位由硬件强制置为 0。在外设流控制器模式下禁止循环模式。

【 16. DMA 配置汇总 】

在这里插入图片描述

【 17. 流配置过程 】

配置 DMA 数据流 x(其中 x 是数据流编号)时应遵守下面的顺序:

  1. 如果使能了数据流,通过重置 DMA_SxCR 寄存器中的 EN 位将其禁止,然后读取此位以确认没有正在进行的数据流操作(这是因为将 EN 位写为 0 不会立即生效,因为实际上只有所有当前传输都已完成时才会将其写为 0, 当所读取 EN 位的值为 0 时,才表示可以配置数据流。因此在开始任何数据流配置之前,需要等待 EN 位置 0)。应将先前的数据块 DMA 传输中在 状态寄存器(DMA_LISR 和 DMA_HISR)中置 1 的所有数据流专用的位置 0,然后才可重新使能数据流
  2. 在 DMA_SxPAR 寄存器中 设置外设端口寄存器地址 。外设事件发生后,数据会从此地址移动到外设端口或从外设端口移动到此地址。
  3. 在 DMA_SxMA0R 寄存器(在双缓冲区模式的情况下还有 DMA_SxMA1R 寄存器)中 设置存储器地址 。外设事件发生后,将从此存储器读取数据或将数据写入此存储器。
  4. 在 DMA_SxNDTR 寄存器中 配置要传输的数据项的总数 。每出现一次外设事件或每出现一个节拍的突发传输,该值都会递减。
  5. 使用 DMA_SxCR 寄存器中的 CHSEL[2:0] 选择 DMA 通道(请求)
  6. 如果外设用作流控制器而且支持此功能,请将 DMA_SxCR 寄存器中的 PFCTRL 位置 1。
  7. 使用 DMA_SxCR 寄存器中的 PL[1:0] 位 配置数据流优先级
  8. 配置 FIFO 的使用情况 (使能或禁止,发送和接收阈值)。
  9. 配置数据传输方向 外设和存储器增量 / 固定模式 单独或突发事务 外设和存储器数据宽度 循环模式 双缓冲区模式 传输完成一半和/或全部完成 和/或 DMA_SxCR 寄存器中错误的中断
  10. 通过将 DMA_SxCR 寄存器中的 EN 位置 1 激活数据流
    一旦使能了流,即可响应连接到数据流的外设发出的任何 DMA 请求。
    一旦在 AHB 目标端口上传输了一半数据,传输一半标志 (HTIF) 便会置 1,如果传输一半中断使能位 (HTIE) 置 1,还会生成中断。传输结束时,传输完成标志 (TCIF) 便会置 1,如果传输完成中断使能位 (TCIE) 置 1,还会生成中断。
  • 要关闭连接到 DMA 数据流请求的外设,必须首先关闭外设连接的 DMA 数据流,然后等待 EN 位 = 0 ,只有这样才能安全地禁止外设。

【 18. 错误管理 】

DMA 控制器可以检测到以下错误:

  • 传输错误:当发生下列情况时,传输错误中断标志 (TEIFx) 将置 1:
    • DMA 读或写访问期间发生总线错误
    • 软件请求在双缓冲区模式下写访问存储器地址寄存器,但是,已使能数据流,并且当前目标存储器是受写入存储器地址寄存器操作影响的存储器(请参见第 9.3.9 节:双缓冲区模式)
  • FIFO 错误:如果发生下列情况, FIFO 错误中断标志 (FEIFx) 将置 1:
    • 检测到 FIFO 下溢情况
    • 检测到 FIFO 上溢情况(在存储器到存储器模式下由 DMA 内部管理请求和传输,所以在此模式下不检测溢出情况)
    • 当 FIFO 阈值级别与存储器突发大小不兼容时使能流(请参见表 41: FIFO 阈值配置)
  • 直接模式错误:只有当在直接模式下工作并且已将 DMA_SxCR 寄存器中的 MINC 位清零时,才能在外设到存储器模式下将直接模式错误中断标志 (DMEIFx) 置 1。当在先前数据未完全传输到存储器(因为存储器总线未得到授权)的情况下发生 DMA 请求时,该标志将置 1。在这种情况下,该标志指示有两个数据项相继传输到相同的目标地址,如果目标不能管理这种情况,会发生问题。在直接模式下,如果出现下列条件, FIFO 错误标志也会置 1:
    • 在外设到存储器模式下,如果未对存储器总线授权支持多个外设请求, FIFO 可能饱和(上溢)
    • 在存储器到外设模式下,如果在外设请求发生前存储器总线未得到授权,则可能发生下溢的情况
  • 如果由于突发大小与 FIFO 阈值级别之间的不兼容而引起 TEIFx 或 FEIFx 标志置 1,则硬件自动将相应数据流配置寄存器 (DMA_SxCR) 中的 EN 位清零,从而禁止错误的数据流。
  • 如果由于上溢或下溢情况引起 DMEIFx 或 FEIFx 标志置 1,则不会自动禁止错误的数据流,而是由软件决定是否通过重置 DMA_SxCR 寄存器中的 EN 位禁止数据流。这是因为当发生这种错误时没有数据丢失。
  • 当 DMA_LISR 或 DMA_HISR 寄存器中数据流的错误中断标志( TEIF、 FEIF、 DMEIF)置 1时,如果 DMA_SxCR 或 DMA_SxFCR 寄存器中相应的中断使能位( TEIE、 FEIE、 DMIE)也置 1,则会生成一个中断。
  • 当 FIFO 上溢或下溢的情况发生时,因为直到上溢或下溢的情况被清除,数据流才会确认外设请求,所以数据不会丢失。如果此确认过程花费过多时间,则外设本身会检测到其内部缓冲器上溢或下溢的情况,数据可能丢失。

【 19. 中断 】

对于每个 DMA 数据流,可在发生以下事件时产生中断:

  • 达到半传输
  • 传输完成
  • 传输错误
  • FIFO 错误(上溢、下溢或 FIFO 级别错误)
  • 直接模式错误
    可以使用单独的中断使能位以实现灵活性,如表 43 所示。在这里插入图片描述
    注意: 在将使能控制位置 ‘1’ 前,应将相应的事件标志清零,否则会立即产生中断

【 20. 寄存器 】

中断状态寄存器 DMA_HISR、DMA_LISR

  • 该寄存器总共有 2 个: DMA_LISR 和 DMA_HISR,每个寄存器管理 4 数据流, DMA_LISR 寄存器用于管理数据流 0到3,而 DMA_HISR 用于管理数据流 4到7。
  • 如果开启了 DMA_LISR 中这些位对应的中断, 则在达到条件后就会跳到中断服务函数里面去,即使没开启,我们也可以通过查询这些位来获得当前 DMA 传输的状态。这里我们常用的是 TCIFx位,即数据流 x 的 DMA 传输完成与否标志。
  • 注意 此寄存器为只读寄存器,所以在这些位被置位之后,只能通过操作 中断标志清除寄存器 来清除。
    在这里插入图片描述
  • 位 31:28、15:12 保留,必须保持复位值
  • 位 27、21、11、5 TCIFx:数据流 x 传输完成中断标志 (Stream x transfer complete interrupt flag) (x=7…4)
    此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_HIFCR 寄存器的相应位。
    0:数据流 x 上无传输完成事件
    1:数据流 x 上发生传输完成事件
  • 位 26、20、10、4 HTIFx:数据流 x 半传输中断标志 (Stream x half transfer interrupt flag) (x=7…4)
    此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_HIFCR 寄存器的相应位。
    0:数据流 x 上无半传输事件
    1:数据流 x 上发生半传输事件
  • 位 25、19、9、3 TEIFx:数据流 x 传输错误中断标志 (Stream x transfer error interrupt flag) (x=7…4)
    此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_HIFCR 寄存器的相应位。
    0:数据流 x 上无传输错误
    1:数据流 x 上发生传输错误
  • 位 24、18、8、2 DMEIFx:数据流 x 直接模式错误中断标志 (Stream x direct mode error interrupt flag) (x=7…4)
    此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_HIFCR 寄存器的相应位。
    0:数据流 x 上无直接模式错误
    1:数据流 x 上发生直接模式错误
  • 位 23、17、7、1 保留,必须保持复位值
  • 位 22、16、6、0 FEIFx:数据流 x FIFO 错误中断标志 (Stream x FIFO error interrupt flag) (x=7…4)
    此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_HIFCR 寄存器的相应位。
    0:数据流 x 上无 FIFO 错误事件
    1:数据流 x 上发生 FIFO 错误事件

在这里插入图片描述

  • 位 31:28、15:12 保留,必须保持复位值
  • 位 27、21、11、5 TCIFx:数据流 x 传输完成中断标志 (Stream x transfer complete interrupt flag) (x = 3…0)
    此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_LIFCR 寄存器的相应位。
    0:数据流 x 上无传输完成事件
    1:数据流 x 上发生传输完成事件
  • 位 26、20、10、4 HTIFx:数据流 x 半传输中断标志 (Stream x half transfer interrupt flag) (x=3…0)
    此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_LIFCR 寄存器的相应位。
    0:数据流 x 上无半传输事件
    1:数据流 x 上发生半传输事件
  • 位 25、19、9、3 TEIFx:数据流 x 传输错误中断标志 (Stream x transfer error interrupt flag) (x=3…0)
    此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_LIFCR 寄存器的相应位。
    0:数据流 x 上无传输错误
    1:数据流 x 上发生传输错误
  • 位 24、18、8、2 DMEIFx:数据流 x 直接模式错误中断标志 (Stream x direct mode error interrupt flag) (x=3…0)
    此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_LIFCR 寄存器的相应位。
    0:数据流 x 上无直接模式错误
    1:数据流 x 上发生直接模式错误
  • 位 23、17、7、1 保留,必须保持复位值
  • 位 22、16、6、0 FEIFx:数据流 x FIFO 错误中断标志 (Stream x FIFO error interrupt flag) (x=3…0)
    此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_LIFCR 寄存器的相应位。
    0:数据流 x 上无 FIFO 错误事件
    1:数据流 x 上发生 FIFO 错误事件

中断标志清除寄存器 DMA_HIFCR、DMA_LIFCR

  • 该寄存器同样有 2 个: DMA_LIFCR 和 DMA_HIFCR,同样是每个寄存器控制 4 个数据流。 DMA_LIFCR 寄存器用于管理数据流 0至3,而 DMA_ HIFCR 用于管理数据流 4至7。这两个寄存器各位描述都完全一模一样,只是管理的数据流不一样。
  • DMA_LIFCR 的各位就是用来清除 DMA_LISR 的对应位的,通过写 1 清除。在 DMA_LISR 被置
    位后,我们必须通过向该位寄存器对应的位写入 1 来清除。 DMA_HIFCR 的使用同 DMA_LIFCR 类似。
    在这里插入图片描述
  • 位 31:28、15:12 保留,必须保持复位值
  • 位 27、21、11、5 CTCIFx:数据流 x 传输完成中断标志清零 (Stream x clear transfer complete interrupt flag) (x = 7.0.4)
    将 1 写入此位时,DMA_HISR 寄存器中相应的 TCIFx 标志将清零
  • 位 26、20、10、4 CHTIFx:数据流 x 半传输中断标志清零 (Stream x clear half transfer interrupt flag) (x = 7.0.4)
    将 1 写入此位时,DMA_HISR 寄存器中相应的 HTIFx 标志将清零
  • 位 25、19、9、3 CTEIFx:数据流 x 传输错误中断标志清零 (Stream x clear transfer error interrupt flag) (x = 7.0.4)
    将 1 写入此位时,DMA_HISR 寄存器中相应的 TEIFx 标志将清零
  • 位 24、18、8、2 CDMEIFx:数据流 x 直接模式错误中断标志清零 (Stream x clear direct mode error interrupt flag) (x = 7.0.4)
    将 1 写入此位时,DMA_HISR 寄存器中相应的 DMEIFx 标志将清零
  • 位 23、17、7、1 保留,必须保持复位值
  • 位 22、16、6、0 CFEIFx:数据流 x FIFO 错误中断标志清零 (Stream x clear FIFO error interrupt flag) (x = 7.0.4)
    将 1 写入此位时,DMA_HISR 寄存器中相应的 CFEIFx 标志将清零

在这里插入图片描述

  • 位 31:28、15:12 保留,必须保持复位值
  • 位 27、21、11、5 CTCIFx:数据流 x 传输完成中断标志清零 (Stream x clear transfer complete interrupt flag) (x = 3…0)
    将 1 写入此位时,DMA_LISR 寄存器中相应的 TCIFx 标志将清零
  • 位 26、20、10、4 CHTIFx:数据流 x 半传输中断标志清零 (Stream x clear half transfer interrupt flag) (x = 3…0)
    将 1 写入此位时,DMA_LISR 寄存器中相应的 HTIFx 标志将清零
  • 位 25、19、9、3 CTEIFx:数据流 x 传输错误中断标志清零 (Stream x clear transfer error interrupt flag) (x = 3…0)
    将 1 写入此位时,DMA_LISR 寄存器中相应的 TEIFx 标志将清零
  • 位 24、18、8、2 CDMEIFx:数据流 x 直接模式错误中断标志清零 (Stream x clear direct mode error interrupt flag) (x = 3…0)
    将 1 写入此位时,DMA_LISR 寄存器中相应的 DMEIFx 标志将清零
  • 位 23、17、7、1 保留,必须保持复位值
  • 位 22、16、6、0 CFEIFx:数据流 x FIFO 错误中断标志清零 (Stream x clear FIFO error interrupt flag) (x = 3…0)
    将 1 写入此位时,DMA_LISR 寄存器中相应的 CFEIFx 标志将清零

数据流 x 配置寄存器 DMA_SxCR (x = 0…7)

  • 该寄存器控制着 DMA 的很多相关信息,包括数据宽度、外设及存储器的宽度、优先级、增量模式、传输方向、中断允许、使能等都是通过该寄存器来设置的。所以 DMA_ SxCR 是 DMA 传输的核心控制寄存器。
    在这里插入图片描述
  • 位 31:28 保留,必须保持复位值。
  • 位 27:25 CHSEL[2:0]:通道选择 (Channel selection)
    这些位将由软件置 1 和清零。
    000:选择通道 0
    001:选择通道 1
    010:选择通道 2
    011:选择通道 3
    100:选择通道 4
    101:选择通道 5
    110:选择通道 6
    111:选择通道 7
    这些位受到保护,只有 EN 为“0”时才可以写入
  • 位 24:23 MBURST[1:0]:存储器突发传输配置 (Memory burst transfer configuration)
    这些位将由软件置 1 和清零。
    00:单次传输
    01:INCR4(4 个节拍的增量突发传输)
    10:INCR8(8 个节拍的增量突发传输)
    11:INCR16(16 个节拍的增量突发传输)
    这些位受到保护,只有 EN 为“0”时才可以写入
    在直接模式中,当位 EN =“1”时,这些位由硬件强制置为 0x0。
  • 位 22:21 PBURST[1:0]:外设突发传输配置 (Peripheral burst transfer configuration)
    这些位将由软件置 1 和清零。
    00:单次传输
    01:INCR4(4 个节拍的增量突发传输)
    10:INCR8(8 个节拍的增量突发传输)
    11:INCR16(16 个节拍的增量突发传输)
    这些位受到保护,只有 EN 为“0”时才可以写入
    在直接模式下,这些位由硬件强制置为 0x0。
  • 位 20 保留,必须保持复位值
  • 位 19 CT:当前目标(仅在双缓冲区模式下)(Current target (only in double buffer mode))
    此位由硬件置 1 和清零,也可由软件写入。
    0:当前目标存储器为存储器 0(使用 DMA_SxM0AR 指针寻址)
    1:当前目标存储器为存储器 1(使用 DMA_SxM1AR 指针寻址)
    只有 EN 为“0”时,此位才可以写入,以指示第一次传输的目标存储区。在使能数据流后,此位相当于一个状态标志,用于指示作为当前目标的存储区。
  • 位 18 DBM:双缓冲区模式 (Double buffer mode)
    此位由软件置 1 和清零。
    0:传输结束时不切换缓冲区
    1:DMA 传输结束时切换目标存储区
    此位受到保护,只有 EN 为“0”时才可以写入。
  • 位 17:16 PL[1:0]:优先级 (Priority level)
    这些位将由软件置 1 和清零。
    00:低
    01:中
    10:高
    11:非常高
    这些位受到保护,只有 EN 为“0”时才可以写入。
  • 位 15 PINCOS:外设增量偏移量 (Peripheral increment offset size)
    此位由软件置 1 和清零
    0:用于计算外设地址的偏移量与 PSIZE 相关
    1:用于计算外设地址的偏移量固定为 4(32 位对齐)。
    如果位 PINC =“0”,则此位没有意义。
    此位受到保护,只有 EN 为“0”时才可以写入。
    如果选择直接模式或者 PBURST 不等于“00”,则当使能数据流(位 EN =“1”)时,此 位由硬件强制置为低电平。
  • 位 14:13 MSIZE[1:0]:存储器数据大小 (Memory data size)
    这些位将由软件置 1 和清零。
    00:字节(8 位)
    01:半字(16 位)
    10:字(32 位)
    11:保留
    这些位受到保护,只有 EN 为“0”时才可以写入。
    在直接模式下,当位 EN =“1”时,MSIZE 位由硬件强制置为与 PSIZE 相同的值。
  • 位 12:11 PSIZE[1:0]:外设数据大小 (Peripheral data size)
    这些位将由软件置 1 和清零。
    00:字节(8 位)
    01:半字(16 位)
    10:字(32 位)
    11:保留
    这些位受到保护,只有 EN 为“0”时才可以写入
  • 位 10 MINC:存储器递增模式 (Memory increment mode)
    此位由软件置 1 和清零。
    0:存储器地址指针固定
    1:每次数据传输后,存储器地址指针递增(增量为 MSIZE 值)
    此位受到保护,只有 EN 为“0”时才可以写入。
  • 位 9 PINC:外设递增模式 (Peripheral increment mode)
    此位由软件置 1 和清零。
    0:外设地址指针固定
    1:每次数据传输后,外设地址指针递增(增量为 PSIZE 值)
    此位受到保护,只有 EN 为“0”时才可以写入。
  • 位 8 CIRC:循环模式 (Circular mode)
    此位由软件置 1 和清零,并可由硬件清零。
    0:禁止循环模式
    1:使能循环模式
    如果外设为流控制器(位 PFCTRL=1)且使能数据流(位 EN=1),此位由硬件自动强制 清零。
    如果 DBM 位置 1,当使能数据流(位 EN =“1”)时,此位由硬件自动强制置 1。
  • 位 7:6 DIR[1:0]:数据传输方向 (Data transfer direction)
    这些位将由软件置 1 和清零。
    00:外设到存储器
    01:存储器到外设
    10:存储器到存储器
    11:保留
    这些位受到保护,只有 EN 为“0”时才可以写入。
  • 位 5 PFCTRL:外设流控制器 (Peripheral flow controller)
    此位由软件置 1 和清零。
    0:DMA 是流控制器
    1:外设是流控制器
    此位受到保护,只有 EN 为“0”时才可以写入。
    选择存储器到存储器模式(位 DIR[1:0]=10)后,此位由硬件自动强制清零。
  • 位 4 TCIE:传输完成中断使能 (Transfer complete interrupt enable)
    此位由软件置 1 和清零。
    0:禁止 TC 中断
    1:使能 TC 中断
  • 位 3 HTIE:半传输中断使能 (Half transfer interrupt enable)
    此位由软件置 1 和清零。
    0:禁止 HT 中断
    1:使能 HT 中断
  • 位 2 TEIE:传输错误中断使能 (Transfer error interrupt enable)
    此位由软件置 1 和清零。
    0:禁止 TE 中断
    1:使能 TE 中断
  • 位 1 DMEIE:直接模式错误中断使能 (Direct mode error interrupt enable)
    此位由软件置 1 和清零。
    0:禁止 DME 中断
    1:使能 DME 中断
  • 位 0 EN:数据流使能/读作低电平时数据流就绪标志 (Stream enable / flag stream ready when read low)
    此位由软件置 1 和清零。
    0:禁止数据流
    1:使能数据流
    以下情况下,此位可由硬件清零:
    - DMA 传输结束时(准备好配置数据流)
    - AHB 主总线出现传输错误时
    - 存储器 AHB 端口上的 FIFO 阈值与突发大小不兼容时
    此位读作 0 时,软件可以对配置和 FIFO 位寄存器编程。EN 位读作 1 时,禁止向这些寄存 器执行写操作。
    将 EN 位置“1”以启动新传输之前,DMA_LISR 或 DMA_HISR 寄存器中与数据流相对应的事件标志必须清零。

数据流 x 数据项数寄存器 DMA_SxNDTR (x = 0…7)

  • 这个寄存器控制 DMA 数据流 x 的每次传输所要传输的数据量(其设置范围为 0~65535)。
  • 该寄存器的值会随着传输的进行而减少,当该寄存器的值为 0 的时候就代表此次数据传输已经全部发送完成了。所以可以通过这个寄存器的值来知道当前 DMA 传输的进度。
  • 特别注意,这里是数据项数目,而不是指的字节数。比如设置数据位宽为 16 位,那么传输一次(一个项)就是 2 个字节。
    在这里插入图片描述
  • 位 31:16 保留,必须保持复位值。
  • 位 15:0 NDT[15:0]:要传输的数据项数目 (Number of data items to transfer)
    要传输的数据项数目(0 到 65535)。只有在禁止数据流时,才能向此寄存器执行写操作。
    使能数据流后,此寄存器为只读,用于指示要传输的剩余数据项数。每次 DMA 传输后,此 寄存器将递减。
    传输完成后,此寄存器保持为零(数据流处于正常模式时),或者在以下情况下自动以先前编程的值重载:
    - 以循环模式配置数据流时。
    - 通过将 EN 位置“1”来重新使能数据流时
    如果该寄存器的值为零,则即使使能数据流,也无法完成任何事务。

数据流 x 外设地址寄存器 DMA_SxPAR (x = 0…7)

  • 该寄存器用来存储 STM32F4 外设的地址。
  • 比如我们使用串口 1,那么该寄存器必须写入 0x40011004(其实就是&USART1_DR)。如果使用其他外设,就修改成相应外设的地址就行了。
    在这里插入图片描述
  • 位 31:0 PAR[31:0]:外设地址 (Peripheral address)
    读/写数据的外设数据寄存器的基址。
    这些位受到写保护,只有 DMA_SxCR 寄存器中的 EN 为“0”时才可以写入。

数据流 x 存储器地址寄存器 DMA_SxM0AR、DMA_SxM1AR (x = 0…7)

  • 由于 STM32F4 的 DMA 支持双缓存,所以存储器地址寄存器有两个: DMA_SxM0AR 和DMA_SxM1AR,其中 DMA_SxM1AR 仅在双缓冲模式下才有效
  • DMA_SxM0AR寄存器和DMA_CPARx 差不多,但是是用来放存储器的地址的。
    比如我们使用 SendBuf[8200]数组来做存储器,那么我们在 DMA_SxM0AR 中写入&SendBuff 就可以了。

在这里插入图片描述

  • 位 31:0 M0A[31:0]:存储器 0 地址 (Memory 0 address)
    读/写数据的存储区 0 的基址。
    这些位受到写保护,只有在以下情况下才可以写入:
    - 禁止数据流(DMA_SxCR 寄存器中的位 EN=“0”)或
    - 使能数据流(DMA_SxCR 寄存器中的 EN=“1”)并且 DMA_SxCR 寄存器中的 位 CT =“1”(在双缓冲区模式下)。

在这里插入图片描述

  • 位 31:0 M1A[31:0]:存储器 1 地址(用于双缓冲区模式)(Memory 1 address (used in case of Double buffer mode))
    读/写数据的存储区 1 的基址。
    此寄存器仅用于双缓冲区模式。
    这些位受到写保护,只有在以下情况下才可以写入:
    - 禁止数据流(DMA_SxCR 寄存器中的位 EN=“0”)或
    - 使能数据流(DMA_SxCR 寄存器中的 EN=“1”)并且 DMA_SxCR 寄存器中的位 CT =“0”。

数据流 x FIFO 控制寄存器 DMA_SxFCR (x = 0…7)

在这里插入图片描述

  • 位 31:8 保留,必须保持复位值
  • 位 7 FEIE:FIFO 错误中断使能 (FIFO error interrupt enable)
    此位由软件置 1 和清零。
    0:禁止 FE 中断
    1:使能 FE 中断
  • 位 6 保留,必须保持复位值
  • 位 5:3 FS[2:0]:FIFO 状态 (FIFO status)
    这些位为只读。
    000:0 < fifo_level < 1/4
    001:1/4  fifo_level < 1/2
    010:1/2  fifo_level < 3/4
    011:3/4  fifo_level < 满
    100:FIFO 为空
    101:FIFO 已满
    其它:无意义
    在直接模式(DMDIS 位为零)下,这些位无意义。
  • 位 2 DMDIS:直接模式禁止 (Direct mode disable)
    此位由软件置 1 和清零。它可由硬件置 1。
    0:使能直接模式
    1:禁止直接模式
    此位受到保护,只有 EN 为“0”时才可以写入。
    如果选择存储器到存储器模式(DMA_SxCR 中的 DIR 位为“10”),并且 DMA_SxCR 寄存 器中的 EN 位为“1”,则此位由硬件置 1,因为在存储器到存储器配置不能使用直接模式。
  • 位 1:0 FTH[1:0]:FIFO 阈值选择 (FIFO threshold selection)
    这些位将由软件置 1 和清零。
    00:FIFO 容量的 1/4
    01:FIFO 容量的 1/2
    10:FIFO 容量的 3/4
    11:FIFO 完整容量
    在直接模式(DMDIS 值为零)下,不使用这些位。
    这些位受到保护,只有 EN 为“1”时才可以写入。

【 21. 配置过程 】

我们要用到串口 1 的发送,属于 DMA2 的数据流 7,通道 4。步骤如下:

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

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

  • 库函数使能 DMA2 时钟的方法为:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2 时钟使能
  • 等待 DMA 可配置,也就是等待 DMA_SxCR 寄存器最低位为 0 的方法为:
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待 DMA 可配置

2. 初始化 DMA2 数据流

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

void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);
  • 函数的第一个参数是指定初始化的 DMA 的数据流编号,这个很容易理解。入口参数范围为: DMAx_Stream0~ DMAx_Stream7(x=1,2)。
  • 下面我们主要看看第二个参数。跟其他外设一样,同样是通过初始化结构体成员变量值来达到初始化的目的。
typedef struct
{
uint32_t DMA_Channel;
uint32_t DMA_PeripheralBaseAddr;
uint32_t DMA_Memory0BaseAddr;
uint32_t DMA_DIR;
uint32_t DMA_BufferSize;
uint32_t DMA_PeripheralInc;
uint32_t DMA_MemoryInc;
uint32_t DMA_PeripheralDataSize;
uint32_t DMA_MemoryDataSize;
uint32_t DMA_Mode;
uint32_t DMA_Priority;
uint32_t DMA_FIFOMode;
uint32_t DMA_FIFOThreshold;
uint32_t DMA_MemoryBurst;
uint32_t DMA_PeripheralBurst;
}DMA_InitTypeDef;

  1. DMA_Channel
    用来 设置 DMA 数据流对应的通道。 可供每个数据流选择的通道请求多达 8 个,取值范围为: DMA_Channel_0~ DMA_Channel_7。
  2. DMA_PeripheralBaseAddr
    用来 设置 DMA 传输的外设基地址,比如要进行串口DMA 传输,那么外设基地址为串口接受发送数据存储器 USART1->DR 的地址,表示方法为&USART1->DR。
  3. DMA_Memory0BaseAddr
    为内存基地址,也就是我们 存放 DMA 传输数据的内存地址
  4. DMA_DIR
    设置数据传输方向,决定是从外设读取数据到内存还送从内存读取数据发送到外设,也就是外设是源地还是目的地,这里我们设置为从内存读取数据发送到串口,所以外设自然就是目的地了,所以选择值为 DMA_DIR_PeripheralDST。
  5. DMA_BufferSize
    设置一次传输数据量的大小,这个很容易理解。
  6. DMA_PeripheralInc
    设置传输数据的时候外设地址是不变还是递增。如果设置为递增,那么下一次传输的时候地址加 1,这里因为我们是一直往固定外设地址&USART1->DR发送数据,所以地址不递增,值为DMA_PeripheralInc_Disable;
  7. DMA_MemoryInc
    设置传输数据时候内存地址 是否递增。这个参数 和DMA_PeripheralInc 意思接近,只不过针对的是内存。这里我们的场景是将内存中连续存储单元的数据发送到串口,毫无疑问内存地址是需要递增的,所以值为 DMA_MemoryInc_Enable。
  8. DMA_PeripheralDataSize
    设置外设的数据长度是为字节传输(8bits),半字传 输 (16bits) 还 是 字 传 输 (32bits) , 这 里 我 们 是 8 位 字 节 传 输 , 所 以 值 设 置 为
    DMA_PeripheralDataSize_Byte。
  9. DMA_MemoryDataSize
    设置内存的数据长度,和第八个参数意思接近,这里我们同样设置为字节传输 DMA_MemoryDataSize_Byte。
  10. DMA_Mode
    设置 DMA 模式是否循环采集,也就是说,比如我们要从内存中采集 64 个字节发送到串口,如果设置为重复采集,那么它会在 64 个字节采集完成之后继续从内存的第一个地址采集,如此循环。这里我们设置为一次连续采集完成之后不循环。所以设置值为 DMA_Mode_Normal。在我们下面的实验中,如果设置此参数为循环采集,那么你会看到串口不停的打印数据,不会中断。
  11. DMA_Priority
    设置 DMA 通道的优先级,有低,中,高,超高三种模式
    这个在前面讲解过,这里我们设置优先级别为中级,所以值为 DMA_Priority_Medium。 优先级可以随便设置,因为我们只有一个数据流被开启了。假设有多个数据流开启(最多 8 个),那么就要设置优先级了, DMA 仲裁器将根据这些优先级的设置来决定先执行那个数据流的 DMA。优先级越高的,越早执行,当优先级相同的时候,根据硬件上的编号来决定哪个先执行(编号越小越优先)。
  12. DMA_FIFOMode
    设置是否开启 FIFO 模式。这里我们不开启所以选择 DMA_FIFOMode_Disable。
  13. DMA_FIFOThreshold
    选择 FIFO 阈值。根据前面讲解可以为 FIFO 容量的1/4,1/2,3/4 以及 1 倍。这里我们实际并没有开启 FIFO 模式,所以可以不关心。
  14. DMA_MemoryBurst
    配置存储器突发传输配置。可以选择为 4 个节拍的增量突发传输 DMA_MemoryBurst_INC4, 8 个节拍的增量突发传输 DMA_MemoryBurst_INC8, 16 个街拍的增量突发传输 DMA_MemoryBurst_INC16 以及单次传输 DMA_MemoryBurst_Single。
  15. DMA_PeripheralBurst
    配置外设突发传输配置。跟前面一个参数DMA_MemoryBurst 作用类似,只不过一个针对的是存储器,一个是外设。这里我们选择单次传输 DMA_PeripheralBurst_Single。
/* 配置 DMA Stream */
DMA_InitStructure.DMA_Channel = chx; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA 外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = mar;//DMA 存储器 0 地址
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//存储器到外设模式
DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8 位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据长度:8 位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化 DMA Stream

3. 使能串口 1 的 DMA 发送

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

USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口 1 的 DMA 发送

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

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

使能 DMA 数据流的函数为:

void DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState)

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

DMA_Cmd (DMA2_Stream7, ENABLE) ;

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

5. 查询 DMA 传输状态

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

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)

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

DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7);

这里还有一个比较重要的函数就是获取当前剩余数据量大小的函数:

uint16_t DMA_GetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx);

比如我们要获取 DMA 数据流 7 还有多少个数据没有传输,方法是:

DMA_GetCurrDataCounter(DMA1_Channel4);

同样,我们也可以设置对应的 DMA 数据流传输的数据量大小,函数为:

void DMA_SetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx, uint16_t Counter);

【 22. 例程 】

  • 利用外部按键 KEY0 来控制 DMA 的传送,每按一次 KEY0, DMA 就传送一次数据到串口1,然后在 TFTLCD 模块上显示进度等信息。 DS0 用来做为程序运行的指示灯。
  • 按键 KEY0 每按下一次,则
    1、使能串口1的 DMA 发送
    2、开启DMA传输(先关闭DMA,再设置DMA,最后开启DMA)
    3、while循环中判断是否已经完成DMA传输,若已完成则跳出循环,否则输出DMA传输进度信息。

dma.c

//DMAx 的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8 位数据宽度/存储器增量模式
//DMA_Streamx:DMA 数据流,DMA1_Stream0~7/DMA2_Stream0~7
//chx:DMA 通道选择, @ref DMA_channel DMA_Channel_0~DMA_Channel_7
//par:外设地址 mar:存储器地址 ndtr:数据传输量
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr)
{
	DMA_InitTypeDef DMA_InitStructure;
	if((u32)DMA_Streamx>(u32)DMA2)//得到当前 stream 是属于 DMA2 还是 DMA1
	{
	    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2 时钟使能
	}
	else
	{
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1 时钟使能
	}
	DMA_DeInit(DMA_Streamx);
	while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待 DMA 可配置
	/* 配置 DMA Stream */
	DMA_InitStructure.DMA_Channel = chx; //通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA 外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = mar;//DMA 存储器 0 地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//存储器到外设模式
	DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8 位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据长度:8 位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
	DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化 DMA Stream
}

//开启一次 DMA 传输
//DMA_Streamx:DMA 数据流,DMA1_Stream0~7/DMA2_Stream0~7
//ndtr:数据传输量
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)
{
	DMA_Cmd(DMA_Streamx, DISABLE); //关闭 DMA 传输
	while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){} //确保 DMA 可以被设置
	DMA_SetCurrDataCounter(DMA_Streamx,ndtr); //数据传输量
	DMA_Cmd(DMA_Streamx, ENABLE); //开启 DMA 传输
}

main.c

/*发送数据长度,最好等于 sizeof(TEXT_TO_SEND)+2 的整数倍.*/
#define SEND_BUF_SIZE 8200
u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区
const u8 TEXT_TO_SEND[]={"ALIENTEK Explorer STM32F4 DMA 串口实验"};
int main(void)
{
	u16 i;
	u8 t=0,j,mask=0;
	float pro=0;//进度
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组 2
	delay_init(168); //初始化延时函数
	uart_init(115200); //初始化串口波特率为 115200
	LED_Init(); //初始化 LED
	LCD_Init(); //LCD 初始化
	KEY_Init(); //按键初始化
	/*DMA2,STEAM7,CH4,外设为串口 1,存储器为 SendBuff,长度为:SEND_BUF_SIZE.*/
	MYDMA_Config(DMA2_Stream7,DMA_Channel_4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);
	LCD_ShowString(30,70,200,16,16,"DMA TEST");
	LCD_ShowString(30,130,200,16,16,"KEY0:Start");
	POINT_COLOR=BLUE;//设置字体为蓝色
	//显示提示信息
	j=sizeof(TEXT_TO_SEND);
	for(i=0;i<SEND_BUF_SIZE;i++)//填充 ASCII 字符集数据
	{
		if(t>=j)//加入换行符
		{
			if(mask)
			{
				SendBuff[i]=0x0a;t=0;
			}
			else
			{
				SendBuff[i]=0x0d;mask++;
			}
		}
		else//复制 TEXT_TO_SEND 语句
		{
			mask=0;
			SendBuff[i]=TEXT_TO_SEND[t];t++;
		}
	}
	POINT_COLOR=BLUE;//设置字体为蓝色
	i=0;
	while(1)
	{
		t=KEY_Scan(0);
		if(t==KEY0_PRES) //KEY0 按下
		{
			printf("\r\nDMA DATA:\r\n");
			LCD_ShowString(30,150,200,16,16,"Start Transimit....");
			LCD_ShowString(30,170,200,16,16," %");//显示百分号
			USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口 1 的 DMA 发送
			MYDMA_Enable(DMA2_Stream7,SEND_BUF_SIZE); //开始一次 DMA 传输!
			//等待 DMA 传输完成,此时我们来做另外一些事,点灯
			//实际应用中,传输数据期间,可以执行另外的任务
			while(1)
			{
				if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET)
				//等待 DMA2_Steam7 传输完成
				{
					DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);//清传输完成标志
					break;
				}
				pro=DMA_GetCurrDataCounter(DMA2_Stream7);//得到当前剩余数据数
				pro=1-pro/SEND_BUF_SIZE;//得到百分比
				pro*=100; //扩大 100 倍
				LCD_ShowNum(30,170,pro,3,16);
			}
			LCD_ShowNum(30,170,100,3,16);//显示 100%
			LCD_ShowString(30,150,200,16,16,"Transimit Finished!");
		}
		i++;
		delay_ms(10);
		if(i==20)
		{
			LED0=!LED0;//提示系统正在运行
			i=0;
		}
	}
}

  • 7
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MR_Promethus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值