1.DMA简介
- DMA(Direct Memory Access),即“直接存储器存取/访问”,可以提供外设和存储器之间,或存储器和存储器之间直接的高速数据传输,无需CPU干预,节省了CPU资源。这里的外设指外设寄存器,一般指外设的数据寄存器Data Register——DR。存储器指运行内存SRAM和程序存储器Flash,是存储变量、数组和程序代码的地方。需要注意的是,原理上DMA执行存储器到存储器的数据转运,只不过STM32特殊制定了可以将外设的存储器看作普通的存储器进行数据转运。(外设指的是,外设的数据寄存器DR,比如ADC的数据寄存器,串口的数据寄存器等)这个传输过程无需CPU干预,节省了CPU资源。 这里可以理解为CPU将搬运数据这种“杂活”外包给一个小秘书,CPU剩下时间和空间,用来完成其他更复杂和专业的操作。
- DMA可以直接访问STM32内部存储器的,直接存储器存取,包括运行内存SRAM,程序存储器Flash和寄存器等等,DMA都有权限访问他们,所以DMA才能完成数据转运的工作。
- STM32的DMA有12个可以独立配置的通道,通道就是数据转运的路径,从一个地方到另一个地方就可以看作一个通道。其中DMA1有7个通道,DMA2有5个通道。STM32F103C8T6的DMA资源:DMA1(7个通道)。
- 每一个通道都支持软件触发和特定的硬件触发。 软件触发:DMA执行从存储器到存储器的数据转运,例如把Flash中的一批数据转运到SRAM中去,这时DMA会把要转运的数据以最快的速度全部转运完成。 特定的硬件触发:DMA执行外设和存储器之间的数据传输,由于外设中需要转运的数据存在一定的时机,所以就需要用到硬件触发转运。例如:在ADC工作时,需要在ADC每个通道AD转换完成后,硬件触发依次DMA,DMA才会执行转运。“特定”指每个DMA的通道的硬件触发源都不同。
2.STM32存储器组织与映像
- (计算机系统的5大组成部分:运算器,控制器,存储器,输入设备,输出设备。) 在这个表里,无论是Flash,还是SRAM,还是外设寄存器,它们都是存储器的一种,包括外设寄存器,实际上也是存储器。在DMA简介中,我们说的是外设到存储器,存储器到存储器,本质上其实都是存储器之间的数据转运,说成外设到存储器,只不过是STM32他特别指定了可以转运外设的存储器而已。
- ROM 只读存储器:是一种非易失性,掉电不丢失的存储器。
- 程序存储器Flash:即主闪存,用途是存储C语言编译后的程序指令码,即下载程序的位置。运行程序一般都是从主闪存Flash开始的。
- 系统存储器:用来存储Bootloader程序,用于串口下载。该程序是芯片出厂自动写入的,一般不允许用户修改,它和选项字节一同存储在ROM的最后面。存储介质同样是Flash。
- 选项字节:用来存储一些独立于代码的配置参数,例如Flash的读保护和写保护、看门狗配置等。它和系统存储器一起存储在ROM区的最后面。下载程序后可以不刷新选项字节的内容,这样选项字节的内容就会保持不变。
- RAM 随机存储器:是一种易失性,掉电丢失的存储器。
- 运行内存SRAM(静态随机存取储存器,Static RAM):存储程序运行过程中定义的临时变量,如变量、数组、结构体。
- 外设存储器:存储各个外设的配置参数。初始化外设相当于对其中的数据进行读写。外设存储器也是存储器的一种,它的存储介质同样是SRAM。
- 内核外设存储器:存储内核各个外设的配置参数。内核外设指NVIC和Systic。由于内核外设和其他外设在设计时不是一个厂家设计的,所以他们的存储地址不是连续的。
3.stm32的整体架构
首先,上图展现了STM32的Cortex-M3内核,可以将除该内核以外的其他部分都看作存储器。 DMA的主要结构分为:
- DMA总线
- 数据转运通道
- 仲裁器
- AHB从设备
- DMA请求
-
左上角这里是Cortex-M3内核,里面包含了CPU和内核外设等等,剩下的这所有东西,你都可以把它看成是存储器,所以总共就是CPU和存储器两个东西。Flash是主闪存,SRAM是运行内存,各个外设,都可以看成是寄存器,也是一种SRAM
-
寄存器是一种特殊的存储器,一方面,CPU可以对寄存器进行读写,就像读写运行内存一样,另一方面,寄存器的每一位背后,都连接了一根导线,这些导线可以用于控制外设电路的状态,比如置引脚的高低电平、导通和断开开关、切换数据选择器,或者多位组合起来,当做计数器、数据寄存器等等。所以,寄存器是连接软件和硬件的桥梁。软件读写寄存器,就相当于在控制硬件的执行。
- 回到这里,既然外设就是寄存器,寄存器就是存储器,那使用DMA进行数据转运,就都可以归为一类问题了。就是从某个地址取内容,再放到另一个地址去。
-
如上图所示,为了高效访问各类存储器,STM32存在一个总线矩阵,它的左侧是主动单元(拥有总线矩阵控制权的单元,图中所示为Cortex-M3内核,DMA,以太网MAC模块),右侧是被动单元(只能被读写的存储器单元)。
-
上图同样可以看到STM32中的两个DMA内部的部分结构。其中DMA1有7个通道,DMA2有5个通道,每个通道都可以独立配置数据转运的源地址和目的地址。DMA拥有多个转运通道,可以独立转运数据,但是每个DMA与外部连接的DMA总线却只有一条,这就要求所有通道要分时复用这一条DMA总线,当多个DMA通道转运数据是产生了冲突,就需要由仲裁器按照DMA通道的优先级来决定各个通道使用DMA总线的次序(总线矩阵中同样有一个仲裁器,如果DMA和CPU都要访问同一个目标,那么DMA就会通过总线仲裁器暂停CPU的访问以防止冲突,同时给CPU留有一般的带宽保证CPU可以正常工作)。
- DMA内部的“AHB从设备”(DMA自身的寄存器),然后CPU通过AHB总线对DMA内部的配置寄存器进行读写操作,从而完成对DMA的配置。所以DMA既是总线矩阵的主动单元,可以读写存储器,又是AHB总线的被动单元。 图中DMA右侧的“DMA请求”指DMA的硬件触发源。当ADC转换完成、串口接收到数据、定时器中断触发后,就会通过“DMA请求”线路发出硬件触发信号,请求DMA转运数据。
Flash是一种ROM,即只读存储器。无论是CPU还是DMA都不能对Flash进行写操作,只能对Flash进行读操作。如果DMA的目的地址填写了Flash区域的内存,在数据转运时就会出错。但Flash也不是绝对的不可以写入,可以通过配置“Flash接口控制器”对Flash进行写操作,需要对Flash按页进行擦除,之后再写入数据,比较麻烦。不过这部分内容属于另一个课题,这里仅作简单了解即可。
4.DMA基本结构图
-
DMA进行数据转运时,首先应该定义两个站点。在数据手册中,ST公司将两个站点分别描述为“外设寄存器”和“存储器”,但实际上两个站点并不一定要写入外设寄存器的地址和存储器的地址,所以可以简单将两个站点理解为站点A和站点B。数据是从A传输到B,还是从B传输到A,由两个站点之间的“方向”参数确定。每一个站点拥有三个参数,他们分别是:
- 起始地址:存储将要传输数据的地址。需要注意,这里的“起始”地址并不一定指被传输数据的地址,它同样可以是数据被送往的目的地址。
- 数据宽度:一次被传输的数据宽度,它可以是字节Byte(8位)、半字HalfWord(16位)、字Word(32位)。(STM32的字长为32位)
- 地址是否自增:完成一次数据传输后地址指针是否自增。类似指针的P++
-
DMA内部还拥有传输计数器和自动重装器两个寄存器。
- 传输计数器是用来指定传输次数的寄存器,它是一个自减计数器,当它的值为0时,DMA就不会进行数据转运了,由于传输时自增后的地址也会恢复到起始地址的位置,方便下一次的数据转运。
- 自动重装器的作用是:当传输计数器自减到0后,可以配置自动重装器来确定是否自动使传输计数器回到初始定义的值,它决定了DMA转运数据的模式是单次模式或者循环模式(完成一次工作以后,是否立即开始下一次工作)。如果想把一个数组的数据转运到另一个数组,一般就要使用单次模式;如果要在ADC扫描模式并且连续转换时使用DMA转运数据,就要配合ADC使用循环模式。
-
DMA的触发控制由M2M(Memory to Memory)位进行控制。
- 当M2M为1时,执行软件触发;需要特别注意的是,这里的软件触发与之前ADC、外部中断等的软件触发的定义不同,这里的软件触发的逻辑是:以最快的速度,连续不断地连续触发DMA,直到传输计数器为0为止,即连续触发。很显然,软件触发和DMA的循环模式不能同时使用,否则DMA将无法停止工作。软件触发一般完成从存储器到存储器的数据转运(存储器到存储器的转运是软件启动,不需要时机)
- 当M2M为0时,执行特定通道的硬件触发。于此对应,硬件触发一般完成从外设到存储器的数据转运原因是该种转运时需要转运的数据时机来临,但是在编程时程序员并不知道这个时机具体何时到来,例如ADC完成一次转换,串口USART收到数据,定时器达到一定的时间等。(触发源ADC,串口,定时器等等)
-
DMA执行数据转运时,必须满足以下三个条件:
- DMA_Cmd()函数必须设置为使能ENABLE;
- 传输计数器必须大于0;
- 一定要有触发信号。
当传输计数器自减到0后,并且在单次模式下(自动重装器不会自动重装),无论是否触发,DMA都不会工作。此时如果要重新开启DMA,就需要在DMA_Cmd函数中配置DISABLE,关闭DMA,之后手动修改传输计数器的值,再通过DMA_Cmd函数开启DMA。注意:在对传输计数器进行写操作时必须先关闭DMA,之后再对传输计数器进行写操作,即不能在DMA工作时手动更改传输计数器的值,这是STM32关于DMA使用的规定。
5. DMA使用时的细节问题
1.DMA请求
- 每个触发器都可以配置为软件触发,如上图所示,是否选择软件触发由M2M位控制,当M2M位为1时该通道配置为软件触发,当M2M为0时软件触发通道关闭,可以配置为硬件触发。
- 当需要配置某通道的触发源为硬件触发时,需要注意每个通道可选择的硬件触发源都是固定的,换言之,如果想要选择某个硬件触发源,就必须选择与该触发源相对应的DMA通道。对每个通道而言,具体选择哪个通道,需要由对应的外设是否开启了DMA输出来决定的,并不由DMA决定。如果使用ADC1时进行数据转运,就要在ADC的库函数ADC_DMACmd()中开启DMA1的通道1输出,其他外设同理。
2. 数据宽度与对齐要求
规则:如果低宽度转运到高宽度,在每个目标单位的高位补0;如果高宽度转运到低宽度,源单位的高位数据消失
6.示例
1.数据转运+DMA
将SRAM里的数组DataA,转运到另一个数组DataB中:
- 首先是外设站点和存储器站点的起始地址、数据宽度、地址是否自增这三个参数。那在这个任务里,外设地址显然应该填DataA数组的首地址,存储器地址,给DataB数组的首地址,然后数据宽度,两个数组的类型都是uint8_t,所以数据宽度都是按8位的字节传输。之后地址是否自增,在中间可以看到,我们想要的效果是DataA[0]转到DataB[0],DataA[1]转到DataB[1],等等。所以转运完DataA[0]和DataB[0]之后,两个站点的地址都应该自增,都移动到下一个数据的位置,继续转运DataA[1]和DataB[1],这样来进行。
- 2.之后,这里的方向参数,那显然就是外设站点转运到存储器站点了,当然如果你想把DataB的数据转运到DataA,那可以把方向参数换过来,这样就是方向转运了。
- 3.然后是传输计数器和是否要自动重装,在这里,显然要转运7次,所以传输计数器给7,自动重装暂时不需要,之后触发选择部分,这里,我们要使用软件触发。因为这是存储器到存储器的数据转运,是不需要等待硬件时机的,尽快转运完成就行了。
- 4.那最后,调用DMA_Cmd,给DMA使能,这样数据就会从DataA转运到DataB了。转运7次之后,传输计数器自减到0,DMA停止,转运完成。这里的数据转运是一种复制转运,转运完成后DataA的数据并不会消失,这个过程相当于是把DataA的数据复制到了DataB的位置。
2.ADC扫描模式+DMA(最常见)
-
左边是ADC扫描模式的执行流程,在这里有7个通道,触发一次后,7个通道依次进行AD转换,然后转换结果都放到ADC_DR数据寄存器里面。那我们要做的就是,在每个单独的通道转换完成后,进行一个DMA数据转运,并且目的地址进行自增,这样数据就不会被覆盖了。所以在这里DMA的配置就是,外设地址写入ADC_DR这个寄存器的地址;存储器的地址,可以在SRAM中定义一个数组ADValue,然后把ADValue的地址当做存储器的地址。
-
这些就是ADC扫描模式和DMA配合使用的流程。一般来说,DMA最常见的用途就是配合ADC的扫描模式,因为ADC扫描模式有个数据覆盖的特征,这个缺陷使ADC和DMA成为了最常见的伙伴。