STM32 DMA直接存储器存取

单片机学习!


前言

        DMA是一个转运数据小助手,它主要是用来协助CPU完成数据转运的工作。本文就介绍了DMA的基础内容。


一、DMA简介

  • DMA(Direct Memory Access)直接存储器存取。
  • DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源。
  • 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)。
  • 每个通道都支持软件触发和特定的硬件触发。
  • STM32F103C8T6 DMA资源:DMA1(7个通道)。

1.1 DMA是什么

        DMA就是直接存储器存取或者叫直接存储器访问。DMA名字的意思就表示,DMA这个外设,是可以直接访问STM32内部的存储器的,包括运行内存SRAM、程序存储器Flash、寄存器等等,DMA都有权限访问它们。正是DMA有直接访问内部存储器的权限,DMA才能完成数据转运的工作。

1.2 DMA作用

        DMA可以提供外设存储器或者存储器存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源。

        外设指的就是外设寄存器。一般是外设的数据寄存器DR(Data Register),比如ADC的数据寄存器、串口的数据寄存器等等。

        存储器指的就是运行内存SRAM和程序存储器Flash,是存储变量数组和程序代码的地方。

        外设和存储器或者存储器和存储器之间进行数据转运,就可以使用DMA来完成。并且在转运的过程中,无需CPU的参与。CPU省下的时间就可以干一些其他的,更加专业的事情。搬运数据这种杂活,交给DMA就行了。

1.3 DMA通道

        STM32的DMA有12个独立可配置的通道,其中DMA1有7个通道,DMA2有5个通道。这里的通道就是数据转运的路径。从一个地方到另一个地方就需要占用一个通道。如果有多个通道进行转运,那它们之间可以各转各的,互不干扰。

1.4 软硬件触发

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

        如果DMA进行的是存储器到存储器的数据转运,比如需要把Flash里的一批数据,转运到SRAM里去,那就需要软件触发。使用软件触发之后,DMA就会一股脑的把这批数据以最快的速度全部转运完成。

        如果DMA进行的是外设到存储器的数据转运,那就不能一股脑的转运了。因为外设的数据是有一定时机的,所以这时就需要用硬件触发。比如转运ADC的数据,就需要ADC每个通道AD转换完成,硬件触发一次DMA之后,DMA再转运,接下来触发一次,转运一次,触发一次,转运一次。这样数据才是正确的、想要的效果。

        特定的硬件触发的意思就是每个DMA的通道,它的硬件触发源是不一样的。需要使用某个外设的硬件触发源,就得使用某个外设对应连接的那个通道,不能任意选择通道。

总结:

  • 存储器到存储器的数据转运,一般使用软件触发。
  • 外设到存储器的数据转运,一般使用硬件触发。

1.5 芯片资源

        STM32F103C8T6 的DMA资源只有DMA1的7个通道,没有DMA2.

二、存储器映像

        既然DMA是在存储器之间进行数据转运的,那就需要了解一下STM32中都有哪些存储器,并且STM32的存储器又是被安排到了哪些地址上。这就是本小节存储器映像的内容。

2.1 存储器

        计算机系统的5大组成部分是:运算器、控制器、存储器、输入设备、输出设备。其中运算器和控制器一般会合在一起,叫做CPU。所以计算机的核心关键部分就是CPU和存储器。

        存储器有两个重要知识点:一个是存储器的内容,另一个是存储器的地址。以下内容着重从这两点入手。

