《STM32从零开始学习历程》——DMA直接存储区访问理论知识

《STM32从零开始学习历程》@EnzoReventon

DMA—直接存储区访问理论知识

本文主要介绍STM32F4 DMA直接存储区的理论知识部分,本文主要参考手册为:
[野火EmbedFire]《STM32库开发实战指南——基于野火霸天虎开发板》
[正点原子]STM32F4开发指南-库函数版本_V1.2
[ST]《STM32F4xx中文参考手册》
在学习野火教程第22章的基础上进行理解、解读与拓展,争取以一种比较好理解的简述方式向大家介绍这一内容。

1. DMA简介

DMA(Direct Memory Access, 直接存储区访问)是一种独立于CPU的控制器。它的主要功能是实现数据在“外设寄存器与存储器之间”、“存储器与存储器之间独立于CPU的高速传输。
《STM32从零开始学习历程》——DMA直接存储区访问理论知识_第1张图片

在这里,外设一般指外设的数据寄存器(如ADC,SPI,I2C,DCMI等外设的数据寄存器);存储器一般是指片内SRAM外部存储器片内FLASH等。

外设寄存器到存储器传输:就是把外设数据寄存器内容转移到指定的内存空间中。
存储器到外设寄存器传输:就是把特定存储区域的内容转移到外设寄存器中。
存储器到存储器传输:就是把一个指定存储区内容拷贝到另一个指定的存储区中。
注意: DMA1 仅支持① ② ,不支持③。DMA2支持① ② ③。

=============================================================================================

解释一下为什么DMA1不支持存储器到存储器的传输:

《STM32从零开始学习历程》——DMA直接存储区访问理论知识_第2张图片
如上图所示,DMA2控制器ANB接口都与中间的总线矩阵相连接了,存储器RAM与总线矩阵相连接,因此DMA2可以实现存储器与存储器之间的数据传输。
而DMA1,其中一个AHB接口没有与总线矩阵相连接,固然也就无法实现存储器与存储器之间的数据传输了。
作为一个固定的知识点记住就好。

2. DMA的主要特点(针对STM32F4)

  1. DMA传输速率非常高效(原因为2)
  2. 实现数据传输无需CPU参与,DMA控制器是独立于CPU的

3. 功能框图分析

《STM32从零开始学习历程》——DMA直接存储区访问理论知识_第3张图片
STM32F4其中一个的DMA控制器框图如上图所示,下面我们来详细讲解一下这个框图。(STM32一共有两个DMA控制器)

=============================================================================================

  1. 首先我们来看一下DMA框图的最左边。如下图所示:
    《STM32从零开始学习历程》——DMA直接存储区访问理论知识_第4张图片

在上图中,左边的叫做通道,右边的叫做

好了,那么问题来了,什么是“通道”?什么是“”?

首先,“”是数据传输的一条链路,“通道”:emmm不知道怎么解释,只可意会不可言传,不同的通道对印着不同的DMA请求。

紧接着,从上图看到,对于一个DMA控制器共有8路“”,每一“”,又有8个“通道”!也就是说,对于一个DMA控制共有8 x 8 = 64 路通道可以用来独立的传输数据。

好了又有问题来了,那么这么多通道怎么用?是不是可以随便用?
答案是否定的!
每一个通道有着不同的传输请求映射!如下表所示:

DMA1:

外设请求 数据流0 数据流1 数据流2 数据流3 数据流4 数据流5 数据流6 数据流7
通道 0 SPI3_RX SPI3_RX SPI2_RX SPI2_TX SPI3_TX SPI3_TX
通道 1 I2C1_RX TIM7_UP TIM7_UP I2C1_RX I2C1_TX I2C1_TX
通道 2 TIM4_CH1 I2S3_EXT_RX TIM4_CH2 CH2 I2S2_EXT_TX I2S3_EXT_TX TIM4_UP TIM4_CH3
通道 3 I2S3_EXT_RX TIM2_UP TIM2_CH3 I2C3_RX I2S2_EXT_RX I2C3_TX TIM2_CH1 TIM2_CH2 TIM2_CH4 TIM2_UP TIM2_CH4
通道 4 UART5_RX USART3_RX UART4_RX USART3_TX UART4_TX USART2_RX USART2_TX UART5_TX
通道 5 UART8_TX UART7_TX TIM3_CH4 TIM3_UP UART7_RX TIM3_CH1 TIM3_TRIG TIM3_CH2 UART8_RX TIM3_CH3
通道 6 TIM5_CH3 TIM5_UP TIM5_CH4 TIM5_TRIG TIM5_CH1 TIM5_CH4 TIM5_TRIG TIM5_CH2 TIM5_UP
通道 7 TIM6_UP I2C2_RX I2C2_RX USART3_TX DAC1 DAC2 I2C2_TX

DMA2:

