STM32–DMA

  • Post author:
  • Post category:其他



目录


DMA简介


DMA框图


DMA 优先级仲裁


FIFO阈值和burst模式传输


CUBEMX配置DMA使用


DMA_MEMORY_TO_MEMORY


DMA_PERIPH_TO_MEMORY


DMA_MEMORY_TO_PERIPH


DMA 中断


环境:STM32CUBEMX V5.5 (F4固件包 V1.24.2) + MDK + STM32F407ZGT6



DMA简介

DMA:Direct Memory Access 直接存储器访问

DMA传输将数据从一个地址空间复制到另一个地址空间,当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的 ,其作用,

就是为CPU减负的

STM32F4最多有2个DMA控制器(DMA2仅存在大容量产品中),2个DMA控制器总共有16个数据流(每个控制器8个)。每个数据流总共有8个通道,每个通道都有一个仲裁器,用于处理DMA请求间的优先级


DMA框图

DMA 优先级仲裁

仲裁器为两个 AHB 主端口(存储器和外设端口)提供基于请求优先级的 8 个 DMA 数据流请求管理,并启动外设/存储器访问序列。

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

● 软件:每个数据流优先级都可以在 DMA_SxCR 寄存器中配置。分为四个级别:

— 非常高优先级

— 高优先级

— 中优先级

— 低优先级

● 硬件:如果两个请求具有相同的软件优先级,则编号低的数据流优先于编号高的数据流。例如,数据流 2 的优先级高于数据流 4。

注意:因为一个 stream 只能配置一个 channel 。所以,确定 stream 的优先级就等于控制了 channel 的优先级

FIFO阈值和burst模式传输

FIFO 用于在源数据传输到目标之前临时存储这些数据。每个 stream 都有一个独立的 4 字 FIFO,阈值级别可由软件配置为 1/4、1/2、3/4 或满。合理的配置这些阈值级别和后面会提到的 burst length可以使搬运数据的效率相较于 direct模式更高。

burst size的长度可配置为如下:

其中,single 表示每次传输一个单位,可以说成没使用到burst的功能

在上图中,有些禁止设置的配置项,原因是 burst 搬运的模式下,是一次搬运 4/8/16 块的数据。这个“块”就是设置的 data width 的宽度。如果设置的 FIFO的阈值,data width, burst size 三者不协调的话,可能会出现数据访问出错的情况。

打个比方:

当 data width 设置成 半字传输,FIFO阈值设置位为1/4。burst size 设置为 4。因为FIFO 为 4 个 word 的深度,所以设置成 1/4 也就是 1 个 word 的数据在 FIFO 中后就会触发搬运。但现在因为 burst size 设置为4,data width 设置为半字。所以说在这种情况下一旦触发 burst 搬运。就会一次性搬运 4 块半字的数据。总计 2 个 word。但 burst 搬运触发时,FIFO 中只有一个 word 的数据。就会导致搬运的数据出错。所以可以看到表中是禁止这样配置的。


CUBEMX配置DMA使用

DMA_MEMORY_TO_MEMORY

两个注意的点:

DMA1 控制器 AHB 外设端口与 DMA2 控制器的情况不同,不连接到总线矩阵,因此,仅 DMA2 数据流能够执行存储器到存储器的传输。

使用存储器到存储器模式时,不允许循环模式和直接模式。也就是只能使用 Normal 模式加FIFO 的组合。

在CUBEMX中配置如上图所示。DMA是挂在AHB高速总线上的。配置好时钟等之后生成代码。通过调用 HAL_DMA_Start 来开始使用 DMA 搬运数据。但使用是需要注意的是,该函数调用一次就会失效。其原因有三个:

  1. 函数内将 hdma->State 的状态改为了 HAL_DMA_STATE_BUSY,但结束传输以后并没有改回为 HAL_DMA_STATE_READY。
  2. 函数内的 __HAL_UNLOCK() 执行是在 hdma->State为 HAL_DMA_STATE_BUSY 的状态下。
  3. 在中断状态寄存器(DMA_L/HISR)中。传输完成后会将 TCIFx 和 HTIFx 位置位。在不使用中断的情况下,也会置位。HAL_DMA_Start 并没有清除这两位。