2.2 STM32存储器

        上表就是STM32中所有类型的存储器和这些存储器所被安排的地址。STM32的数据手册这里,也有存储器映像的图,上表就是从存储器图中总结。

        在表中,无论是Flash,还是SRAM,还是外设寄存器,他们都是存储器的一种。上文说的“DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输”这里本质上都是存储器之间的数据转运,因为包括外设寄存器,实际上也是存储器。外设到存储器只不过是STM32特别指定了可以转运的存储器而已。


        表中存储器分为两大类:ROMRAM

  • ROM是只读存储器,是一种非易失性,掉电不丢失的存储器。
  • RAM是随机存储器,是一种易失性,掉电丢失的存储器。

        表中ROM分为了3块:程序存储器Flash、系统存储器、选项字节。

        程序存储器Flash,也就是主闪存。它的用途就是,存储C语言编译后的代码程序,也就是下载程序的位置。运行程序一般也是从主闪存里面开始运行的。这一块存储器,STM32给它分配的地址是 0x 0800 0000. 起始地址就是第一个字节的地址是 0x 0800 0000,剩余地址依次增长,每个字节都分配一个独一无二的地址。这就像给每个住户编门牌号一样,只有分配了独一无二的门牌号,程序才能精准地访问这个存储器。最终终止地址是多少取决于存储器的容量。以后调试在软件里看到某个数据的地址是 0x 0800开头的,那就可以确定,它是属于主闪存的数据。

        系统存储器选项字节这两块存储器也是ROM的一种,掉电不丢失,实际上他们的存储介质也是Flash,只不过是一般常说的Flash指的是主闪存Flash,而不指这两块区域。它们俩的地址都是 0x 1FFF 开头的,紧跟着 0x 2000 开头的就是RAM区了。所以可以看出,这两块存储器的位置是在ROM区的最后面。

        系统存储器的用途是存储BootLoader,用于串口下载,BootLoader程序是芯片出厂自动写入的,一般不允许修改。

        选项字节是用来存储一些独立于程序代码的配置参数,它的位置是在ROM区的最后面,下载程序可以不刷新选项字节的内容。这样选项字节的配置就可以保持不变。选项字节里存的主要是Flash的读保护、写保护、看门狗等待的配置。

        表中RAM区也分了3块:运行内存SRAM、外设寄存器、内核外设寄存器。

        运行内存SRAM,分配的地址是 0x 2000 0000,用途是存储运行过程中的临时变量。临时变量也就是在程序中定义变量、数组、结构体的地方。当定义一个变量,再取它的地址显示出来,那这个地址肯定是 0x 2000 开头的。类比于电脑的话,运行内存就是内存条。

        外设寄存器,它的地址是 0x 4000 0000 这块区域。用途是存储各个外设的配置参数。也就是初始化各个外设,最终所读写的东西。外设寄存器也是存储器的一种,它的存储介质其实也是SRAM,只不过一般将运行内存叫做SRAM,外设寄存器就直接叫做寄存器。

        内核外设寄存器,地址是 0x E000 0000 这片区域。用途是存储内核各个外设的配置参数,内核外设就是NVIC和SysTick。内核外设和其他外设的地址是分开的,内核外设是 0x E000 0000,其他外设是 0x 4000 0000。


        再对照数据手册中的图来看,STM32中,所有的存储器都被安排到了 0x 0000 0000 ~ 0x FFFF FFFF 这个地址范围内。因为CPU是32位的,所以寻址范围就是32位的范围,32位的寻址范围非常大,最大可支持4GB容量的存储器,STM32的存储器都是KB级别的,所以4GB的寻址空间有大量的地址都是空的,地址使用率还不到1%,在上图中有灰色填充的就是Reserved区域,意思为保留区域,没有使用到的区域。

        图中灰色0区域的地址,实际上也是没有存储器的,0区域的扩展块 0x 0000 0000 ~ 0x 0800 0000 这个区域里写的是别名到Flash或者系统存储器,取决于BOOT引脚。因为程序是从0地址开始运行的,所以对照图中需要把需要执行的程序映射到0区域的地址来:

  • 如果映射在Flash区,就是从Flash执行;
  • 如果映射在系统存储器区(System memory),就是从系统存储器运行BootLoader;
  • 如果映射到SRAM,就是从SRAM启动。

以上不同方式的选择,由BOOT0和BOOT1两个引脚来决定。

        剩下的 0x 0800 0000 开始的Flash区,用于存储程序代码;

0x 1FFF F000 开始的系统存储器选项字节,在ROM区的最后面,可对照上述表格。

0x 2000 0000 开始的是SRAM

0x 4000 0000 开始的是外设寄存器区,里面内容可以展开,对应图中最右边一列。具体到每个外设,也有外设自己的起始地址。

  • TIM2的地址是 0x 4000 0000 
  • TIM3的地址是 0x 4000 0400 
  • TIM4的地址是 0x 4000 0800 
  • ......

        外设地址里面,又可以具体细分到每个寄存器的地址,寄存器里每个字节的地址,最终所有字节的地址就都可以算出来了。

