DMA
1. DMA直接存储器存取
- DMA(Direct Memory Access)
直接存储器存取 - DMA可以提供
外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
外设—般指的是外设的数据寄存器DR,Data Register,比如ADC的数据寄存器、串口的数据寄存器等等;这里存储器,指的就是运行内存SRAM和程序存储器Flash,是我们存储变量数组和程序代码的地方。
12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)每个通道都支持软件触发和特定的硬件触发
意思就是每个DMA的通道,它的硬件触发源是不一样的。你要使用某个外设的硬件触发源。就得使用它连接的那个通道,而不能任意选择通道。
- STM32F103C8T6 DMA资源:DMA1(7个通道)
这里如果DMA进行的是存储器到存储器的数据转运,比如我们想把Flash里的—批数据,转运到SRAM里去,那就需要软件触发了。使用软件触发之后,DMA就会—股脑地把这批数据。以最快的速度,全部转运完成,这也时我们想要的效果。
那如果DMA进行的是外设到存储器的数据转运,就不能—股脑地转运了,因为外设的数据是有一定时机的,所以这时我们就需要用硬件触发。比如转运ADC的数据,那就需要等到ADC每个通道AD转换完成后,硬件触发一次DMA,之后DMA再转运,触发一次转运一次,这样数掘才是正确的,才是我们想要的效果。
我们来看一下STM32的存储器映像,既然DMA是在存储器之间进行数据转运的,那我们就应该要了解一下,STM32中都有哪些存储器,这些存储器又是被安排到了哪些地址上,这就是存储器映像的内容。
在这个表里,无论是Flash,还是SRAM,还是外设寄存器,它们都是存储器的一种,包括外设寄存器,实际上也是存储器。在DMA简介中,我们说的是外设到存储器,存储器到存储器,本质上其实都是存储器之间的数据转运,说成外设到存储器,只不过是STM32它特别指定了可以转运外设的存储器而已。


左上角这里是Cortex-M3内核,里面包含了CPU和内核外设等等,剩下的这所有东西,你都可以把它看成是存储器,所以总共就是CPU和存储器两个东西。Flash是主闪存,SRAM是运行内存,各个外设,都可以看成是寄存器,也是一种SRAM存储器。
寄存器是一种特殊的存储器,一方面,CPU可以对寄存器进行读写,就像读写运行内存一样,另一方面,寄存器的每一位背后,都连接了一根导线,这些导线可以用于控制外设电路的状态,比如置引脚的高低电平、导通和断开开关、切换数据选择器,或者多位组合起来,当做计数器、数据寄存器等等。所以,寄存器是连接软件和硬件的桥梁,软件读写寄存器,就相当于在控制硬件的执行。
回到这里,既然外设就是寄存器,寄存器就是存储器,那使用DMA进行数据转运,就都可以归为一类问题了。就是从某个地址取内容,再放到另一个地址去。

为了高效有条理地访问存储器,这里设计了一个总线矩阵,总线矩阵的左端,是主动单元,也就是拥有存储器的访问权,右边这些,是被动单元,它们的存储器只能被左边的主动单元读写。主动单元这里,内核有DCode和系统总线,可以访问右边的存储器,其中DCode总线是专门访问Flash的,系统总线是访问其他东西的。
另外,由于DMA要转运数据,所以DMA也必须要有访问的主动权。那主动单元,除了内核CPU,剩下的就是DMA总线了。这里DMA1有一条DMA总线,DMA2也有一条DMA总线,下面这还有一条DMA总线,这是以太网外设自己私有的DMA,这个可以不用管的。
在DMA1和DMA2里面,可以看到,DMA1有7个通道,DMA2有5个通道,各个通道可以分别设置它们转运数据的源地址和目的地址,这样它们就可以各自独立地工作了。

接着下面这里有个仲裁器,这个是因为,虽然多个通道可以独立转运数据,但是最终DMA总线只有一条,所以所有的通道都只能分时复用这一条DMA总线。如果产生了冲突,那就会由仲裁器,根据通道的优先级来决定谁来使用。
另外在总线矩阵这里,也会有个仲裁器,如果DMA和CPU都要访问同一个目标,那么DMA就会暂停CPU的访问,以防止冲突。不过总线仲裁器,仍然会保证CPU得到一半的总线带宽,使CPU也能正常的工作。

下面这里是AHB从设备,也就是DMA自身的寄存器,因为DMA作为一个外设,它自己也会有相应的配置寄存器,这里连接在了总线右边的AHB总线上,所以DMA,即是总线矩阵的主动单元,可以读写各种存储器,也是AHB总线上的被动单元。CPU通过这一条线路,就可以对DMA进行配置了。

接着继续看这里,是DMA请求,请求就是触发的意思,这条线路右边的触发源,是各个外设,所以这个DMA请求就是DMA的硬件触发源。比如ADC转换完成、串口接收到数据,需要触发DMA转运数据的时候,就会通过这条线路,向DMA发出硬件触发信号,之后DMA就可以执行数据转运的工作了。这就是DMA请求的作用。
到这里,有关DMA的结构就讲的差不多了,其中包括:用于访问各个存储器的DMA总线;内部的多个通道,可以进行独立的数据转运;仲裁器,用于调度各个通道,防止产生冲突;AHB从设备,用于配置DMA参数;DMA请求,用于硬件触发DMA的数据转运,这就是这个DMA的各个部分和作用。
2. DMA基本结构

