STM32学习笔记-DMA


DMA(Direct Memory Access, 直接存储区访问),用于 外设与存储器之间、 存储器与存储器之间的高速数据传输。其最主要的特点是 不占用CPU资源

当我们需要将一个外设的数据转存到存储器中时,我们可以通过CPU读取外设数据并向存储器发送。当程序越来越庞大,功能越来越多的时候,这样直接使用CPU传输数据的方式就不太合适了。

原先使用的是红线1的方式,有了DMA之后可以通过黑线2 的方式传输数据。CPU只需要向DMA发送指令DMA即可开始工作。大大减少了程序对CPU的占用。

image-20230228164816598

一、功能框图

image-20230228113602044

STM32F4有两个DMA控制器,每个DMA有8个数据流,每个数据流总共可以有8个通道,每个通道都有一个仲裁器,用于处理DMA请求间的优先级

通道选择

DMA 控制器会通过 DMA 数据流 x 的配置寄存器 DMA_SxCR(x 为 0~7,对应 8 个 DMA数据流) 的 CHSEL[2:0] 位选择对应的通道作为该数据流的目标外设。

image-20230228114556965

但并不是每个通道都可以连接到所有的外设或者存储器,具体对应如下:

image-20230228114821344

DMA2各个通道的请求映像

image-20230228114844959

比如我们要使用DMA2的数据流0,通道0,即ADC1,那DMA_S0CR设置为000,那么DMA2的数据流0只能使用ADC1,其他七个通道都被关闭。

仲裁器

一个 DMA 控制器对应 8 个数据流,数据流包含要传输数据的源地址目标地址、数据等等信息。如果我们需要同时使用同一个 DMA 控制器 (DMA1 或 DMA2) 多个外设请求时,那必然需要同时使用多个数据流,那究竟哪一个数据流具有优先传输的权利呢?这就需要仲裁器来管理判断了。

仲裁器管理数据流方法分为两个阶段。

  • 第一阶段属于软件阶段,我们在配置数据流时可以通过寄存器设定它的优先级别,具体配置 DMA_SxCR 寄存器 PL[1:0] 位,可以设置为非常高、高、中和低四个级别。

  • 第二阶段属于硬件阶段,如果两个或以上数据流软件设置优先级一样,则他们优先级取决于数据流编号,编号越低越具有优先权,比如数据流 2 优先级高于数据流 3。

FIFO

1. FIFO: First in First out

每个数据流都独立拥有四级 32 位 FIFO(先进先出存储器缓冲区)。DMA 传输具有 FIFO 模式直接模式

  • 直接模式在每个外设请求都立即启动对存储器传输。在直接模式下,如果 DMA 配置为存储器到外设传输那 DMA 会见一个数据存放在 FIFO 内,如果外设启动 DMA 传输请求就可以马上将数据传输过去。

  • FIFO 用于在源数据传输到目标地址之前临时存放这些数据。可以通过 DMA 数据流 xFIFO 控制寄存器 DMA_SxFCR 的 FTH[1:0] 位来控制 FIFO 的阈值,分别为 1/4、1/2、3/4 和满。如果数据存储量达到阈值级别时,FIFO 内容将传输到目标中。

2. FIFO作用:
  • FIFO 对于要求源地址和目标地址数据宽度不同时非常有用,比如源数据是源源不断的字节数据,而目标地址要求输出字宽度(一个字,在32位系统里是4字节)的数据,即在实现数据传输时同时把原来 4 个 8 位字节的数据拼凑成一个 32 位字数据。此时使用 FIFO 功能先把数据缓存起来,分别根据需要输出数据。

  • FIFO 另外一个作用使用于突发 (burst) 传输。

参考链接:一文看懂FIFO - 知乎 (zhihu.com)

端口

DMA的两侧的端口用于连接源地址和目标地址,可以连接外设和存储器。传输模式有以下三种

  • 外设到存储器

  • 存储器到外设

  • 存储器到存储器