0x E000 0000 开始的区域,存放的就是内核里面的外设寄存器了。

三、DMA框图


3.1 内核与存储器

        Cortex-M3内核,里面包含了CPU和内核外设等等。框图中除了Cortex-M3内核之外的所有东西,都可以把它看作是存储器。所以整个DMA框图就是CPU和存储器两个东西。

        Flash是主闪存,SRAM是运行内存,各个外设都可以看成是寄存器,也是一种SRAM存储器。


3.2 寄存器

        寄存器是一种特殊的存储器,

一方面CPU可以对寄存器进行读写,就像读写运行内存一样;

另一方面,寄存器的每一位背后,都连接了一根导线,这些导线可以用于控制外设电路的状态。比如:

  • 置引脚的高低电平,
  • 导通和断开开关,
  • 切换数据选择器,
  • 或者多位结合起来当作计数器、数据寄存器等等。

所以寄存器是连接软件和硬件的桥梁。软件读写寄存器就相当于在控制硬件的执行。


3.3 DMA数据转运

        既然外设就是寄存器,寄存器就是存储器,那使用DMA进行数据转运,就都可以归为一类问题了。就是从某个地址取内容,再放到另一个地址去。

        从框图中可以看到,为了高效有条理地访问存储器,这里设计了一个总线矩阵。

  • 总线矩阵的左端是主动单元,也就是拥有存储器的访问权。
  • 总线矩阵的右端是被动单元,它们的存储器只能被左边的主动单元读写。

主动单元这里,内核有DCode和系统总线,可以访问右边的存储器。

其中DCoude总线是专门访问Flash的,系统总线是访问其它东西的。

另外由于DMA要转运数据,所以DMA也必须要有访问的主动权。


3.4 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进行配置了。


3.5 DMA请求

        DMA请求,请求就是触发的意思。DMA请求线路右边的触发源是各个外设,所以DMA请求就是DMA的硬件触发源。比如ADC转换完成,串口接收到数据。

        需要触发DMA转运数据的时候,就会通过图中红线标注的线路。向DMA发出硬件触发信号之后DMA就可以执行数据转运的工作了,这就是DMA请求的作用。


3.6 DMA结构框图总结

DMA框图中主要讲述了以下内容:

  • DMA内部的多个通道,可以进行独立的数据转运;
  • 仲裁器,用于调度各个通道,防止产生冲突;
  • AHB从设备,用于配置DMA参数;
  • DMA总线用于访问各个存储器的;
  • DMA请求,用于硬件触发DMA的数据转运。

注:

SARM是运行内存,可以任意读写;

外设寄存器,需要参考手册里面的描述,有的寄存器是只读的,有的寄存器是只写的,常用的数据寄存器是可以正常读写的;

CPU或者DMA直接访问Flash的话,是只可以读而不可以写的。

        Flash是ROM只读存储器的一种,如果通过总线直接访问的话,无论是CPU,还是DMA都是只读的,只能读取数据,而不能写入。如果DMA的目的地址,填写了Flash的区域,那转运时就会出错。当然Flash也不是绝对的不可写入,可以配置Flash接口控制器,对Flash进行写入,这个流程比较麻烦,先要对Flash按页进行擦除,再写入数据。

四、DMA基本结构

        DMA基本结构图主要体现DMA内部执行的细节,由图来分析DMA具体是怎么工作的。

4.1 数据转运两大站点

        数据转运的两大站点是,外设寄存器站点存储器站点,存储器站点包括FlashSRAM

        在STM32手册中所说的存储器一般是特指Flash和SRAM,不包含外设寄存器,外设寄存器一般直接称作外设。所以就是外设到存储器,存储器到存储器这样来描述的。虽然外设寄存器也是存储器的一种,但是STM32还是使用了外设寄存器和存储器来作为区分。

        图中可以看到,DMA的数据转运,可以是从外设到存储器,也可以从存储器到外设,具体是从哪到哪,有一个方向的参数,可以进行控制。

        除了外设和寄存器之间的数据转运,还有一种转运方式,就是存储器到存储器。比如Flash到SRAM或者SRAM到SRAM这两种方式。由于Flash是只读的,所以DMA不可以进行SRAM到Flash,或者Flash到Flash的转运操作。

