一、DMA简介

直接存储器访问(Direct Memory Access),简称DMA。

前述的对I/O设备的数据搬运操作,无论是轮询方式还是中断方式,都需要CPU读写参与其中,在要进行大量数据传输时,CPU的利用效率将大大降低;

DMA是CPU一个用于数据从一个地址空间到另一地址空间“搬运”(拷贝)的组件,数据拷贝过程不需CPU干预,数据拷贝结束则通知CPU处理。因此,大量数据拷贝时,使用DMA可以释放CPU资源。

DMA就是基于以上设想设计的,它的作用就是解决大量数据转移过度消耗CPU资源的问题。有了DMA使CPU更专注于更加实用的操作–计算、控制等。

二、STM32F1的DMA控制器

ARM Cortex-M3内核的STM32F1系列单片机中的DMA 控制器最多可以配置两个 DMA1 和 DMA2,DMA2 只存在于大容量的单片机中。

其中 DMA1 有 7 个通道, DMA2 有 5 个通道,这里的通道可以理解为传输数据的一种管道。

2.1 DMA 功能框图

1) DMA 请求和通道

如果外设要想通过 DMA 来传输数据,必须先给 DMA 控制器发送 DMA 请求, DMA 收到请求信号之后,控制器会给外设一个应答信号,当外设应答后且 DMA 控制器收到应答信号之后,就会启动 DMA 的传输,直到传输完毕。
DMA 有 DMA1 和 DMA2 两个控制器, DMA1 有 7 个通道, DMA2 有 5 个通道,不同的 DMA 控制器的通道对应着不同的外设请求,这决定了我们在软件编程上该怎么设置,具体见 DMA 请求映像表。 需要注意虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能同时接收多个。

2) 仲裁器

当发生多个 DMA 通道请求时,就意味着有先后响应处理的顺序问题,这个就由仲裁器也管理。仲裁器管理 DMA 通道请求分为两个阶段。第一阶段属于软件阶段,可以在 DMA_CCRx 寄存器中设置,有 4 个等级:非常高、高、中和低四个优先级。第二阶段属于硬件阶段,如果两个或以上的 DMA 通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高,比如通道 0 高于通道 1。在大容量产品和互联型产品中, DMA1 控制器拥有高于 DMA2 控制器的优先级。

2.2 DMA的数据传输配置

1)数据传输方向

DMA 传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储器到存储器。其中M2M时内部FLASH存储器,也看做外设;
具体的方向 DMA_CCR 位 4 DIR 配置: 0 表示从外设到存储器, 1 表示从存储器到外设。这里面涉及到的外设地址由 DMA_CPAR 配置,存储器地址由 DMA_CMAR 配置。

a) 外设到存储器

当我们使用从外设到存储器传输时,以 ADC 采集为例。 DMA 外设寄存器的地址对应的就是 ADC数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(用来接收存储 AD 采集的数据)的地址。方向我们设置外设为源地址。

b) 存储器到外设

当我们使用从存储器到外设传输时,以串口向电脑端发送数据为例。 DMA 外设寄存器的地址对应的就是串口数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储通过串口发送到电脑的数据)的地址。方向我们设置外设为目标地址。

c) 存储器到存储器

当我们使用从存储器到存储器传输时,以内部 FLASH 向内部 SRAM 复制数据为例。 DMA 外设寄存器的地址对应的就是内部 FLASH(我们这里把内部 FALSH 当作一个外设来看)的地址, DMA存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部 FLASH 的数据)的地址。方向我们设置外设(即内部FLASH)为源地址。跟上面两个不一样的是,这里需要把DMA_CCR 位 14: MEM2MEM:存储器到存储器模式配置为 1,启动 M2M 模式。

2)传输的数据类型和长度

当我们配置好数据要从哪里来到哪里去之后,我们还需要知道我们要传输的数据是多少,数据的单位是什么。

以串口向电脑发送数据为例,我们可以一次性给电脑发送很多数据,具体多少由 DMA_CNDTR配置,这是一个 32 位的寄存器,一次最多只能传输 65535 个数据。

要想数据传输正确,源和目标地址存储的数据宽度还必须一致,串口数据寄存器是 8 位的,所以我们定义的要发送的数据也必须是 8 位。外设的数据宽度由 DMA_CCR 的 PSIZE[1:0] 配置,可以是 8/16/32 位,存储器的数据宽度由 DMA_CCR 的 MSIZE[1:0] 配置,可以是 8/16/32 位。

在 DMA 控制器的控制下,数据要想有条不紊的从一个地方搬到另外一个地方,还必须正确设置两边数据指针的增量模式。外设的地址指针由 DMA_CCRx 的 PINC 配置,存储器的地址指针由MINC 配置。以串口向电脑发送数据为例,要发送的数据很多,每发送完一个,那么存储器的地址指针就应该加 1,而串口数据寄存器只有一个,那么外设的地址指针就固定不变。具体的数据指针的增量模式由实际情况决定。

3)传输模式

传输完成还分两种模式,单次传输 和 循环传输;一次传输很好理解,即是传输一次之后就停止,要想再传输的话,必须关断 DMA 使能后再重新配置后才能继续传输。循环传输则是一次传输完成之后又恢复第一次传输时的配置循环传输,不断的重复。具体的由 DMA_CCR 寄存器的CIRC 循环模式位控制。

当PSIZE和MSIZE不相同时, DMA模块按照下表进行数据对齐 (PINC = MINC = 1)

源端宽度

目标宽度

传输数目

源:地址/数据

传输操作

目标:地址/数据

8

8

4

0x0 / B0
0x1 / B1
0x2 / B2
0x3 / B3