image-20230228160534448

  • DMA 控制器实现双 AHB 主接口,更好利用总线矩阵和并行传输。DMA 控制器通过存储器端口和外设端口与存储器和外设进行数据传输,关系见图两个 DMA 控制器系统实现 。DMA 控制器的功能是快速转移内存数据,需要一个连接至源数据地址的端口和一个连接至目标地址的端口。

  • DMA2(DMA 控制器 2) 的存储器端口和外设端口都是连接到 AHB 总线矩阵,可以使用 AHB 总线矩阵功能。DMA2 存储器和外设端口可以访问相关的内存地址,包括有内部 Flash、内部 SRAM、AHB1 外设、AHB2 外设、APB2 外设和外部存储器空间。

DMA1 的存储区端口相比 DMA2 的要减少 AHB2 外设的访问权,同时 DMA1 外设端口是没有连接至总线矩阵的,只有连接到 APB1 外设,所以 DMA1 不能实现存储器到存储器传输

二、DMA模式配置

DMA模式有下面这些组合状况:

image-20230228161936337

1. 传输模式

三种传输模式

  • 外设到存储器

  • 存储器到外设

  • 存储器到存储器

DMA2支持三种传输模式,DMA1只支持外设到存储器、存储器到外设

模式选择可以通过 DMA_SxCR 寄存器的 DIR[1:0] 位控制,进而将 DMA_SxCR 寄存器的 EN 位置 1 就可以使能 DMA 传输。在 DMA_SxCR 寄存器的 PSIZE[1:0] 和 MSIZE[1:0] 位分别指定外设和存储器数据宽度大小,可以指定为字节 (8 位)、半字 (16 位) 和字 (32 位),我们可以根据实际情况设置。直接模式要求外设和存储器数据宽度大小一样,实际上在这种模式下 DMA 数据流直接使用 PSIZE,MSIZE 不被使用。

2. 源地址和目标地址

DMA 数据流 x 外设地址 DMA_SxPAR(x 为 0~7) 寄存器用来指定外设地址,它是一个 32 位数据有效寄存器。DMA 数据流 x 存储器 0 地址DMA_SxM0AR(x 为 0~7) 寄存器和 DMA 数据流 x 存储器 1 地址 DMA_SxM1AR(x 为 0~7) 寄存器用来存放存储器地址,其中 DMA_SxM1AR 只用于双缓冲模式,DMA_SxM0AR 和 DMA_SxM1AR 都是 32 位数据有效的。

  • 当选择外设到存储器模式时,即设置DMA_SxCR 寄存器的 DIR[1:0] 位为“00”,DMA_SxPAR 寄存器为外设地址,也是传输的源地址,DMA_SxM0AR 寄存器为存储器地址,也是传输的目标地址。

  • 对于存储器到存储器传输模式,即设置 DIR[1:0] 位为“10”时,采用与外设到存储器模式相同配置。

  • 而对于存储器到外设模式,即设置 DIR[1:0] 位为“01”时,DMA_SxM0AR 寄存器作为源地址,DMA_SxPAR 寄存器作为目标地址。

3. 流控制器

流控制器主要用于控制DMA的停止。DMA 传输在 DMA_SxCR 寄存器的 EN 位被置 1 后就进入准备传输状态,如果有外设请求 DMA 传输就可以进行数据传输。很多情况下,我们明确知道传输数据的数目,比如要传 1000 个或者 2000 个数据,这样我们就可以在传输之前设置 DMA_SxNDTR 寄存器为要传输数目值,DMA 控制器在传输完这么多数目数据后就可以控制DMA 停止传输

DMA 数据流 x 数据项数 DMA_SxNDTR(x 为 0~7) 寄存器用来记录当前仍需要传输数目,它是一个 16 位数据有效寄存器,即最大值为 65535,这个值在程序设计是非常有用也是需要注意的地方。我们在编程时一般都会明确指定一个传输数量,在完成一次数目传输后 DMA_SxNDTR 计数值就会自减,当达到零时就说明传输完成。

如果某些情况下在传输之前我们无法确定数据的数目,那 DMA 就无法自动控制传输停止了,此时需要外设通过硬件通信向 DMA 控制器发送停止传输信号。这里有一个大前提就是外设必须是可以发出这个停止传输信号,只有 SDIO 才有这个功能,其他外设不具备此功能。