4.2 外设和存储器的参数

        既然要进行数据转运,那肯定就要指定从哪里转到哪里,具体怎么转了。所以外设和存储器两个站点就都有3个参数来配置数据转运:

        第一个是起始地址,有外设端的起始地址和存储器端的起始地址,这两个参数决定了数据是从哪里来到哪里去的。

        第二个是数据宽度,这个参数的作用是,指定一次转运要按多大的数据宽度来进行。可以选择字节Byte、半字HalfWord和字Word。

  • 字节是8位,也就是一次转运一个 unit8_t 这么大的数据。
  • 半字是16位,也就是一次转运一个 unit16_t 这么大的数据。
  • 字是32位,也就是一次转运一个 unit32_t 这么大的数据。

比如转运ADC的数据,ADC的结果是 unit16_t 这么大,所以这个参数就要选择半字,一次转运一个 unit16_t 这么大的数据才可以。

        第三个地址是否自增,这个参数的作用是,指定一次转运完成后,下一次转运,是不是要把地址移动到下一个位置去。这个就相当于是指针,p++,这个意思。比如ADC扫描模式下,用DMA进行数据转运,外设端的起始地址是ADC_DR寄存器,外设寄存器这边显然地址是不用自增的,如果自增的话,那下一次转运就跑到别的寄存器那里去了。存储器这边地址就需要自增,每转运一个数据后就往后挪个坑,要不然下次再转就把上次的覆盖掉了。这就是地址是否自增的作用,就是指定是不是要转运一次挪个坑这个意思。

        以上就是外设站点和存储器站点各自的3个参数了。如果要进行存储器到存储器的数据转运,那就需要把其中一个存储器的地址,放在外设的这个站点,这样就能进行存储器到存储器的数据转运了。只要在外设起始地址里写Flash或者SRAM的地址,那程序就会去Flash或SRAM找数据。

        站点名字虽然叫外设寄存器,但它就只是个名字而已,并不能说这个地址只能写外设寄存器的地址。如果写Flash的地址,那程序就会去Flash里找;如果写SRAM的地址,那程序就会去SRAM里找,这个没有限制。

        甚至在进行外设到存储器的数据转运时,可以在外设站点写存储器的地址,存储器站点写外设的地址,只要把方向参数给反过来,这样也是可以的。外设站点和存储器站点只是ST公司给它起了这样的名字而已,DMA基本结构图中是按照名字来设计的。可以理解为站点A和站点B,从A到B或者B到A转运数据。不必拘泥于外设站点和存储器站点这两个名字。

4.3 传输计数器和自动重装器

        传输计数器,是用来指定总共需要转运几次的。这个传输计数器是一个自减计数器,比如给传输计数器写一个5,那DMA就只能进行5次数据转运,转运过程中,每转运一次,计数器的数就会减1.当传输计数器减到0之后,DMA就不会再进行数据转运了。另外,传输计数器减到0之后,之前自增的地址,也会恢复到起始地址的位置。以方便之后DMA开始新一轮的转运。

        自动重装器,在传输计数器的右边就是自动重装器,作用就是传输计数器减到0之后,是否要自动恢复到最初的值。

  • 如果不使用自动重装器,那转运5次后,DMA就结束了;
  • 如果使用自动重装器,那转运5次,计数器减到0后,就会立即重装到初始值5.

        自动重装器决定了转运的模式:

  • 如果不重装,就是正常的单次模式;
  • 如果重装,就是循环模式。

        举例:

  • 如果需要转运一个数组,那一般就是单次模式,转运一轮就结束了;
  • 如果是ADC扫描模式+连续转换,那为了配合ADC,DMA也需要使用循环模式。

这个循环模式和ADC的连续模式差不多。都是指定一轮工作后,是不是立即开始下一轮工作。

4.4 DMA触发控制

        上图红色框框起来的一块就是DMA的触发控制。

        触发就是决定DMA需要在什么时机进行转运的。

        触发源硬件触发软件触发,具体选择选择哪个,由M2M这个参数决定。

        M2M就是 Memory to Memory,因为数字2的英文 two 和 to 同音,所以 M2M 就是 M to M,存储器到存储器的意思。

        M2M位给1时,DMA就会选择软件触发,这里的软件触发并不是调用某个函数一次,触发一次。这里软件触发执行逻辑是,以最快的速度,连续不断地触发DMA,争取早日把传输计数器清零,完成这一轮的转换。DMA的软件触发和外部中断与ADC的软件触发不太一样,可以把DMA的软件触发理解为连续触发。

