跳到主要内容

DMA直接存储器存取

1. DMA简介

DMA(Direct Memory Access)直接存储器存取,可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源。

12个独立可配置的通道:DMA1(7个通道),DMA2(5个通道)。

每个通道都支持软件触发和特定的硬件触发。

STM32F103C8T6 DMA资源:DMA1(7个通道)

2. 存储器映像

Image

每个字节都分配一个独一无二的地址,以便精准访问。

比如如果某个数据开头是0800,那它就是属于主闪存的。

内核外设有NVIC、SysTick。因为内核外设是其他厂家设计的,所以分到的地址不同。

Image

3. DMA基本结构

Image

Flash是主闪存,是ROM只读存储器的一种,也可以配置Flash接口控制器写入Flash(先按页擦除,再写入数据)。SRAM是运行内存,各个外设都可以看成寄存器,也是一种SRAM存储器。

寄存器是一种特殊的存储器,CPU可以对寄存器进行读写,寄存器的每一位都连接了一根导线,可以用于控制外设电路的状态:如置引脚的高低电平、导通和断开开关、切换数据选择器;或者多位相组合。所以寄存器是连接软件和硬件的桥梁。

为了高效访问存储器,设计了一个总线矩阵,总线矩阵的左端是主动单元,也就是拥有存储器的访问权,右边是被动单元,其存储器只能被左边的主动单元读写。主动单元中,内核有DCode和系统总线,可以访问右边的存储器;DCode总线是专门访问Flash的,系统总线是访问其他东西的。

DMA1、2都分别有一条DMA总线,下面还有一条DMA,是以太网外设自己私有的DMA。DMA1有7个通道,DMA2有5个通道,各个通道可以分别设置它们转运数据的源地址和目的地址,从而独立工作。

下面有一个仲裁器,用于调度各个通道。虽然多个通道可以独立转运数据,所有的通道都只能分时复用这一条DMA总线。如果产生了冲突,那就会由仲裁器,根据通道的优先级,

如果DMA和CPU都要访问同一个目标,那么DMA就会暂停CPU的访问,总线仲裁器仍然保证CPU得到一半的总线带宽,使CPU也能正常工作。

AHB从设备也就是DMA自身的寄存器,用于配置DMA参数。连接在了总线右边的AHB总线上,所以DMA即是总线矩阵的主动单元,可以读写各种存储器,这些寄存器也是AHB总线上的被动单元。CPU通过这一条线路就可以配置DMA。

DMA请求,用于硬件触发DMA数据转运。请求就是触发的意思,右边的触发源是各个外设,这个DMA请求就是DMA的硬件触发源,比如ADC转换完成、串口接收到数据,需要触发DMA转运数据时,就会通过这条线路,向DMA发出硬件触发信号,即可执行数据转运。

Image

左边是外设寄存器站点,右边是存储器站点,包括Flash和SRAM(手册的存储器特指Flash和SRAM)。外设寄存器一般直接称作外设。

虽然寄存器也是存储器的一种,但是STM32还是使用了外设和存储器来作为区分。

由于Flash是只读的,所以DMA不可以进行SRAM到Flash,或者Flash到FLash的转运操作。

地址是否自增:指的是下一次转运是不是要把地址移动到下一个位置去,相当于指针。比如ADC扫描模式,用DMA进行数据转运,外设地址是ADC_DR寄存器,寄存器地址是不用自增的。如果自增,下一次转运就到别的寄存器处了。存储器的地址需要自增,否则会覆盖上次的数据。

如果要进行存储器到存储器的数据转运,那就需要把其中一个存储器的地址放在外设的起始地址。只要在外设起始地址里写Flash或者SRAM的地址,就会去Flash或SRAM里找数据。甚至可以在外设站点写存储器的地址,存储器站点写外设的地址。