在这个图里,这两部分就是数据转运的两大站点了,左边是外设寄存器站点,右边是存储器站点,包括Flash和SRAM。
DMA的数据转运,可以是从外设到存储器,也可以从存储器到外设,具体是向左还是向右,有一个方向的参数,可以进行控制。
另外,还有一种转运方式,就是存储器到存储器,比如Flash到SRAM或者SRAM到SRAM,这两种方式。由于Flash是只读的,所以DMA不可以进行SRAM到Flash,或者Flash到Flash的转运操作。
然后我们继续看这两边的参数,既然要进行数据转运,那肯定就要指定从哪里转到哪里,具体怎么转了,所以外设和存储器两个站点,就都有3个参数。
第一个是起始地址,有外设端的起始地址,和存储器端的起始地址,这两个参数决定了数据是从哪里来,到哪里去的。
之后第二个参数是数据宽度,这个参数的作用是,指定一次转运要按多大的数据宽度来进行,它可以选择字节Byte (字节就是8位,uint8_t) 、半字HalfWord(半字是16位,uint16_t)和字Word(字是32位,uint32_t),比如转运ADC的数据,ADC的结果是uint16_t这么大,所以这个参数就要选择半字,一次转运一个uint16_t,这样才对。
然后第三个参数,是地址是否自增,比如ADC扫描模式,用DMA进行数据转运,外设地址是ADC_DR寄存器,寄存器这边,显然地址是不用自增的,如果自增,那下一次转运就跑到别的寄存器那里去了,存储器这边,地址就需要自增,每转运一个数据后,就往后那个坑,要不然下次再转就把上次的覆盖掉了。这就是地址是否自增的作用,就是指定,是不是要转运一次挪个坑,这个意思。
在STM32手册里,所说的存储器,一般是特指Flash和SRAM,不包含外设寄存器。外设寄存器,他一般直接称作外设,所以就是外设到存储器,存储器到存储器,这样来描述。虽然我们刚才说了,寄存器也是存储器的一种,但是STM32还是使用了外设和存储器来作为区分,这个注意一下描述方法的不同。

接着往下面看,传输计数器,是用来指定,我总共需要转运几次的。这个传输计数器是一个自减计数器,比如你给它写一个5,那DMA就只能进行5次数据转运,转运过程中,每转运一次,计数器的数就会减1,当传输计数器减到0之后,DMA就不会再进行数据转运了。
另外,它减到0之后,之前自增的地址,也会恢复到起始地址的位置,以方便之后DMA开始新一轮的转运。
在传输计数器的右边,有一个自动重装器,这个自动重装器的作用就是,传输计数器减到0之后,是否要自动恢复到最初的值。比如最初传输计数器给5,如果不使用自动重装器,那转运5次后,DMA就结束了,反之,计数器减到0后,就会立即重装到初始值5。
这个就是自动重装器,它决定了转运的模式,如果不重装,就是正常的单次模式,如果重装,就是循环模式。比如如果你想转运一个数组,那一般就是单次模式;如果是ADC扫描模式+连续转换那为了配合ADC。DMA需要使用循环模式。

这一块,就是DMA的触发控制了。触发,就是决定DMA需要在什么时机进行转运的。触发源,有硬件触发和软件触发,具体选择哪个,由M2M这个参数决定,M2M就是Memory to Memory (存储器到存储器)。
当我们给M2M位1时,DMA就会选择软件触发,软件触发一般适用于存储器到存储器的转运,因为存储器到存储器的转运,是软件启动、不需要时机,并且想尽快完成的任务。所以上面这里,M2M位给1,就是软件触发,就是应用在存储器到存储器转运的情况。
当M2M位给0,那就是使用硬件触发了。硬件触发源可以选择ADC、串口、定时器等等。使用硬件触发的转运,一般都是与外设有关的转运。这些转运需要一定的时机,比如ADC转换完成、串口收到数据、定时时间到等等,所以需要使用硬件触发,在硬件达到这些时机时,传一个信号过来,来触发DMA进行转运,这就是硬件触发。
3. 基于DMA基本结构的一些问题
问题1:那如何进行存储器到存储器的数据转运,方向反过来可以吗?
如果要进行存储器到存储器的数据转运。那我们就需要把其中一个存储器的地址,放在外设的这个站点,这样就能进行存储器到存储器的转运了。只要你在外设起始地址里写Flash或者SRAM的地址,那它就会去Flash或SRAM找数据。这个站点虽然叫外设寄存器,但是它就只是个名字而已。甚至你可以在外设站点写存储器的地址,存储器站点写外设的地址,然后方向参数给反过来,这样也是可以的,只是ST公司给它起了这样的名字而已。你也可以把它叫做站点A、站点B从A到B或者从B到A转运数据。
问题2:在DMA中软件触发的执行逻辑?和外部中断、ADC的软件触发有什么区别?
这个软件触发并不是调用某个函数一次,触发一次,它这个软件触发的执行逻辑是,以最快的速度,连续不断地触发DMA,争取早日把传输计数器清零,完成这一轮的转换。所以这里的软件触发,和我们之前外部中断和ADC的软件触发可能不太一样,你可以把它理解成连续触发,那这个软件触发和(自动重装器)循环模式,不能同时用。因为软件触发就是想把传输计数器清零,循环模式是清零后自动重装,如果同时用的话,那DMA就停不下来了,这就是软件触发。
问题3:DMA的转运条件?
DMA进行转运,有几个条件,第一,就是开关控制,DMA_Cmd必须使能;第二,就是传输计数器必须大于0;第三,就是触发源,必须有触发信号。触发一次,转运一次,传输计数器自减一次,当传输计数器等于0,且没有自动重装时。这时无论是否触发,DMA都不会再进行转运了,此时就需要DMA_Cmd,给DISABLE,关闭DMA,再为传输计数器写入一个大于0的数,再DMA_Cmd,给ENABLE,开启DMA,DMA才能继续工作。
注意一下,写传输计数器时,必须要先关闭DMA,再进行,不能在DMA开启时,写传输计数器,这是手册里的规定。
阿源的知识库