这里的软件触发和循环模式不能同时用,因为软件触发就是想把传输计数器清零,循环模式是清零后自动重装。如果同时使用的话,DMA就停不下来了。

软件触发一般适用于存储器到存储器的转运,因为存储器到存储器的转运是软件启动,不需要时机,并且想尽快完成的任务。

        M2M位给0时,DMA就是使用硬件触发。硬件触发源可以选择ADC、串口、定时器等等。使用硬件触发的转运,一般都是与外设有关的转运。这些转运需要一定的时机,比如ADC转换完成、串口收到数据、定时时间到等等。所以需要使用硬件触发,在硬件达到这些时机时,传一个信号过来,就可以触发DMA进行转运。

DMA触发控制总结:

        M2M位给1,就是使用软件触发,就是应用在存储器到存储器转运的情况;

        M2M位给0,就是使用硬件触发,一般都是与外设有关的转运。

4.5 开关控制

        开关控制也就是由DMA_Cmd函数来配置,当给DMA使能后,DMA就准备就绪,可以进行转运了。

4.6 DMA转运条件

DMA进行转运有几个条件:

  • 第一,开关控制,DMA_Cmd函数必须使能。
  • 第二,传输计数器必须大于0.
  • 第三,触发源必须有触发信号。

        触发信号触发一次,转运一次,传输计数器自减一次。当传输计数器等于0,且没有自动重装器时,这时无论是否触发,DMA都不会再进行转运了。此时就需要DMA_Cmd函数配置DISABLE,关闭DMA。然后再为传输计数器写入一个大于0的数。再对DMA_Cmd函数配置ENABLE,开启DMA,DMA才能继续工作。

注意:

        写传输计数器时,必须先关闭DMA,再进行。不能在DMA开启时,写传输计数器,这是手册里的规定。

五、DMA请求

        下图表示的就是上文“4.4 DMA触发控制”这一部分。

        DMA1的请求映像,有DMA的7个通道,每个通道都有一个数据选择器,可以选择硬件触发或软件触发。

        每个通道的数据选择器下边都有一个EN位,这里的EN位并不是数据选择器的控制位,而是决定这个数据选择器要不要工作的位。

  • 当EN位=0时,数据选择器不工作;
  • 当EN位=1时,数据选择器工作。

        每个通道的数据选择器左边的软件触发(MEM2MEM位)的意思是,当M2M位=1时,DMA选择软件触发。

        硬件触发源看图中最左边的外设请求信号,通道1~通道7的硬件触发源都是不同的:

  • 如果需要ADC1来触发的话,那就必须选择通道1;
  • 如果需要定时器2的更新事件(TIM2_UP)来触发的话,那就必须选择通道2;
  • .........

        因为每个通道的硬件触发源都不同,所以如果需要使用某个硬件触发源的话,就必须使用它所在的通道,这就是硬件触发的注意事项。而如果使用软件触发的话,通道就可以任意选择了。因为每个通道的软件触发都是一样的。这也就是“DMA每个通道都支持软件触发和特定的硬件触发”这句话中“特定”的意思,选择硬件触发需要看是哪个通道的。

        通道1的硬件触发是ADC1、定时器2的通道3(TIM2_CH3)、定时器4的通道1(TIM4_CH1)这三个,要选择哪个触发源,是对应的外设是否开启了DMA输出来决定的:

  • 如果需要使用ADC1,那会使用库函数ADC_DMACmd来开启ADC1这一路输出,ADC1才有效; 
  • 如果想选择定时器2的通道3,那会使用库函数TIM_DMACmd来开启TIM2_CH3这一路输出,TIM2_CH3才有效。

如果三个都开启了,图中标识的是一个或门,意思是三个硬件都可以进行触发。但是一般操作时,都是只开启其中一个硬件触发源。

        不同触发源通过7个通道进入到仲裁器,仲裁器用于进行优先级判断,排好先后最终产生内部的DMA1请求。

        仲裁器优先级的判断类似于中断的优先级,默认优先级是通道号越小,优先级越高。当然也可以在程序中配置优先级。