4. 循环模式

循环模式相对应于一次模式。一次模式就是传输一次就停止传输,下一次传输需要手动控制,而循环模式在传输一次后会自动按照相同配置重新传输,周而复始直至被控制停止或传输发生错误。常用于ADC数据的转移。

通过 DMA_SxCR 寄存器的 CIRC 位可以使能循环模式。

5. 传输类型

DMA 传输类型有单次 (Single) 传输和突发 (Burst) 传输。

6. 直接模式

默认情况下,DMA 工作在直接模式,不使能 FIFO 阈值级别。

直接模式在每个外设请求都立即启动对存储器传输的单次传输。直接模式要求源地址和目标地址的数据宽度必须一致,所以只有 PSIZE 控制,而 MSIZE 值被忽略。突发传输是基于 FIFO 的所以直接模式不被支持。另外直接模式不能用于存储器到存储器传输。在直接模式下,如果 DMA 配置为存储器到外设传输那 DMA 会见一个数据存放在 FIFO 内,如果外设启动 DMA 传输请求就可以马上将数据传输过去。

7. 双缓冲模式

双缓冲模式下,两个存储器地址指针都有效,即 DMA_SxM1AR寄存器将被激活使用。开始传输使用 DMA_SxM0AR 寄存器的地址指针所对应的存储区,当这个存储区数据传输完 DMA 控制器会自动切换至 DMA_SxM1AR 寄存器的地址指针所对应的另一块存储区,如果这一块也传输完成就再切换至 DMA_SxM0AR 寄存器的地址指针所对应的存储区,这样循环调用。

双缓冲模式的注意事项

  • 设置 DMA_SxCR 寄存器的 DBM 位为 1 可启动双缓冲传输模式,并自动激活循环模式。

  • 双缓冲不能用与存储器到存储器的传输。

8.DMA中断事件

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

  1. 达到半传输:DMA 数据传输达到一半时 HTIF 标志位被置 1,如果使能 HTIE 中断控制位将产生达到半传输中断;

  2. 传输完成:DMA 数据传输完成时 TCIF 标志位被置 1,如果使能 TCIE 中断控制位将产生传输完成中断;

  3. 传输错误:DMA 访问总线发生错误或者在双缓冲模式下试图访问“受限”存储器地址寄存器时 TEIF 标志位被置 1,如果使能 TEIE 中断控制位将产生传输错误中断;

  4. FIFO 错误:发生 FIFO 下溢或者上溢时 FEIF 标志位被置 1,如果使能 FEIE 中断控制位将产生FIFO 错误中断;

  5. 直接模式错误:在外设到存储器的直接模式下,因为存储器总线没得到授权,使得先前数据没有完成被传输到存储器空间上,此时 DMEIF 标志位被置 1,如果使能 DMEIE 中断控制位将产生直接模式错误中断。

三、程序设计

1. DMA初始化结构体