传输计数器用来指定总共需要转运几次,是一个自减计数器。每转运一次,就会自减一次,减到0之后就不会再转运。减到0后之前自增的地址也会恢复到起始地址的位置。

自动重装器用来决定转运的模式,决定传输计数器减到0之后是否要自动恢复到最初的值。转运一个数组一般就用单次模式,如果是ADC+扫描模式就会使用上这个循环模式。

下面部分是DMA的触发控制,决定DMA在什么时机进行转运。M2M(Memory to Memory,存储器到存储器)决定具体使用硬件还是软件触发,置1即为软件触发,置0即为硬件触发。软件触发适用于存储器到存储器,它的执行逻辑是以最快的速度不断触发DMA,争取早日清零传输计数器完成一轮转换,和外部中断和ADC的软件触发不太一样,类似连续触发。硬件触发源可以选择ADC、串口、定时器等,一般是与外设有关的转运,转运需要一定的时机,比如ADC转换完成、串口收到数据、定时时间到,达到时机时传输信号,完成触发。

DMA转运的条件:

  1. 开关控制:DMA_Cmd必须使能
  2. 传输计数器必须大于0
  3. 触发源必须有触发信号

当传输计数器等于0,没有自动重装,无论是否触发,DMA都不会再转运。此时需要DMA_Cmd,给DISABLE,关闭DMA。再为传输计数器写入一个大于0的数,DMA_CMd,给ENABLE,使能DMA。

危险

写传输计数器时,必须要先关闭DMA再进行,不能在DMA开启时写入。

4. DMA请求

Image

DMA的七个通道每个都有数据选择器,可以选择硬件触发或者软件触发。

EN并不是数据选择器的控制位,而且决定这个数据选择器要不要工作。EN=0,数据选择器不工作,EN=1,数据选择器工作。当M2M=1时,选择软件触发。

每个通道的硬件触发源都是不同的。如果需要ADC1来触发,就必须选择通道1,如果需要定时器2的更新事件来触发,就必须选择通道2。

还需要看对应的外设是否开启了DMA输出。比如ADC1有一个库函数ADC_DMACmd,用于开启ADC1的DMA输出。

7个通道进入优先级判断,产生内部的DMA请求,这个优先级的判断类似于中断的优先级。默认优先级是通道号越小,优先级越高。也可以在程序中配置优先级。

5. 数据宽度与对齐

Image

如果数据宽度一致,就是正常的一个个转运;不一致则需要对齐。

如果目标数据宽度比源端的数据宽度更大,那就在前面补0。

6. DMA工作实例

数据转运+DMA

将SRAM里的数组DataA,转运到另一个数组DataB中。

Image

在这种情况下,外设地址是DataA的首地址,存储器地址是DataB的首地址。数据宽度,两个都是Uint_8,8位字节参数。数据都应该自增。

传输计数器给7,不用自动重装。触发选择使用软件触发,因为是存储器-存储器,不需要等待硬件时机。

ADC扫描模式+DMA

这是最常用的工作模式。因为ADC扫描会带来数据覆盖的问题,配合DMA才能打出组合拳。

Image

在这种情况下,外设地址是ADC_DR寄存器的地址;存储器地址,可以在SRAM中定义一个数组ADValue,将ADValue的地址作为存储器地址;ADC_DR和SRAM数组,要的都是uint16_t的数据,数据宽度都是16位半字传输;外设地址不自增,存储器地址自增。传输方向是外设->存储器。

传输计数器给7。自动重装:如果ADC单次扫描,DMA传输计数器可以不自动重装,ADC连续扫描,DMA可以自动重装,ADC启动下一轮转换,DMA也启动下一轮转运。

ADC和DMA同步工作。

ADC_DR的值是在ADC单个通道转换完成后才有效,所以DMA转运需要和ADC单个通道转换完成同步,选择硬件触发。单个通道转换完成后,不产生任何标志位和中断,判断不了转换是否完成,但是会发出DMA请求,触发DMA转运。