所以,要想使用 HAL_DMA_Start 来搬运数据,加上以下代码即可:

__HAL_UNLOCK(&hdma_memtomem_dma2_stream0);
hdma_memtomem_dma2_stream0.State = HAL_DMA_STATE_READY;
__HAL_DMA_CLEAR_FLAG(&hdma_memtomem_dma2_stream0, DMA_FLAG_TCIF0_4);
__HAL_DMA_CLEAR_FLAG(&hdma_memtomem_dma2_stream0, DMA_FLAG_HTIF0_4);
HAL_DMA_Start(&hdma_memtomem_dma2_stream0, (uint32_t)source, (uint32_t)destination, 10);

DMA_PERIPH_TO_MEMORY

eg:DMA 去搬运串口1 的接受数据到指定目标地址

如上图,模式设置为了 circular 模式。使用了 FIFO,并且 FIFO 阈值为一半,也就是 2 个 word 触发搬运。

生成代码后,需要调用 HAL_UART_Receive_DMA 函数来指定目标地址和搬运 size。 该函数会先配置 DMA 传输过程中的一些回调函数,比如 DMA 传输完成回调函数。跟进代码,最终DMA 传输完成在此情况下会调用 HAL_UART_RxCpltCallback 回调函数。

HAL_UART_Receive_DMA 函数在最后会使能 USART_CR3 寄存器中的 DMAR 位来 使能DMA 协助接受串口数据。因为是 circular 模式,所以该函数只需要执行一次就好。

在上图中,红色为发送数据,黑色为接受后打印数据。因为配置了 DMA 的 FIFO 阈值为 2个 word,所以连续发送 8 字节数据后,DMA 开始从FIFO 中触发搬运到目标地址。然后最终会调用 HAL_UART_RxCpltCallback 函数,在此函数中进行接受数据的打印无误。

DMA_MEMORY_TO_PERIPH

eg:DMA 从源目标地址搬运数据到串口 1 的TX

如上图,这次将 mode 改为 normal模式,即DMA 搬运一次后就停止搬运。

调用HAL_UART_Transmit_DMA 函数来配置搬运,此函数结构上与 HAL_UART_Receive_DMA很相似,在最后会使能 USART_CR3寄存器中的 DMAT 位来 使能DMA 协助发送串口数据。


DMA 中断

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

其中 FIFO 上溢/下溢 错误我是这样理解的。

当DMA 在搬运数据到 FIFO 进行发送时,如果使用了FIFO 和 burst 传输。那么当 FIFO 中剩余数据量小于或等于 FIFO阈值时,会触发 burst 从源目标搬运数据到 FIFO 中,若 FIFO 阈值设置太低。就会导致每次触发 DMA 从源目标搬运数据到 FIFO 中太晚。阈值设置太低和 burst size 的设置不合适也可能会导致触发 burst 搬运会额外搬出超出 FIFO 的数据,导致数据下溢。

至于上溢,当 DMA 在从 FIFO 搬运数据进行接受时,如果 FIFO 阈值设置太高,就有可能导致数据搬运到目标地址不及时,可能造成FIFO 中接受数据溢出。

使能中断后,在 it.c 里面会进入中断服务函数,在其中会做判断中断类型和清除中断的操作,然后执行相应的中断 callback 函数。callback 函数需要自己编写。然后将函数地址给 DMA_HandleTypeDef 中的相关函数指针。

在此, HAL 提供了一个函数

HAL_DMA_RegisterCallback

,此函数可以将 callback 函数指针赋值给 DMA_HandleTypeDef 相关的函数指针。



版权声明:本文为qq_41867145原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。