六、数据宽度与对齐

        外设和存储器的参数中有一个参数是数据宽度。如果外设和存储器的数据宽度都一样,那就是正常的一个一个转运。如果外设和存储器的数据宽度不一样,那就需要参考下表的内容。

        表的第一列是源端宽度;第二列是目标宽度;第三列是传输数目。

  • 当源端宽度和目标宽度都是8位时。转运的第一步:在源端的0位置,读数据B0;在目标的0位置,写入数据B0。就是把B0从源端地址挪到目标地址。下一步就是挪B1,接着B2、B3。
  • 当源端宽度是8位,目标宽度是16位。转运的第一步:在源端的0位置,读数据B0;在目标的0位置,写入数据00B0.下一步读B1,写00B1;读B2,写00B2;读B3,写00B3.
  • 当源端宽度是16位,目标宽度是8位。转运的第一步:在源端的0位置,读数据B1B0;在目标的0位置,只写入数据B0.下一步读B3B2,写B2;读B5B4,写B4;读B7B6,写B6.
  1. 当目标的数据宽度比源端的数据宽度相等时,就是把源端数据挪给目标数据。源端8位转目标8位、源端16位转目标16位、源端32位转目标32位都是一样的处理,数据不变
  2. 当目标的数据宽度比源端的数据宽度时,那就在目标的数据前面多出来的空位补0。源端8位转目标16位、源端8位转目标32位、源端16位转目标32位都是一样的处理,前面多出的空位都补0.
  3. 当目标的数据宽度比源端的数据宽度时,就把多出来的高位舍弃掉。源端16位转目标8位、源端32位转目标16位、源端32位转目标8位都是一样的处理,舍弃多余的高位。

        数据宽度的对齐就和 uint8_t、uint16_t、uint32_t 变量之间相互赋值一样,不够就补0,超了就舍弃高位,是一个逻辑。

七、数据转运+DMA

        下图对应一个任务,是将SRAM里的数组DataA转运到另一个数组DataB中。在这个任务下,分析DMA基本结构里的各个参数该如何配置。

        DMA基本结构参数配置:

首先是外设站点和存储器站点的起始地址、数据宽度、地址是否自增这三个参数。看任务图得出:

  1. 外设地址应该填DataA数组的首地址,存储器地址应该给DataB数组的首地址;
  2. 数据宽度,两个数组的类型都是uint8_t,所以数据宽度都是按8位的字节传输;
  3. 地址是否自增,应该外设地址和存储器地址都自增。

        任务图展示的要求是 DataA[0] 转到 DataB[0] 、DataA[1] 转到 DataB[1] 、DataA[2] 转到 DataB[2] ...... DataA[6] 转到 DataB[6] ,两个数组的位置一一对应。所以转运完DataA[0] 和 DataB[0] 之后,两个站点的地址都应该自增,都移动到下一个数据的位置,继续转运DataA[1] 和 DataB[1] ,这样来进行。

  • 如果外设地址不自增,存储器地址自增,效果就会是在转运完成后,DataB的所有数据都会等于DataA[0];
  • 如果外设地址自增,存储器地址不自增,效果就会是在转运完成后,DataB[0]就会等于DataA的最后一个数,DataB其它数不变。
  • 如果外设地址和存储器地址都不自增,那就一直会是DataA[0]转到DataB[0],其它数据不变。

第二方向参数,根据任务图,显然就是外设站点转运到存储器站点了。

        如果需要把 DataB 的数据转运到 DataA ,那可以把方向参数换一下,这样就是反向转运了。

第三传输计数器是否要自动重装。根据任务图,显然要转运7次,所以传输计数器给7。自动重装暂时不需要。

第四触发选择部分,这里使用软件触发。因为这是存储器到存储器的数据转运,不需要等待硬件时机的,尽快转运完成就行了。

最后,给DMA使能,调用DMA_Cmd函数之后,数据就会从 DataA 转运到 DataB 了。转运7次之后,传输计数器自减到0,DMA停止,转运完成。

        这里的数据转运是一种复制转运,转运完成后 DataA 的数据并不会消失。这个任务的过程相当于是把 DataA 的数据复制到了 DataB 的位置。