外设请求 数据流0 数据流1 数据流2 数据流3 数据流4 数据流5 数据流6 数据流7
通道 0 ADC1 TIM8_CH1 TIM8_CH2 TIM8_CH3 ADC1 TIM1_CH1 TIM1_CH2 TIM1_CH3
通道 1 DCMI ADC2 ADC2 SPI6_TX SPI6_RX DCMI
通道 2 ADC3 ADC3 SPI5_RX SPI5_TX CRYP_OUT CRYP_IN HASH_IN
通道 3 SPI1_RX SPI1_RX SPI1_TX SPI1_TX
通道 4 SPI4_RX SPI4_TX USART1_RX SDIO SART1_RX SDIO USART1_TX
通道 5 USART6_RX USART6_RX SPI4_RX SPI4_TX USART6_TX USART6_TX
通道 6 TIM1_TRIG TIM1_CH1 TIM1_CH2 TIM1_CH1 TIM1_CH4 TIM1_TRIG TIM1_COM TIM1_UP TIM1_CH3
通道 7 TIM8_UP TIM8_CH1 TIM8_CH2 TIM8_CH3 SPI5_RX SPI5_TX TIM8_CH4 TIM8_TRIG TIM8_COM

每个外设请求都会占用一个数据流通道,相同外设请求可以占用不同的数据流通道。

例如:DMA1的数据流0,我选择使用通道2 I2C1_RX,那么其他7路通道就不可以使用了。
再例如:DMA1的数据流2,通道1,TIM7_UP与DMA1的数据流4,通道1,TIM7_UP是可以同时使用的。也就是说只要不是同一个数据流就可以了。

好了,通道 和 流 的基本信息就已经介绍完了,总结一下:

① STM32F4共有2个DMA控制器
② DMA1不支持存储器到存储器传输
③ 每一个DMA有8路数据流
④ 每一路流有8个通道
⑤ 每一个通道有特定的功能映射,需要查阅上文的表格进行选择使用
⑥ 每一路流的只能有一个通道使用,一路流有一个以上通道使用是不允许的
⑦ 相同功能不同流的通道可以同时使用

=============================================================================================

  1. 这个“仲裁器”:
    《STM32从零开始学习历程》——DMA直接存储区访问理论知识_第5张图片

顾名思义,仲裁器是用来仲裁、评判的,一个DMA控制器有8个数据流,如果在某一时刻,我们使用同一个DMA控制器中的多个数据流进行传输数据,那么必然会导致有多个数据流,如果没有仲裁器,就像是十字路口没有红绿灯,会造成拥堵甚至事故,对于数据也是如此,因此需要一个仲裁器来设定数据流的优先传输的权力。

通过仲裁器来设定流的优先级时分为两个阶段,第一阶段为软件阶段,第二阶段为硬件阶段。
软件阶段: 用户可以通过配置优先级寄存器,将数据流优先级设定为:非常高,高,中和低四个级别。
硬件阶段: 如果两个或以上数据流的优先级一样,则仲裁器优先级设定转为硬件层面,他们的优先级取决于数据流的编号,编号越低则优先级越高,例如,在软件层面优先级相同的情况下,数据流1的优先级高于数据流5的优先级。

=============================================================================================

  1. FIFO:

我们要重点的来讲一下什么是FIFO,First In First Out。

《STM32从零开始学习历程》——DMA直接存储区访问理论知识_第6张图片

首先,FIFO叫做先进先出存储缓冲区,它介于源与目标之间,是一个数据的中转站。
每一个数据流都有4字大小的FIFO。1字 = 4字节 = 32位,4字 = 16字节。

对于这个缓冲区,存在有两种数据传输模式:第一种为直接模式,第二种为FIFO模式。
模式的选择由 DMA_SxFCR寄存器的DMDIS位控制,0为使能直接模式,1为禁止直接模式、开启FIFO模式。

直接模式: 数据经过FIFO,不在FIFO中停留,直接将数据传送到目标地址中。
FIFO模式: 数据经过FIFO,在FIFO中停留,根据设置的阈值,等到数据量达到了设定的阈值然后再发送到目标地址中。阈值可以设定为1/4,1/2,3/4,1。例如,阈值设定为1/4时,FIFO中的数据量达到16*(1/4)= 4 字节时将数据打包发送到目标地址中。

对于在FIFO中的数据,是以什么方式进行传输的呢?例如,阈值设定为1/4时,FIFO中也存满了4字节的数据,这4字节的数据怎么打包发送呢?它是4字节全部打包发送还是4字节分四次打包发送呢?这就需要另外一个寄存器进行选择配置了:DMA_SxCR中的MBURST(存储器突发传输配置)以及PBURST(外设突发传输配置),突发模式!

下一章节将详细的讲解何为突发模式!

4. DMA数据配置

1. 突发&节拍的配置

