DMA介绍
DMA可以直接访问STM32内部存储器的,直接存储器存取,包括运行内存SRAM,程序存储器Flash和寄存器等等,DMA都有权限访问他们,所以DMA才能完成数据转运的工作。
外设指的是,外设的数据寄存器DR,比如ADC的数据寄存器,串口的数据寄存器等。
存储器指的是,运行内存SRAM,和程序存储器Flash,是我们存储变量数组和代码的过程。
每个通道都支持软件触发和特定的硬件触发。
这里如果DMA进行的是存储器到存储器的数据转移,比如我们想把Flash的一批数据转运到SRAM去,那就需要软件触发了。使用软件触发之后,DMA就是一股脑的把这批数据,以最快的速度全部转运完成,这也是我们想要的效果。
如果DMA,进行的是外设到存储器的数据转移,就不能一股脑的转运了。因为外设的数据是有一定时机的,所以这时我们就需要用硬件触发。
比如转运ADC的数据,那就需要等到每个ADC通道AD转化完成后,硬件触发一次DMA,之后DMA再转运,触发一次转运一次,这样数据才是正确的,才是我们想要的效果。
所以存储器到存储器的数据转运,我们一般使用软件触发,外设到存储器的数据转运,我们一般使用硬件触发。
那在这里我写的是特定的硬件触发,意思就是每个的通道,它的硬件触发源是不一样的,不能任意选择通道。
STM32存储器
在这个表里,无论是Flash,还是SRAM,还是外设寄存器,它们都是存储器的一种,包括外设寄存器,实际上也是存储器。在DMA简介中,我们说的是外设到存储器,存储器到存储器,本质上其实都是存储器之间的数据转运,说成外设到存储器,只不过是STM32他特别指定了可以转运外设的存储器而已。
ROM:只读存储器,是一种非易失性,掉电不丢失的存储器。
RAM:随机存储器,是一种易失性,掉电丢失的存储器。
选项字节,存的主要是Flash的读保护,写保护,还有看门狗等等的配置。
内核外设就是NVIC 和Systick,内核外设和外设,不是一个厂家配置的,所以不一样。
这些存储器又是被安排到了哪些地址上
在图里,有灰色填充的Rerved,就是保留区域,没有用到。 这里0地址,实际上也没有存储器的,这里写的是别名到Flash或者系统寄存器,取决于BOOT引脚。因为程序从0地址开始运行的,所以想要把执行的程序,映射到0地址上来,如映射到Flash区,就是从Flash开始执行。
DMA框图
左上角这里是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的各个部分和作用其中包括:
用于访问各个存储器的DMA总线;
内部的多个通道,可以进行独立的数据转运;
仲裁器,用于调度各个通道,防止产生冲突;
AHB从设备,用于配置DMA参数;
DMA请求,用于硬件触发DMA的数据转运,这就是这个DMA的各个部分和作用。
注意一下:就是这里的Flash,它是ROM只读存储器的一种,如果通过总线直接访问的话,无论是CPU,还是DMA,都是只读的,只能读取数据,而不能写入。
如果你DMA的目的地址,填了Flash的区域,那转运时,就会出错。当然Flash也不是绝对的不可写入,我们可以配置这个Flash接口控制器,对Flash进行写入,这个流程就比较麻烦了,要先对Flash按页进行擦除,再写入数据。
总之就是CPU或者DMA直接访问Flash的话,是只可以读而不可以写的,然后SRAM是运行内存,可以任意读写,没有问题的。
DMA基本结构图
在这个图里,这两部分就是数据转运的两大站点了,左边是外设寄存器站点,右边是存储器站点
是包括Flash和SRAM.。在STM32手册,它所说的存储器,一般特指Flash和SRAM,不包括外设寄存器。
DMA的数据转运,可以是从外设到存储器,也可以从存储器到外设,具体是向左还是向右,
有一个方向的参数,可以进行控制。
另外,还有一种转运方式,就是存储器到存储器,比如Flash到SRAM,或SRAM到SRAM 这两种方式。由于Flash是只读的,所以DMA不可以进行SRAM到Flash, 或者Flash到Fash的转运操作。
起始地址,决定了数据到哪里来,到哪里去的。
数据宽度,作用是指定一次转运要按照多大的数据宽度来进行,可以选择字节Byte、半字HalfWord、字Word。 字节是8位的,也就是转运一个uint_8t。半字是16位,也就是转运一个uint_16t。 字是16位,也就是转运一个uint_32t。
地址是否自增,作用是指定一个转运完成后,下一次转运,是不是要把地址移动到下一个位置去。比如,ADC扫描模式,用DMA进行数据转运,外设地址是ADC_DR寄存器,寄存器这边,显然地址是不用自增的,如果自增,那下一次转运就跑到别的寄存器了。
存储器,这边的地址就需要自增,每转运一个数据后,就往后挪个坑,要不然下次再转就把上次的覆盖掉了。
传输计数器,用来指定总共需要转运几次的,是一个自减计数器。如给它写5,那DMA就只能转运5次,并不断自减,当计数器减到0时,DMA就不会在进行数据转运了。另外,在减到0之后,之前的自增地址,就会恢复到起始地址的位置,就是5,方便开始新一轮的转换。
自动重装器,作用是传输计数器减到0之后,是否要自动恢复到最初的值。 如最初传输计数器给5,如果不使用自动重装器,那转运5次后,DMA就结束了。如果使用自动重装器,转运5次后,计数器减到0后,就会立即重装到初始值5。
决定了转运的模式,如不重装,就是正常的单次模式,如重装,就是循环模式。如想转运一个数组,那就是单次模式,转运一轮结束。如是ADC扫描模式+连续转换,指定一个工作完成后,是不是立即开始下一轮的工作。
触发源,有硬件触发和软件触发,选择哪个触发由M2M决定的,M2M就是存储器到存储器的。
当M2M给1时,DMA选择软件触发,软件触发,一般是存储器到存储器的转运。
软件触发执行逻辑是,以最快的速度,连续不断地触发DMA,早早的把传输计数器清0,完成这一轮的转换。可以理解成连续触发。
软件触发和循环模式(自动重装),不能同时用。区别是,软件触发是把传输计数器清0,循环模式是清0后自动重装。如同时使用,DMA停不下来。
当M2M给0时,DMA选择硬件触发,硬件触发源,可以选择ADC,串口,定时器等。使用硬件触发的转运,一般于外设转运有关,这些转运需要一定的时机,如ADC转换完成,串口收到数据,定时时间到等。
开关控制,是DMA_Cmd函数,当给DMA使能后,DMA准备就绪,可以进行转运了。
DMA转运的条件,第一开关控制,DMA_Cmd使能,第二传输计数器必须大于0,第三就是触发源,必须有触发信号。
触发一次,转运一次,传输计数器自减一次,当传输计数器等于0,且没有自动重装时。这时无论是否触发,DMA都不会再进行转运了,此时就需要DMA_Cmd,给DISABLE,关闭DMA,再为传输计数器写入一个大于0的数,再DMA_Cmd,给ENABLE,开启DMA,DMA才能继续工作。
注意一下,写传输计数器时,必须要先关闭DMA,再进行,不能在DMA开启时,写传输计数器,这是手册里的规定。
DMA1请求映像
是上图中,触发源部分
那然后继续看左边的硬件触发源,这里外设请求信号,可以看到,每个通道的硬件触发都是不同的
如果你需要用ADC1来触发,那就必须选择通道1,如果需要定时器2的更新事件触发的话,那就必须选择通道2,而如果使用软件触发的话,那通道就可任意选择了,因为每个通道的软件触发都是一样的。
这里可以这样理解:EN并不是数据选择器的控制位,而且决定这个数据选择要不要工作,EN=0.数据选择器不工作,EN1,数据选择器工作;然后软件触发后面跟个M2M位的意思应该是当M2M位=1时,选择软件触发。
之后,这7个触发源,进入到仲裁器,进行优先级判断,最終产生内部的DMA1请求。这个优先级的判断,类似于中新的优先级,默认优先级是通道号越小,优先级越高,当然也可以在程序中配置优先级。
数据宽度与对齐
这个表的意思,就是如果你把小的数据转到大的里面去,高位就会补0;如果把大的数据转到小的里面去高位,就会舍弃掉;如果数据宽度一样,那就没事。
数据转运+DMA
这2个例子与程序对应。
将SRAM里的数组DataA,转运到另一个数组DataB中,我们看一下这种情况下,这个基本结构里的各个参数该如何配置。
- 1.首先是外设站点和存储器站点的起始地址、数据宽度、地址是否自增这三个参数。那在这个任务里,外设地址显然应该填DataA数组的首地址,存储器地址,给DataB数组的首地址,然后数据宽度,两个数组的类型都是uint8_t,所以数据宽度都是按8位的字节传输。之后地址是否自增,在中间可以看到,我们想要的效果是DataA[0]转到DataB[0],DataA[1]转到DataB[1],等等。所以转运完DataA[0]和DataB[0]之后,两个站点的地址都应该自增,都移动到下一个数据的位置,继续转运DataA[1]和DataB[1],这样来进行。
如果左边不自增,右边自增,效果是:转运完成后,DataB的所有数据,都会等于DataA[0]。
- 2.之后,这里的方向参数,那显然就是外设站点转运到存储器站点了,当然如果你想把DataB的数据转运到DataA,那可以把方向参数换过来,这样就是方向转运了。
- 3.然后是传输计数器和是否要自动重装,在这里,显然要转运7次,所以传输计数器给7,自动重装暂时不需要,之后触发选择部分,这里,我们要使用软件触发。因为这是存储器到存储器的数据转运,是不需要等待硬件时机的,尽快转运完成就行了。
- 4.那最后,调用DMA_Cmd,给DMA使能,这样数据就会从DataA转运到DataB了。转运7次之后,传输计数器自减到0,DMA停止,转运完成。这里的数据转运是一种复制转运,转运完成后DataA的数据并不会消失,这个过程相当于是把DataA的数据复制到了DataB的位置。
ADC扫描模式+DMA
左边是ADC扫描模式的执行流程,在这里有7个通道,触发一次后,7个通道依次进行AD转换,然后转换结果都放到ADC_DR数据寄存器里面。那我们要做的就是,在每个单独的通道转换完成后,进行一个DMA数据转运,并且目的地址进行自增,这样数据就不会被覆盖了。所以在这里DMA的配置就是,外设地址写入ADC_DR这个寄存器的地址;存储器的地址,可以在SRAM中定义一个数组ADValue,然后把ADValue的地址当做存储器的地址。
之后数据宽度,因为ADC_DR和SRAM数组,我们要的都是uint16_t的数据,所以数据宽度都是16位的半字传输。
接着判断地址是否自增,那从这个图里,显然是外设地址不自增,存储器地址自增。
传输方向,是外设站点到存储器站点。
传输计数器,这里通道有7个,所以计数7次。
计数器是否自动重装,这里可以看ADC的配置,ADC如果是单次扫描,那DMA的传输计数器可以不自动重装,转换一轮就停止,如果ADC是连续扫描,那DMA就可以使用自动重装,在ADC启动下一轮转换的时候,DMA也启动下一轮的转运,ADC和DMA同步工作。
最后是触发选择,这里ADC_DR的值是在ADC单个通道转换完成后才会有效,所以DMA转运的时机,需要和ADC单个通道转换完成同步,所以DMA的触发要选择ADC的硬件触发。
最后硬件触发这里要说明一下,我们上一节说了,ADC扫描模式,在每个单独的通道转换完成后,没有任何标志位,也不会触发中断。所以我们程序不太好判断,某一个通道转换完成的时机是什么时候。虽然单个通道转换完成后,不产生任何标志位和中断,但是它应该会产生DMA请求,去触发DMA转运,这部分内容,手册里并没有详细描述,根据我实际实验,单个通道的DMA请求肯定是有的。
这些就是ADC扫描模式和DMA配合使用的流程。一般来说,DMA最常见的用途就是配合ADC的扫描模式,因为ADC扫描模式有个数据覆盖的特征,这个缺陷使ADC和DMA成为了最常见的伙伴。