DMA简介
- DMA(Direct Memory Access)直接存储器存取,主要是用来协助CPU,完成数据转运的工作,可以直接访闻STM32内部的存储器的,包括运行内存SRAM、程序存储器Flash和寄存器等等,DMA都有权限访问它们。
- DMA可以提供外设(外设寄存器,数据寄存器DR,Data Register)和存储器(运行内存SRAM和程序存储器Flash)或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
- 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)。这个通道就是数据转运的路径,从一个地方移动到另一个地方,就需要占用一个通道。如果有多个通道进行转运,那它们之间可以各转各的,互不干扰.
- 每个通道都支持软件触发和特定的硬件触发。 如果把flash里的一批数据转运到SRAM里去,需要软件触发。使用软件触发之后,DMA就会一股脑的把这批数据以最快的速度全部转运完成。 如果DMA进行的是外设到存储器的数据转运,就不能一股脑的转运了。因为外设的数据是有一定时机的,所以这时就需要用硬件触发,比如转运ADC的数据,那就得ADC每个通道AD转换完成后,硬件触发一次DMA之后,DMA再转运。触发一次转运一次。这样数据才是正确的才是我们想要的效果。 总的来说,存储器到存储器的数据转运,我们一般使用软件触发。外设到存储器的数据转运,我们一般使用硬件触发。 特定的硬件触发,意思就是每个DMA的通道,它的硬件触发源是不一样的,要使用某个外设的硬件触发源就得使用它连接的那个通道,而不能任意选择通道
- STM32F103C8T6 DMA资源:DMA1(7个 通道)
存储器映象
计算机系统的5大组成部分是运算器、控制器、存储器、输入设备和输出设备
其中运算器和控制器一般会合在一起,叫做cpu,所以计算机的核心关键部分就是cpu和存储器。存储器又有两个重要知识点,一个是存储器的内容,另一个就是存储器的地址。
在STM32的数据手册(P21,图9 存储器图 ),这里也会有个存储器映像的图。
无论是Flash还是SRAM还是外设寄存器,它们都是存储器的一种,实际上也是存储器,外设到存储器,存储器到存储器,本质上其实都是存储器之间的数据转运,说成外设的存储器,只不过是STM32,它特别指定了可以转运外设的存储器而已。
类型 | 起始地址 | 存储器 | 用途 |
ROM(只读存储器,一种非易失性、掉电不丢失的存储器) | 0x0800 0000 | 程序存储器Flash | 存储C语言编译后的程序代码 |
0x1FFF F000 | 系统存储器 | 存储BootLoader,用于串口下载 | |
0x1FFF F800 | 选项字节 | 存储一些独立于程序代码的配置参数(Flash的读保护写保护,看门狗配置) | |
RAM(随机存储器,一种易失性、掉电丢失的存储器) | 0x2000 0000 | 运行内存SRAM | 存储运行过程中的临时变量(定义变量、数组、结构体) |
0x4000 0000 | 外设寄存器 | 存储各个外设的配置参数 | |
0xE000 0000 | 内核外设寄存器 | 存储内核各个外设的配置参数(NVIC和sysTick) |
DMA框图
左上角是Cortex-M3内核,里面包含了CPU和内核外设等等,剩下的这所有东西,都可以把它看成是存储器,所以总共就是cpu和存储器两个东西。Flash是主闪存,SRAM是运行内存,各个外设都可以看成是寄存器,也是一种SRAM存储器啊。
寄存器是一种特殊的存储器,一方面cpu可以对寄存器进行读写,就像读写运行内存一样。另一方面寄存器的每一位背后都连接了一根导线,这些导线可以用于控制外设电路的状态,比如自引脚的高低电平,导通和断开开关,切换数据选择器或者多位结合起来,当做计数器,数据寄存器等等等等,所以寄存器是连接软件和硬件的桥梁,软件读写寄存器就相当于在控制硬件的执行
既然外设就是寄存器,寄存器就是存储器。那使用DMA进行数据转运就都可以归为一类问题了,就是从某个地址取内容,再放到另一个地址去。为了高效有条理的访问存储器,设计了一个总线矩阵,总线矩阵的左端是主动单元,也就是拥有存储器的访问权,右边这些是被动单元,它们的存储器只能被左边的主动单元读写,主动单元这里内核有Dcode和系统总线,可以访问右边的存储器。其中Dcode总线是专门访问Flash的,系统总线是访问其他东西的,另外由于DMA要转移数据,所以DMA也必须要有访问的主动权。主动单元除了内核CPU,剩下的就是DMA总线了,这里DMA1有一条DMA总线,DMA2也有一条DMA总线,下面还有一条DMA总线。
在DMA1和DMA2里面可以看到,DMA1有七个通道,DMA2有5个通道,各个通道可以分别设置它们转移数据的原地址和目的地址,接着下面这里有个仲裁器,因为虽然多个通道可以独立转运数据,但是最终DMA总线只有一条,所以所有的通道都只能分时复用这一条DMA总线。
如果产生了冲突,那就会由仲裁器根据通道的优先级决定谁先用谁后用,另外在总线矩阵这里也会有个仲裁器,如果DMA和CPU都要访问同一个目标,那么DMA就会暂停CPU的访问,以防止冲。不过总线仲裁器,仍然会保证CPU得到一半的总线带宽,使CPU也能正常的工作,这就是仲裁器的作用。
AHB从设备,也就是DMA自身的寄存器,因为DMA作为一个外设,它自己也会有相应的配置寄存器,这里连接在了总线右边的AHB总线上,所以DMA既是总线矩阵的主动单元,可以读写各种存储器,也是AHB总线上的被动单元,CPU通过这一条线路就可以对DMA进行配置了。
DMA请求就是触发的意思,这条线路右边的触发源是各个外设,所以这个DMA请求就是DMA的硬件触发源,需要触发DMA转运数据的时候,就会通过这条线路向DMA发出硬件触发信号之后,DMA就可以执行数据转运的工作了,这就是DMA请求的作用。
DMA结构包括,用于访问各个存储器的DMA总线、内部的多个通道,可以进行独立的数据转运、仲裁器,用于调度各个通道,防止产生冲突,AHB从设备,用于配置DMA参数、DMA请求,用于硬件触发DMA的数据转运。
Flash它是ROM只读存储器的一种,如果通过总线直接访问的话,无论是CPU还是DMA都是只读的只能读取数据而不能写入,如果你DMA的目的地址填写了Flash的区域,那转运时就会出错。当然Flash也不是绝对的不可写入,我们可以配置这个Flash接口控制器,对Flash进行写入,这个流程就比较麻烦了,要先对Flash按页进行擦除,再写入数据。SRAM是运行内存可以任意读写。外设寄存器的话得看参考手册里面的描述,有的寄存器是只读的,有的寄存器是只写的,不过我们主要用的是数据寄存器,数据寄存器都是可以正常读写的
DMA基本结构
数据转运的两大站点:外设寄存器站点,存储器站点(FLASH,SRAM)
外设寄存器站点,存储器站点(FLASH,SRAM)
在STM32手册里,所说的存储器啊,一般是特指Flash和SRAM,不包含外设寄存器。外设寄存器它一般直接称作外设,所以就是外设到存储器,存储器的存储器这样来描述。寄存器也是存储器的一种,但是STM32还是使用了外设和存储器来作为区分。在这里可以看到DMA的数据转运可以是从外设到存储器,也是可以从存储器到外设,具体是向左还是向右,有一个方向的参数可以进行控制,另外还有一种转运方式,就是存储器到存储器,比如Flash到SRAM或者SRAM到SRAM,这两种方式。由于Flash是只读的,所以DMA不可以进行SRAM到Flash或者Flash到Flash的转移操作 继续看这两边的参数,既然要进行数据转运,那肯定就要指定从哪里转到哪里,具体怎么转呢?所以外设和存储器两个站点就都有3个参数 第一个是起始地址,有外设端的起始地址和存储器端的起始地址,这两个参数决定了数据是从哪里来到哪里去的
第2个参数是数据宽度,这个参数的作用是指定一次转运要按多大的数据宽度来进行,他可以选择字节byte、半字节halfword和字word字节就是8位,也就是一次转运一个uint8_t的数据;半字是16位,就是一次转运一个uint16_t;字是32位,就是一次转运unit32_t的数据,比如转运ADC的数据,ADC的结果是uint16_t大小的数据,所以这个参数就要选择半字一次转运一个uint16_t,这样才对。
第3个参数是地址是否自增,这个参数的作用是指定一次转移完成后,下一次转运是不是要把地址移动到下一个位置去,这就相当于是指针p++的意思,比如[[ADC]]扫描模式,用DMA进行数据转运,外设地址是ADC_DR寄存器,寄存器这边,显然地址是不用自增的,如果自增那下一次转运就跑到别的寄存器哪里去了,存储器这边,地址就需要自增,每转运一个数据后就往后挪个坑,要不然下次再转就把上次的覆盖掉了,这就是地址是否自增的作用,就是指定是不是要转运一次挪个坑,这个意思。这就是外设站点和存储器站点各自的3个参数了。
如果要进行存储器到存储器的数据转运,那我们就需要把其中一个存储器的地址放在外设的这个站点,这样就能进行存储器和存储器的转运了,只要你在外设起始地址里写Flash或者SRAM的地址,那他就会去Flash或SRAM找数据,这个站点虽然叫外设存储器,但是它就只是个名字而已,并不是说这个地址只能写寄存器的地址,如果写Flash的地址,那他就会去Flash里找,写SRAM,他就会去SRAM里找,这个没有限制,甚至你可以在外设站点写存储器的地址,存储器站点写外设的地址,然后方向参数给反过来,这样也是可以的,你也可以把它叫做站点a、站点b,从a到b或者从b到a转移数据,不必拘泥于他写的外设站点、存储器站点这个名字的。
传输计数器
这个东西就是用来指定我总共需要转运几次的,这个传输计数器是一个自减计数器,比如你给他写个5,那DMA就只能进行5次数据转运,转运过程中每转运一次机由器的数就会减一,当传输计数器减到零之后,DMA就不会再进行数据转运了,另外它减到零之后之前自增的地址也会恢复到起始地址的位置,以方便之后DMA开始新一轮的转换,在传输计数器的右边有一个自动重装器,这个自动重装器的作用就是传输计数器减到零之后,是否要自动恢复到最初的值,比如最初传输计数器给5,如果不使用自动重装器,那转运5次后DAM就结束了,如果使用自动重装器,那转运5次计数器减到零后就会立即重装到初始值5,这个就是自动重装器,它决定了转运的模式,如果不重装就是正常的单次模式,如果重装就是循环模式,比如如果你想转运一个数组,那一般就是单次模式转运一轮就结束了,如果是[[ADC]]扫描模式加连续转换,那为了,配合ADC DMA也需要使用循环模式,所以这个循环模式和[[ADC]]的连续模式差不多,都是指定一轮工作完成后,是不是立即开始下一轮工作,
DMA的触发控制
触发就是决定DMA需要在什么时机进行转运的。触发源有硬件触发和软件触发,具体选择哪个,由M2M这个参数决定,M2M就是memory to memory,所以M2M就是MtoM存储器到存储器的意思, M2M置1时,DMA就会选择软件触发,这个软件触发并不是调用某个函数一次,触发一次。软件触发的执行逻辑是,以最快的速度,连续不断地触发DMA,就是应用在存储器到存储器转运的情况。 当M2M位给0,那就是使用硬件触发了,硬件触发源可以选择ADC、串口、TIM等等。使用硬件触发的转运,一般都是与外设有关的转运。这些转运需要一定的时机。比如ADC转换完成、串口收到数据、定时时间到等等,这些转运需要一定的时机,比如ADC转换完成、串口收到数据、定时时间到等等,所以需要使用硬件触发,在硬件达到这些时机时,传个信号过来来触发DMA进行转运,这就是硬件触发
开关控制
即DMA_Cmd
函数,当给DMA使能后,DMA就准备就绪,可以进行转运了,DMA进行转运有几个条件 第一就是开关控制DMA_Cmd
必须使能 第2就是传输计数器必须大于0 第3就是触发源必须有触发信号 触发一次转运一次,传输计数器自检一次,当传输计数器等于零,且没有自动重装时,这时无论是否触发DMA都不会再进行转运了,此时就需要DMA_Cmd
给Disable关闭DMA,在为传输计数器写一个大于0的数,再DMA_Cmd
给Enable开启DMA,DMA才能继续工作,注意一下,写传输计数器时,必须要先关闭DMA,再进行,不能在DMA开启时,写传输计数器,这是手册里的规定
DMA请求
这张图表示的就是DMA触发的部分,我们来看一下这张图是DMA1的请求映象,下面是DMA的七个通道,每个通道都有一个数据选择器,可以选择硬件触发或软件触发,这里他这样画的意思应该是,EN并不是数据选择器的控制位啊,而是决定这个数据选择器要不要工作 EN等于0,数据选择器不工作 EN等于1,数据选择器工作 然后软件触发后面跟个M2M位的意思应该是 当M2M位等于1时,选择软件触发
继续看左边的硬件触发源,这里是外设请求信号啊,可以看到每个通道的硬件触发源都是不同的 如果你需要用ADC1来触发的话,那就必须选择通道1 如果需要TIM2的更新事件来触发的话,那就必须选择通道2 剩下的也是同理,因为每个通道的硬件触发源都不同,所以如果想使用某个硬件触发源的话,就必须使用它所在的通道,这就是硬件触发的注意事项 而如果使用软件触发的话,那通道就可以任意选择了,因为每个通道的软件触发都是一样的,所以在ppt的在前面写的是每个通道都支持软件触发和特定的硬件触发,这就是特定的意思,选择硬件触发是要看通道的 这里通道一的硬件触发是ADC1 ,TIM2的通道3和TIM4的通道1,那到底是选择哪个触发源呢?这个是对应的外设是否开启了DMA输出来决定的,比如你要使用ADC1,那,会有个库函数叫ADCDMACmd,必须使用这个库函数开启ADC1的这一路输出,才有效,如果想选择TIM2的通道3,那也会有个TIMDMACmd函数,用来进行DMA输出控制。所以这3个触发源具体使用哪个,取决于你把哪个外设的DMA输出开启了,如果3个都开启了,那这边是一个或门,理论上3个硬件都可以进行触发,不过一般情况下我们都是开启其中一个,这就是这部分电路。
之后,这七个触发源进入到仲裁器进行优先级判断,最终产生内部的DMA1请求,这个优先级的判断 ,类似于中断的优先级,默认优先级是通道号越小,优先级越高,当然也可以在程序中配置优先级,这个其实影响并不是很大。
数据宽度与对齐
可以看一下数据转移的两个站点,都有一个数据宽度的参数
如果数据宽度都一样,那就是正常的一个个转运,如果数据宽度不一样,那会怎么处理呢,这个表就是来说明这个问题的
第一列是源端宽度
第二列是目标宽度
第三列是传输数目
当源端和目标都是8位时,转运第一步,在源端的零位置读数据b0。在目标的零位置写数据b0。就是把这个b0从左边挪到右边,之后的步骤,就是把b1、b2、b3依次从左边挪到右边,这是源端和目标都是8位的情况
当源端是8位,目标是16位时,那操作就是在源端读b0,在目标写00b0,之后读b1写00b1等等,这个意思就是如果你目标的数据宽度比源端的数据宽度大,那就在目标数据前面多出来的空位补0,
之后8位转移到32位也是一样的处理,前面空出来的都补零,
当目标数据宽度<源端数据宽度,比如由16位转到8位去,现象就是读b1 b0 只写入b0,读b3 b2 只写入b2 。也就是把多出来的高位舍弃掉,之后的各种情况也都是类似的操作,
总之这个表的意思就是,如果你把小的数据转到大的里面去,高位就会补零。如果把大的数据转到小的里面去,高位就会舍弃掉,如果数据宽度一样,那就没事,这个操作我们也可以想象的到,跟unit8_t、uint16_t和unit32_t变量之间相互赋值一样,不够就补零,超了就舍弃高位,这是一个道理。
例子1:数据转运+DMA
这个例子的任务是将SRAM里的数组DataA转运到另一个数组DataB中,这种情况下,这个基本结构里的各个参数该如何配置。
首先是外设站点和存储器,站点的起始地址,数据宽度地址是否自增这三个参数
外设地址应该填DataA数组的首地址
存储器地址给DataB数据的首地址
然后数据宽度两个数组的类型都是unit8_t,所以数据宽度都是按8位的字节传输,
地址是否自增,在中间可以看到,我们想要的效果是DataA[0]转到DataB[0],DataA[1]转到DataB[1]等等,两个数组的位置一一对应啊,所以转运完DataA[0]和DataB[0]之后,两个站点的地址都应该指针都移动到下一个数据的位置,继续转运DataA[1]和DataB[1]这样来进行。
如果左边不自增,右边自增,转运完成后,DataB的所有数据都会等于DataA[0]。
如果左边自增,右边不自增,转运完成后,DataB[0]等于DataA的最后一个数。
如果左右都不自增,那就一直是DataA[0]转到DataB[0],其他的数据不变。 这就是地址是否自增的效果
方向参数,那显然就是外设站点转运的存储机站点了,如果把DataB的数据转运到DataA,那可以把方向参数换过来,这样就是反向转运了
传输计数器和是否要自动重装,显然要转运七次,所以传输寄存器给7,自动重装,暂时不需要,
触发选择部分,这里要使用软件触发,因为这是存储器到存储器的数据转运是不需要等待硬件时机的,尽快转移完成就行了
调用DMA_Cmd,给DMA使能 这样数据就会从DataA转移到DataB了,转运七次之后,传输计数器自减到零,DMA停止转运完成,这里的数据转运是一种复制转运,转运完成后,DataA的数据并不会消失,这个过程相当于是把DataA的数据复制到了DataB的位置,这就是第一个任务啊,存储器到存储器的数据转运
例子:ADC扫描模式+DMA
左边是ADC扫描模式的执行流程,在这里有七个通道触发一次后,七个通道依次进行AD转换 然后转换结果都放到ADC_DR数据寄存器里面,那我们要做的就是在每个单独的通道转换完成后,进行一次DMA数据转运,并且目的地址进行自增,这样数据就不会被覆盖了,所以在这里DMA的配置就是
外设地址写入ADC_DR这个寄存器的地址,
存储器的地址可以在SRAM中定义一个数组ADvalue[],然后把ADValue[]的地址当作存储器的地址之后
数据宽度,因为ADC_DR和SRAM数组我们要的都是应用的uint16_tt的数据,所以数据宽度都是16位的半字传输
地址是否自增,从这个图里,显然是外设地址不自增,存储器地址自增。
传输方向是外设站点的存储器站点
传输计数器,这里通道有七个,所以计数七次,
计数器是否自动重装,这里可以看ADC的配置,ADC如果是单次扫描,转换一轮就停止。如果ADC是连续扫描,那DMA就可以使用自动重装,在ADC启动下一轮转换的时候,DMA也启动下一轮的转运,ADC和DMA同步工作。
触发选择,这里ADC_DR的值是在ADC单个通道转换完成后才会有效,所以DMA转运的时机需要和ADC单个通道转换完成同步,所以DMA的触发要选择ADC的硬件触发,
最后硬件触发这里要说明一下,ADC扫描模式在每个单独的通道转换完成后,没有任何标志位,也不会触发中断,所以我们程序不太好判断某一个通道转换完成的时机是什么时候,但是根据研究,虽然单个通道转换完成后不产生任何标志位和中断,但是他应该会产生DMA请求去触发DMA转运,这部分内容手册里并没有详细描述,根据实际实验啊,单个通道的DMA请求肯定是有的,要不然这个实验就做不成了。
数据手册的原话:因为规则通道转换的值储存在一个仅有的数据寄存器中,所以当转换多个规则通道时需要使用 DMA,这可以避免丢失已经存储在ADC_DR寄存器中的数据。
原话2:只有在规则通道的转换结束时才产生DMA请求,并将转换的数据从ADC_DR寄存器传输到用户 指定的目的地址。
这些就是ADC扫描模式和DMA配合使用的流程,一般来说DMA最常见的用途就是配合ADC的扫描模式,因为ADC扫描模式有个数据覆盖的特征,或者可以说这个数据覆盖的问题是ADC固有的缺陷哈,这个缺陷是ADC和DMA成了最常见的伙伴,ADC对DMA的需求是非常强烈的,像其他的一些外设使用DMA可以提高效率,是锦上添花的操作,但是不使用也是可以的,最多是损失一些性能。
但是这个ADC的扫描模式如果不使用DMA功能都会受到很大的限制,所以ADC和DMA的结合最为常见。
参考手册
第十章,DMA控制器
第二章,存储器和总线架构,存储器映射的内容,DMA的简介和一些主要特性,结构框图,功能描述
这里之后这里还有一个位段区域,这两个位段区映射的外设计容器和SRAM中全部的位,这个位段区就相当于是位寻址,它把外设寄存器和SRAM中所有的位都分配了地址,你操作这个新的地址,就相当于操作其中某一个位,因为32位的地址有99%都是空的,所以地址空间很充足,即使把每一位都单独编码,那毫无压力,所以就存在了这样一个位段,用于单独操作寄存器或SRAM的某一位,位段区是另找了一个地方,开辟了一段地址区域。
还其中SRAM位段区域是2200开头的区域
外设寄存器的位段区是4200开头的区域
嵌入式闪存,闪存被分为了很多页,他们的地址都是0800开头的,在闪存区的最后就是系统存储器和选项字节,这两个区域统称为信息块,这是外设的一部分。闪存接口寄存器,他的地址是40开头的,所以它显然是一个外设,这个外设可以对闪存进行读写,这就是闪存的部分。
启动配置,配置boot0和boot1两个硬件来选择程序从哪里启动
点击数:12