《STM32从零开始学习历程》——DMA直接存储区访问理论知识_第7张图片
位 24:23 MBURST:存储器突发传输配置 (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。

这里又来了一个“节拍”的概念,我们来详细的讲一下这是什么。

MSIZE FIFO级别 MBURST=INCR4 MBURST=INCR8 MBURST=INCR16
字节 1/4 4个节拍1次突发
字节 1/2 4个节拍2次突发 8个节拍1次突发
字节 3/4 4个节拍3次突发
字节 1 4个节拍4次突发 8个节拍2次突发 16个节拍1次突发
半字 1/4
半字 1/2 4个节拍1次突发
半字 3/4
半字 1 4个节拍2次突发 8个节拍1次突发
1/4
1/2
3/4
1 4个节拍1次突发

首先,FIFO大小为4个字,FIFO阈值 = FIFO级别 x FIFO大小。
例如:
FIFO级别为1/4时,FIFO阈值大小为 4 x(1/4) = 1字 = 4字节。
FIFO级别为3/4时,FIFO阈值大小为 4 x(3/4) = 3字 = 12字节。

=============================================================================================
当目标地址存储单元为字节时:
FIFO级别为1/4时:
当FIFO中存满4 x(1/4) = 1字 = 4字节时,完成1次突发,4字节数据一次性打包发送给目标地址,每次发送4个字节。

FIFO级别为1/2时:
当FIFO中存满4 x(1/2) = 2字 = 8字节时,完成2次突发,8字节数据分两次发送给目标地址,每次发送4个字节;
或者8字节数据一次性发送给目标地址,每次发送8个字节。

FIFO级别为3/4时:
当FIFO中存满4 x(3/4) = 3字 = 12字节时,完成3次突发,12字节数据分三次发送给目标地址,每次发送4个字节。

FIFO级别为1时:
当FIFO中存满4 x 1 = 4字 = 16字节时,可以以4次突发,16字节数据分四次发送给目标地址,每次发送4个字节;
可以以2次突发,16字节数据分两次发送给目标地址,每次发送8个字节;
也可以以1次突发,16字节数据一次性传输给目标地址,每次发送16个字节。

=============================================================================================

为什么FIFO级别为1/4时不能以8个节拍进行发送呢?

因为,FIFO的级别为1/4,其阈值为1个字,4个字节,此时FIFO存满也就4个字节的数据,固然无法实现8个节拍进行发送。

为什么MSIZE为半字时,无法实现FIFO的级别为1/4以4个字节发送?
因为,FIFO的级别为1/4,其阈值为1个字,4个字节;如果是4个节拍发送的话,就是4个半字=8个字节发送,此时FIFO最大也就只有4个字节的数据,也便无法实现发送。

为什么MSIZE为字时,无法实现FIFO的级别为3/4以3个字节发送?
因为,FIFO的级别为3/4,其阈值为3个字,12个字节;如果实现4个节拍发送的画,就是4个字=16个字节发送,此时FIFO最大也就12个字节的数据,无法实现发送。

下面再来解释下为什么为什么MSIZE为半字时,可以实现FIFO的级别为1/2以8个字节发送?
因为,FIFO的级别为1/2,其阈值为2个字,8个字节;以4个节拍也就是4个半字=8个字节发送,正好等于FIFO阈值的数据大小(8个字节),所以可以成功发送。

以此类推!

=============================================================================================

PS: 在这里为了更好的理解FIFO阈值配置表格,可以将节拍理解为MSIZE,例如:MSIZE为字节时,FIFO级别为1/4(1个字,4个字节),4个节拍1次突发可以理解为4个字节一次突发;MSIZE为半字时,8个节拍一次突发可以理解为8个半字(4个字,16个字节),FIFO级别为1时(4个字,16个字节),可以实现一次突发。
也就是说:
节拍数(MSIZE数)x 突发数 = FIFO级别 x FIFO大小 (FIFO大小固定为4字)
例如:MSIZE = 半字;FIFO级别为 = 1/2;MBURST = 4个节拍1次突发
节拍数(MISIZE数)= 半字
突发数 = 4节拍 x 1次突发 = 4 半字 = 2 字 = 8字节
那么:节拍数(MSIZE数)x 突发数 = 8 字节
FIFO阈值 = 1/2 x 4 字 = 2 字 = 8字节
由此可见 FIFO阈值 = 节拍数(MSIZE数)x 突发数。

2. 循环模式
循环模式相对应于一次模式。一次模式就是传输一次就停止传输,下一次传输需要手动控制,而循环模式在传输一次后会自动按照相同配置重新传输,周而复始直至被控制停止或传输发生错误。
可以通过DMA_SxCR 寄存器的CIRC 位可以使能循环模式。

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

4. 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 中断控制位将产生直接模式错误中断。

5. 初始化结构体库函数

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; 								//外设突发传输
			
			}

1) DMA_Channel:
DMA 请求通道选择,可选通道0 至通道7,每个外设对应固定的通道,具体设置值需要查表DMA1 各个通道的请求映像和表DMA2 各个通道的请求映像。
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_BufferSize:
设定待传输数据数目,初始化设定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 采集传输是直接模式,要求使用单次模式。

你可能感兴趣的:(STM32,ARM,嵌入式,stm32)