1: 在0x0读B0[7:0],在0x0写B0[7:0]
2: 在0x1读B1[7:0],在0x1写B1[7:0]
3: 在0x2读B2[7:0],在0x2写B2[7:0]
4: 在0x3读B3[7:0],在0x3写B3[7:0]

0x0 / B0
0x1 / B1
0x2 / B2
0x3 / B3

8

16

4

0x0 / 00B0
0x2 / 00B1
0x4 / 00B2
0x6 / 00B3

1: 在0x0读B0[7:0],在0x0写00B0[15:0]
2: 在0x1读B1[7:0],在0x2写00B1[15:0]
3: 在0x2读B2[7:0],在0x4写00B2[15:0]
4: 在0x3读B3[7:0],在0x6写00B3[15:0]

0x0 / B0
0x1 / B1
0x2 / B2
0x3 / B3

8

32

4

0x0 / B0
0x1 / B1
0x2 / B2
0x3 / B3

1: 在0x0读B0[7:0],在0x0写000000B0[31:0]
2: 在0x1读B1[7:0],在0x4写000000B1[31:0]
3: 在0x2读B2[7:0],在0x8写000000B2[31:0]
4: 在0x3读B3[7:0],在0xC写000000B3[31:0]

0x0 / 000000B0
0x4 / 000000B1
0x8 / 000000B2
0xC / 000000B3

16

8

4

0x0 / B0
0x1 / B2
0x2 / B4
0x3 / B6

1: 在0x0读B1B0[15:0],在0x0写B0[7:0]
2: 在0x2读B3B2[15:0],在0x1写B2[7:0]
3: 在0x4读B5B4[15:0],在0x2写B4[7:0]
4: 在0x6读B7B6[15:0],在0x3写B6[7:0]

0x0 / B1B0
0x2 / B3B2
0x4 / B5B4
0x6 / B7B6

16

16

4

0x0 / B1B0
0x2 / B3B2
0x4 / B5B4
0x6 / B7B6

1: 在0x0读B1B0[15:0],在0x0写B1B0[15:0]
2: 在0x2读B3B2[15:0],在0x2写B3B2[15:0]
3: 在0x4读B5B4[15:0],在0x4写B5B4[15:0]
4: 在0x6读B7B6[15:0],在0x6写B7B6[15:0]

0x0 / B1B0
0x2 / B3B2
0x4 / B5B4
0x6 / B7B6

16

32

4

0x0 / 0000B1B0
0x4 / 0000B3B2
0x8 / 0000B5B4
0xC / 0000B7B6

1: 在0x0读B1B0[15:0],在0x0写0000B1B0[31:0]
2: 在0x2读B3B2[15:0],在0x4写0000B3B2[31:0]
3: 在0x4读B5B4[15:0],在0x8写0000B5B4[31:0]
4: 在0x6读B7B6[15:0],在0xC写0000B7B6[31:0]

0x0 / B1B0
0x2 / B3B2
0x4 / B5B4
0x6 / B7B6

32

8

4

0x0 / B3B2B1B0
0x4 / B7B6B5B4
0x8 / BBBAB9B8
0xC / BFBEBDBC

1: 在0x0读B3B2B1B0[31:0],在0x0写B0[7:0]
2: 在0x4读B7B6B5B4[31:0],在0x1写B4[7:0]
3: 在0x8读BBBAB9B8[31:0],在0x2写B8[7:0]
4: 在0xC读BFBEBDBC[31:0],在0x3写BC[7:0]

0x0 / B0
0x1 / B4
0x2 / B8
0x3 / BC

32

16

4

0x0 / B1B0
0x2 / B5B4
0x4 / B9B8
0x6 / BDBC

1: 在0x0读B3B2B1B0[31:0],在0x0写B1B0[15:0]
2: 在0x4读B7B6B5B4[31:0],在0x2写B5B4[15:0]
3: 在0x8读BBBAB9B8[31:0],在0x4写B9B8[15:0]
4: 在0xC读BFBEBDBC[31:0],在0x6写BDBC[15:0]

0x0 / B3B2B1B0
0x4 / B7B6B5B4
0x8 / BBBAB9B8
0xC / BFBEBDBC

32

32

4

0x0 / B3B2B1B0
0x4 / B7B6B5B4
0x8 / BBBAB9B8
0xC / BFBEBDBC

1: 在0x0读B3B2B1B0[31:0],在0x0写B3B2B1B0[31:0]
2: 在0x4读B7B6B5B4[31:0],在0x4写B7B6B5B4[31:0]
3: 在0x8读BBBAB9B8[31:0],在0x8写BBBAB9B8[31:0]
4: 在0xC读BFBEBDBC[31:0],在0xC写BFBEBDBC[31:0]

0x0 / B3B2B1B0
0x4 / B7B6B5B4
0x8 / BBBAB9B8
0xC / BFBEBDBC

4)DMA传输标志位和中断

数据什么时候传输完成,我们可以通过查询标志位或者通过中断的方式来鉴别。每个 DMA 通道在 DMA 传输过半、传输完成和传输错误时都会有相应的标志位,如果使能了该类型的中断后,则会产生中断。有关各个标志位的详细描述请参考 DMA 中断状态寄存器 DMA_ISR 的详细描述。

三、DMA的HAL库编程

详见《NANO_STM32f103开发指南-HAL库版本》,第22章 DMA实验部分;

  1. 使能 DMA1 时钟
  2. 初始化 DMA 通道 4,包括配置通道,外设地址,存储器地址,传输数据量等参数
  3. 使能串口 DMA 发送
  4. 使能 DMA 通道 ,启动传输。
  5. 查询 DMA 传输状态
  6. DMA 中断使用方法
物联沃分享整理
物联沃-IOTWORD物联网 » 深入解读DMA技术

发表评论