八、ADC扫描模式+DMA

        下图对应的任务是ADC扫描模式+DMA。图左边是ADC扫描模式的执行流程,有7个通道,触发一次后,7个通道依次进行AD转换。然后转换结果都放到ADC_DR数据寄存器里面。DMA要做的就是,在每个单独的通道转换完成后,进行一个DMA数据转运,并且目的地址进行自增。这样数据就不会被覆盖了。

        DMA基本结构参数配置:

首先是外设站点和存储器站点的起始地址、数据宽度、地址是否自增这三个参数。

  1. 外设地址应该写入 ADC_DR 这个寄存器地址,存储器地址可以在 SRAM 中定义一个数组 ADValue ,然后把 ADValue 的地址当作存储器的地址;
  2. 数据宽度,因为 ADC_DR 和 SRAM 数组需要的数据类型都是uint16_t,所以数据宽度都是16位的半字传输;
  3. 地址是否自增,应该是外设地址不自增,存储器地址自增。

第二方向参数,根据任务图,应该是外设站点转运到存储器站点了。

第三传输计数器是否要自动重装

        传输计数器这里有7个通道,所以计数7次。

        计数器是否自动重装,这需要看ADC的配置。

ADC如果是单次扫描,那DMA的传输计数器可以不自动重装,转换一轮就停止;

ADC如果是连续扫描,那DMA的传输计数器就可以使用自动重装,在ADC启动下一轮转换的时候,DMA也启动下一轮的转运,ADC和DMA同步工作。

第四触发选择部分,这里 ADC_DR 的值是在ADC单个通道转换完成后才会有效,所以DMA转运的时机,需要和ADC单个通道转化完成同步。那DMA的触发要选择ADC的硬件触发。

        硬件触发这里需要说明一下。在ADC扫描模式下,每个单独的通道转换完成后,没有任何标志位,也不会触发中断,所以程序不太好判断某一个通道转换完成的时机是什么时候。这里虽然单个通道转换完成后,不产生任何标志位和中断,但是应该会产生DMA请求,去触发DMA转运。

最后,给DMA使能,调用DMA_Cmd函数之后,数据就会从 ADC_DR 数据寄存器转运到 SRAM 数组了。

        一般来说,DMA最常见的用途就是配合ADC的扫描模式,因为ADC扫描模式有个数据覆盖的特征,或者可以说这个数据覆盖的问题是ADC固有的缺陷,而这个缺陷也使得ADC和DMA成为了最常见的伙伴。ADC对DMA的需求是非常强烈的,其它一些外设使用DMA可以提高效率,是锦上添花的操作,但是不使用也是可以的,顶多损失一些性能。但是ADC的扫描模式,如果不使用DMA,功能都会受到很大的限制。所以ADC和DMA的结合最为常见。


总结

        以上就是今天要讲的内容,本文仅仅简单介绍了DMA的基本结构,并举例了数据转运+DMA和ADC扫描模式+DMA的两个任务,来具体分析DMA基本结构中的参数配置。

相关推荐

  1. <span style='color:red;'>STM</span><span style='color:red;'>32</span><span style='color:red;'>DMA</span>

    STM32DMA

    2024-05-02 11:32:07      18 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-02 11:32:07       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-02 11:32:07       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-02 11:32:07       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-02 11:32:07       18 阅读

热门阅读

  1. 如何使用Vite创建vue项目中配置别名和扩展名

    2024-05-02 11:32:07       13 阅读
  2. 【产品经理修炼之道】- 如何做B端需求分析

    2024-05-02 11:32:07       14 阅读
  3. HCIP第一节

    2024-05-02 11:32:07       9 阅读
  4. http和https 所有的请求头信息

    2024-05-02 11:32:07       12 阅读
  5. Vue3中的ref与reactive:构建响应式数据的双刃剑

    2024-05-02 11:32:07       11 阅读
  6. DAO是什么?有什么用途?

    2024-05-02 11:32:07       10 阅读
  7. 【算法小白周赛1D】K阶恒星系 - 题解和代码

    2024-05-02 11:32:07       14 阅读
  8. ArrayList常考面试题

    2024-05-02 11:32:07       11 阅读