typedef struct {
    uint32_t DMA_Channel; //通道选择
    uint32_t DMA_PeripheralBaseAddr; //外设地址
    uint32_t DMA_Memory0BaseAddr; //存储器 0 地址
    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; //FIFO 模式
    uint32_t DMA_FIFOThreshold; //FIFO 阈值
    uint32_t DMA_MemoryBurst; //存储器突发传输
    uint32_t DMA_PeripheralBurst; //外设突发传输
} DMA_InitTypeDef;
  1. DMA_Channel:DMA 请求通道选择,可选通道 0 至通道 7,每个外设对应固定的通道,具体设置值需要查表 DMA1 各个通道的请求映像 和表 DMA2 各个通道的请求映像 ;它设定 DMA_SxCR寄存器的 CHSEL[2:0] 位的值。例如,我们使用模拟数字转换器 ADC3 规则采集 4 个输入通道的电压数据,查表 DMA2 各个通道的请求映像 可知使用通道 2。

  2. DMA_PeripheralBaseAddr:外设地址,设定 DMA_SxPAR 寄存器的值;一般设置为外设的数据寄存器地址,如果是存储器到存储器模式则设置为其中一个存储区地址。ADC3 的数据寄存器ADC_DR 地址为 ((uint32_t)ADC3+0x4C)。

  3. DMA_Memory0BaseAddr:存储器 0 地址,设定 DMA_SxM0AR 寄存器值;一般设置为我们自定义存储区的首地址。我们程序先自定义一个 16 位无符号整形数组 ADC_ConvertedValue[4]用 来 存 放 每 个 通 道 的 ADC 值, 所 以 把 数 组 首 地 址 (直 接 使 用 数 组 名 即 可) 赋 值 给DMA_Memory0BaseAddr。

  4. DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设以及存储器到存储器。它设定DMA_SxCR 寄存器的 DIR[1:0] 位的值。ADC 采集显然使用外设到存储器模式。

  5. DMA_BufffferSize:设定待传输数据数目,初始化设定 DMA_SxNDTR 寄存器的值。这里 ADC是采集 4 个通道数据,所以待传输数目也就是 4。

  6. DMA_PeripheralInc:如果配置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定 DMA_SxCR 寄存器的 PINC 位的值;一般外设都是只有一个数据寄存器,所以一般不会使能该位。ADC3 的数据寄存器地址是固定并且只有一个所以不使能外设地址递增。

  7. DMA_MemoryInc:如果配置为 DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定 DMA_SxCR 寄存器的 MINC 位的值;我们自定义的存储区一般都是存放多个数据的,所以使能存储器地址自动递增功能。我们之前已经定义了一个包含 4 个元素的数字用来存放数据,使能存储区地址递增功能,自动把每个通道数据存放到对应数组元素内。

  8. DMA_PeripheralDataSize:外设数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_SxCR 寄存器的 PSIZE[1:0] 位的值。ADC 数据寄存器只有低 16 位数据有效,使用半字数据宽度。

  9. DMA_MemoryDataSize:存储器数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_SxCR 寄存器的 MSIZE[1:0] 位的值。保存 ADC 转换数据也要使用半字数据宽度,这跟我们定义的数组是相对应的。

  10. DMA_Mode:DMA 传输模式选择,可选一次传输或者循环传输,它设定 DMA_SxCR 寄存器的 CIRC 位的值。我们希望 ADC 采集是持续循环进行的,所以使用循环传输模式。

  11. DMA_Priority:软件设置数据流的优先级,有 4 个可选优先级分别为非常高、高、中和低,它设定 DMA_SxCR 寄存器的 PL[1:0] 位的值。DMA 优先级只有在多个 DMA 数据流同时使用时才有意义,这里我们设置为非常高优先级就可以了。

  12. DMA_FIFOMode:FIFO 模式使能,如果设置为 DMA_FIFOMode_Enable 表示使能 FIFO 模式功能;它设定 DMA_SxFCR 寄存器的 DMDIS 位。ADC 采集传输使用直接传输模式即可,不需要使用 FIFO 模式。

  13. DMA_FIFOThreshold:FIFO 阈值选择,可选 4 种状态分别为 FIFO 容量的 1/4、1/2、3/4 和满;它设定 DMA_SxFCR 寄存器的 FTH[1:0] 位;DMA_FIFOMode 设置为 DMA_FIFOMode_Disable,那 DMA_FIFOThreshold 值无效。ADC 采集传输不使用 FIFO 模式,设置改值无效。

  14. DMA_MemoryBurst:存储器突发模式选择,可选单次模式、4 节拍的增量突发模式、8 节拍的增量突发模式或 16 节拍的增量突发模式,它设定 DMA_SxCR 寄存器的 MBURST[1:0] 位的值。ADC 采集传输是直接模式,要求使用单次模式。

  15. DMA_PeripheralBurst:外设突发模式选择,可选单次模式、4 节拍的增量突发模式、8 节拍的增量突发模式或 16 节拍的增量突发模式,它设定 DMA_SxCR 寄存器的 PBURST[1:0] 位的值。ADC 采集传输是直接模式,要求使用单次模式。

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

指针到处飞

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

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

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

打赏作者

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

抵扣说明:

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

余额充值