STM32-DMA直接存储器存储(二十)

DMA直接存储器存储

本节我们来学习 DMA 啊,直接存储器存取。 DMA 是一个数据转移小助手,它主要是用来协助 CPU 完成数据转运的工作的。

那我们先看一下本节程序的现象,本节课共有两个程序。第一个是 8-1 DMA 数据转运,第二个是 8-2 DMA 加 AD 多通道。

先看一下第一个啊,在这个程序里,我们将使用 DMA 进行存储器到存储器的数据转运,也就是把一个数组里面的数据复制到另一个数组里。先看一下最终的代码啊,这里我定义了一个数组 data A, 里面存的是 1234。作为待转运的原数据。然后下面再定一个数组 Data B, 里面存的是 4 个 0,作为转运数据的目的地。之后我们将会写一个模块叫买 DMA, 然后买 DMA 初始化,把原数组和目的数组的地址传进去,再传入转运数据的长度 4。接着执行主循环的流程啊

第一步自增变画一下原数组 Data A 的测试数据啊。第二步,显示一下 Data A 和 Data B。 然后延时一秒,方便观看啊。

第三步,调用一下买 DMA Transfor 函数,使用 DMA 进行数据转运。调用一下这个函数啊,和下面这里直接 for 循环,使用 CPU 一个个手动的转运数据,效果是一样的啊。接到最后再显示一下 Data A 和 Data B, 看一下数据是不是从 Data A 转运到了 Data B 啊?

我们下载看一下。这里第一行是 Data A, 右边是 Data A 数组的地址。第二行就是 Data A 的原数据了,每隔两秒变一次。第三行是 Data B, 右边是 Data B 数组的地址。最后一行是 Data B 的目的地数据。可以看到啊, Data A 每变一次,抵了一秒后,数据就转运到了 Data B。 这个转运过程就是由 DMA 来完成的。我这里屏幕比较小啊,所以就只定义了四个数据,演示一下现象。你也可以定义一百个,一千个等等数据,然后使用 DMA 来进行转运都是可以的。那这就是第一个程序的现象。

第二个程序, DMA 加 ADC 多通道。这个程序就是我们上一节预告过的哈,使用 ADC 的扫描模式来实现多通道采集,然后使用 DMA 来进行数据转运,最终 AD 转换的数据就会像这样,直接自动的跑到我们定义的数组里面来。之后我们就只需要用 OLED 显示一下就行。

看上去还是非常方便的是吧?那我们下载看一下现象。这个硬件电路和程序现象啊,和上一节的 AD 多通道是一模一样的,也是测量 PA0 到 PA3 这四个通道的模拟量,这里再简单看一下哈。

拧一下电位器,看 AD0 的数据,往左拧变小,往右拧变大。接着光敏传感器,看 AD1,没问题啊。然后热敏传感器看 AD2,没问题。最后反射传感器看 AD3。也没问题啊。好,这就是第二个程序的现象,和上一节 AD 多通道的现象是一样的啊,就只是在 STM32 端使用了扫描模式,并且加了 DMA 转移数据。

先看一下 DMA 的简介,第一条,DMA 它的英文全称是 Direct Memory Access,直译就是直接存储器存取,或者叫直接存储器访问,从这个 DMA 名字的意思来看啊,DMA 这个外设是可以直接访问 STM32 内部的存储器的,包括运行内存 SRAM,程序存储器,Flash 和寄存器等等,DMA 都有权限访问它们,所以 DMA 才能完成数据转运的工作。

再接着看,DMA 可以提供外设和存储器,或者存储器和存储器之间的高速数据传输,无需 CPU 干预,节省了 CPU 的资源,这里外设指的就是外设寄存器啊,一般是外设的数据寄存器 DR Data Register,比如 ADC 的数据寄存器,串口的数据寄存器等等,这里存储器指的就是运行内存 SRAM 和程序存储器,Flash 是我们存储变量数组和程序代码的地方,在外设和存储器或者存储器和存储器之间进行数据转运就可以使用 DMA 来完成,并且在转运的过程中无需 CPU 的参与,节省了 CPU 的资源,CPU 省下时间就可以干一些其他的更加专业的事情,搬运数据这种杂活啊,交给 DMA 就行了。

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

继续看下一条,每个通道都支持软件触发和特定的硬件触发,这里如果 DMA 进行的是存储器到存储器的数据转运,比如我们想把 Flash 里的一批数据转运到 SRAM 里去,那就需要软件触发了,使用软件触发之后,DMA 就会一股脑的把这批数据啊以最快的速度全部转运完成,这也是我们想要的效果啊。

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

所以存储器到存储器的数据转运,我们一般使用软件触发,外设到存储器的数据转运我们一般使用硬件触发。那这里我写的是特定的硬件触发,意思就是每个 DMA 的通道它的硬件触发源是不一样的,你要使用某个外设的硬件触发源,就得使用它连接的那个通道,而不能任意选择通道,这个我们等会再详细分析啊

那接着最后一条,STM32F103C8T6 的 DMA 资源是 DMA1 7 个通道,我们这个芯片只有 DMA1 的 7 个通道啊,没有 DMA2,这个注意看一下数据手册啊

接着下一个 PPT 我们来看一下 STM32 的存储器映。

我们来看一下 STM32 的存储器映像。既然 DMA 是在存储器之间进行数据转运的,那我们就应该要了解一下 STM32 中都有哪些存储器,这些存储器又是被安排到了哪些地址上,这就是存储器映像的内容。

那我们知道计算机系统的五大组成部分是运算器、控制器、存储器、输入设备和输出设备。其中运算器和控制器一般会合在一起啊,叫做 CPU。 所以计算机的核心关键部分就是 CPU 和存储器。存储器又有两个重要知识点。一个是存储器的内容,另一个就是存储器的地址。那 STM32 也不例外,这里有个表啊,这个表就是 STM32 中所有类型的存储器和它们所被安排的地址。在 STM32 的数据手册这里,也会有个存储器映像的图。我这个表就是从这个图里总结出来的哈,都是一个意思。

在这个表里,无论是 Flash 还是 SRAM 还是外设寄存器,它们都是存储器的一种。包括外设寄存器啊,实际上也是存储器。我们前面这里说的是外设到存储器,存储器到存储器,本质上其实都是存储器之间的数据转运啊,说成外设到存储器只不过是 STM32 它特别指定了可以转运外设的存储器而已

这个了解一下。好我们来看一下这个表,在这个表里,存储器总共分成两大类, ROM 和 RAM。 ROM 就是只读存储器是一种非易失性,掉电不丢丢失的存储器。 RAM 就是随机存储器,是一种易失性掉电丢失的存储器。

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

接着继续看下面两块,系统存储器和选项字节,这两块存储器也是 ROM 的一种哈,掉电不丢失。实际上它们的存储介质也是 Flash, 只不过是我们一般说 Flash 指的是主闪存 Flash 啊,而不指这两块区域。那看一下地址,它们的地址都是 0xFFF 开头的,紧跟着 0x2000 开头的就是 RAM 区的。所以可以看出这两块存储器的位置是在 ROM 区的最后面。它们的用途啊,看一下右边说明。系统存储器的用途是存储 Bootloader, 用于串口下载。这个下一讲串口的时候再给大家演示啊。那这个 Bootloader 程序存储的位置就被分配到了这里。 Bootloader 程序是芯片出厂自动写入的啊,一般也不允许我们修改。之后选项字节,这个是用于存储一些独立于程序代码的配置参数。它的位置是在 ROM 区的最后面啊,你下载程序可以不刷新选项字节的内容,这样选项字节的配置就可以保持不变。选项字节里存的主要是 Flash 的读保护写保护啊,还有看门狗等等的配置,这个如果需要的话可以了解一下。

然后我们看下 RAM 区,首先是运行内存 SRAM, 分配的地址是 0X20000000 用途是存储运行过程中的临时变量,也就是我们在程序中定义变量数组结构体的地方。你可以试一下啊,定义一个变量,再取它的地址显示出来,那这个地址肯定就是二零零零开头的。类比于电脑的话,运行内存就是内存条。

然后 RAM 区剩下的还有外设寄存器,它的地址是 0X4000 0000 这块区,用途是存储各个外设的配置参数,也就是我们初始化各个外设最终所读写的东西。刚才我们说了,外设寄存器也是存储器的一种,它的存储介质其实也是 SRAM 哈,只不过我们一般习惯把运行内存叫 SRAM, 外设寄存器就直接叫寄存器了。

接下来最后是内核外设寄存器,地址是 0XE000 0000 这片区域。用途是存储内核各个外设的配置参数。内核外设就是 NVIC 和 SysTick 啊,因为内核外设和其他外设不是一个厂家设计的,所以它们的地址也是被分开的哈,内核外设是 E000,其他外设是 4000。那以上这些就是 STM32 里的存储器和它们被安排的地址。

接着我们在这个图里也看一下啊,在 STM32 中,所有的存储器都被安排到了 0~8 个 F 这个地址范围内。

因为 CPU 是 32 位的啊,所以寻址范围就是 32 位的范围。32 位的寻址范围是非常大的,最大可以支持 4GB 容量的存储器。而我们 STM32 的存储器都是 KB 级别的,所以这个 4GB 的寻址空间啊,会有大量的地址都是空的。算一下地址的使用率还不到 1%。在这个图里有灰色填充的就是 Reserved 的区,也就是保留区,没有使用到。

然后这个 0 地址啊,实际上也是没有存储器的。它这里写的是别名到 Flash 或者系统存储器,取决于 Boot 引脚。因为程序是从 0 地址开始运行的,所以这里需要把我们想要执行的程序映射到 0 地址来。如果映射在 Flash 区,就从 Flash 执行。如果映射在系统存储器区,就是从系统存储器运行 Bootloader。 如果映射在 SRAM, 就是从 SRAM 启动。怎么选择由 Boot0 和 Boot1 两个引脚来决定。这就是 0 地址里的别名区啊。

接着剩下的 0800 开始的 Flash 区,用于存储程序代码。1FFF 开始的系统存储器和选项字节,是在 ROM 区的最后面啊,存了什么东西刚才也都介绍过。

之后 2000 开始的是 SRAM 区,4000 开始的是外设寄存器。里面可以展开,就是右边这些东西。具体到每个外设,又有它们自己的起始地址。比如 TIM2 的地址是 40000000, TIM3 是 40000400。然后外设地址里面又可以具体细分到每个寄存器的地址,寄存器里每个字节的地址,最终所有字节的地址就都可以算出来了。

就上面这里, E000 开始的区域啊,存放的就是内核里面的外设寄存器了。

那到这里,相信你对 STM32 里面有哪些存储器,每种存储器都对应在哪个地址区间里,就应该清楚了吧?那接下来继续,我们来看一下这个 DMA 的框图。

框图,这个框图我们在第一节 STM32 的系统结构里也见过一个类似的是吧?左上角这里是 Cortex-M3 内核,里面包含了 CPU 和内核外设等等。剩下的这所有东西啊,你都可以把它看成是存储器。所以总共就是 CPU 和存储器两个东西。

Flash 是主闪存, SRAM 是运行内存。各个外设都可以看成是寄存器,也是一种 SRAM 存储器啊。寄存器是一种特殊的存储器,一方面, CPU 可以对寄存器进行读写,就像读写运行内存一样。另一方面,寄存器的每一位背后都连接了一根导线,这些导线可以用于控制外设电路的状态,比如置引脚的高低电平啊,导通和断开开关啊。切换数据选择器,或者多位结合起来当做计数器,数据寄存器等等等等。所以寄存器是连接软件和硬件的桥梁,软件读写寄存器就相当于在控制硬件的执行。

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

我们看图啊,为了高效有条理的访问存储器,这里设计了一个总线。线矩阵。总线矩阵的左端是主动单元,也就是拥有存储器的访问权。右边这些是被动单元,它们的存储器只能被左边的主动单元读写

主动单元这里内核有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 的各个部分和作用。

最后再额外说一个问题啊,就这里的 Flash, 它是 ROM 只读存储器的一种。如果通过总线直接访问的话,无论是 CPU 还是 DMA 都是只读的,只能读取数据而不能写入。如果你 DMA 的目的地址填写在 Flash 的区域,那转运时就会出错,这个注意一下啊。当然, Flash 也不是绝对的不可写入,我们可以配置这个 Flash 接口控制器对 Flash 进行写入啊。这个流程就比较麻烦了,要先对 Flash 按页进行擦除,再写入数据。不过这是另一个课题了啊,这里就不再讨论。总之就是 CPU 或者 DMA 直接访问 Flash 的话,是只可以读而不可以写的。然后 SRAM 是运行内存,可以任意读写啊,没有问题。外设寄存器的话,得看参考手册里面的描述。有了这寄存器是只读的,有的寄存器是只写的。不过我们主要用的是数据寄存器啊,数据寄存器都是可以正常读写的。好,这个图目前就讲到这里。

接着继续看下一个 PPT 啊,这里是我总结的 DMA 基本结构图。如果想编写代码实际去控制 DMA 的话,那这个图就是必不可少的。刚才这个框图只是一个笼统的结构图啊,对于 DMA 内部的执行细节,它还是没体现出来。所以我们再来分析一下这个图,看看 DMA 具体是怎么工作的。

在这个图里,这两部分就是数据转运的两大站点了,左边是外设寄存器站点,右边是存储器站点,包括 Flash 和 SRAM。 在 STM32 手册里,它所说的存储器啊,一般是特指 Flash 和 SRAM, 不包含外设寄存器啊。外设寄存器它一般直接称作外设。所以就是外设到存储器,存储器到存储器这样来描述。虽然我们刚才说了寄存器也是存储器的一种,但是 STM32 还是使用了外设和存储器来作为区分哈,这个注意一下描述方法的不同。那在这里可以看到, DMA 的数据转运可以是从外设到存储器,也是可以从存储器到外设。具体是向左还是向右,有一个方向的参数可以进行控制。另外还有一种转运方式,就是存储器到存储器,比如 Flash 到 SRAM,或者 SRAM 到 SRAM 这两种方式。由于 Flash 是只读的,所以 DMA 不可以进行 SRAM 到 Flash 或者 Flash 到 Flash 的转运操作。

然后我们继续看这两边的参数,既然要进行数据转运,那肯定就要指定从哪里转到哪里,具体怎么转呢?所以外设和存储器两个点就都有三个参数。第一个是起始地址,有外设端的起始地址和存储器端的起始地址。这两个参数决定了数据是从哪里来到哪里去的。之后第二个参数是数据宽度。这个参数的作用是指定一次转运要按多大的数据宽度来进行。它可以选择字节 Byte、半字 HalfWord 和字 Word。字节就是八位啊,也就是一次转运一个 uint 8 杠 t 这么大的数据。半字是十六位,就是一次转运一个 uint 16 杠 t 怎么大?字是 32 位,就是一次转运 uint 32 杠 t 这么大。比如转运 ADC 的数据, ADC 的结果是 uint 16 杠 t 这么大,所以这个参数就要选择半字,一次转运一个 uint 16 杠 t, 这样才对。然后第三个参数是地址是否自增。这个参数的作用是指定一次转运完成后,下一次转运是不是要把地址移动到下一个位置去。这就相当于是指针 P 加加这个意思。

比如 ADC 扫描模式用 DMA 进数据转运,外设地址是 ADC DR 寄存器,寄存器这边显然地址是不用自增的,如果自增,那下一次转运就跑到别的寄存器那里去了。存储器这边,地址就需要自增,每转运一个数据后,就往后挪个坑,要不然下次再转就把上次的覆盖掉了。这就是地址是否自增的作用,就是指定是不是要转运一次挪个坑这个意思。这就是外设站点和存储器站点各自的三个参数了。

如果要进行存储器到存储器的数据转运,那我们就需要把其中一个存储器的地址放在外设的这个站点,这样就能进行存储器到存储器的转运了。

只要你在外设起始地址里写 Flash 或者 SRAM 的地址,那它就会去 Flash 或 SRAM 找数据。这个站点虽然叫外设存储器,但是它就只是个名字而已哈,并不是说这个地址只能写寄存器的地址。如果写 Flash 的地址,那它就会去 Flash 里找,写 SRAM 它就会去 SRAM 里找,这个没有限制哈。甚至你可以在外设站点写存储器的地址,存储器站点写外设的地址,然后方向参数给反过来,这样也是可以的。只是 ST 公司给它起了这样的名字而已哈。所以我这里就按照它的名字来做的 PPT。

你也可以把它叫做站点 A 站点 B,从 A 到 B 或者从 B 到 A 转运数据,不必拘泥于它写的外设站点、存储器站点这个名字的哈。好,接着往下面看,这里有个东西叫做传输计数器,这个东西就是用来指定我总共需要转运几次的。这个传输计数器是一个自减计数器啊,比如你给它写个 5,那 DMA 就只能进行 5 次数据转运。转运过程中,每转一次计数器的数就会减一。当传输计数器减到 0 之后, DMA 就不会再进行数据转运了。另外它减到 0 之后,之前自增的地址啊,也会恢复到起始地址的位置,以方便之后 DMA 开始新一轮的转换。在传输计数器的右边,有一个自动重装器。这个自动重装器的作用就是,传输计数器减到 0 之后,是否要自动恢复到最初的值。比如最初传输计数器给 5,如果不使用自动重装器,那转用 5 次后, DMA 就结束了。如果使用自动重装器,那转运 5 次,计数器减到 0 后,就会立即重装到初始值 5。这个就是自动重装器,它决定了转运的模式。如果不重装,就是正常的单次模式。如果重装,就是循环模式。

比如如果你想转运一个数组,那一般就是单次模式,转运一轮就结束了。如果是 ADC 扫描模式加连续转换,那为了配合 ADC, DMA 也需要使用循环模式。所以这个循环模式和 ADC 的连续模式差不多啊,都是指定一轮工作完成后是不是立即开始下一轮工作。然后继续往下看啊

这一块就是 DMA 的触发控制了。触发就是决定 DMA 需要在什么时机进行转运的。触发源有硬件触发和软件触发,具体选择哪个由 M2M 这个参数决定。 M2M 就是 Memory to Memory 啊,因为 2 的英文 two 和 to 同音,所以 M2M 就是 M to M, 存储器到存储器的意思。当我们给 M2M 位一时 DMA 就会选择软件触发。这个软件触发并不是调用某个函数一次触发一次啊,它这个软件触发的执行逻辑是以最快的速度连续不断的触发 DMA, 争取早日把传输计数器清 0,完成这一轮的转换。

所以这里的软件触发和我们之前外部中断和 ADC 的软件触发可能不太一样哈,你可以把它理解成连续触发。那这个软件触发和循环模式不能同时用哈,因为软件触发就是想把传输计数器清 0,循环模式是清 0 后自动重装。如果同时用的话,那 DMA 就停不下来了。这就是软件触发。

软件触发一般适用于存储器到存储器的转运。因为存储器到存储器的转运是软件启动,不需要时机,并且想尽快完成的任务。所以上面这里, M2M 位给一就是软件触发,就是应用在存储器到存储器转运的情况。

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

然后最后就是开关控制了,也就是 DMA CMD 函数啊。当给 DMA 使能后, DMA 就准备就绪,可以进行转运了。 DMA 进行转运有几个条件啊

第一就是开关控制, DMA CMD 必须使能

第二就是传输计数器必须大于 0。

第三就是触发源必须有触发信号,触发一次转运一次,传输计数器自减一次。当传输计数器等于 0,且没有自动重装时,这时无论是否触发, DMA 都不会再进行转运了。此时就需要 DMA CMD 给 disable, 关闭 DMA, 再为传输计数器写一个大于 0 的数,再 DMA CMD 给 enable, 开启 DMA, DMA 才能继续工作。

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

好,到这里, DMA 的基本结构我就介绍完了。接下来我们再看几个细节的问题。第一

再看几个细节的问题,第一个是 DMA 请求。这张图表示的就是我们上面这里的这部分结构, DMA 触发的部分。我们来看一下,这张图是 DMA1 的请求映像,下面是 DMA 的 7 个通道,每个通道都有一个数据选择器,可以选择硬件触发或软件触发。这里画的图啊,我觉得可能不太好理解。你看他把 EN 位画在了数据选择器的侧边,一般数据选择器的侧边是输入选择控制位吧,难道这里的意思是 EN 给一选择硬件触发, EN 给零选择软件触发吗?那显然不对啊。而且它左边这里写的是软件触发,括号, memory to memory way。难道 M2 M V 是软件触发吗?这个也不太好理解啊。所以这个图我重新给它布了个局,就是上面我这里画的这样, M2M 位是数据选择器的控制位,用于选择是硬件触发还是软件触发。 EN 位是开关控制, EN 等于 0 时不工作, EN 等于 1 时工作。这样就好理解一些了。那这里他这样画的意思应该是, EN 并不是数据选择器的控制位啊,而是决定这个数据选择器要不要工作。 EN 等于 0 数据选择器不工作, EN 等于 1 数据选择器工作。然后软件触发,后面跟个 M2M 位的意思应该是,当 M2M 位等于一时选择软件触发。这样理解的话就跟我这个图里是一个意思了。

那然后继续看左边的硬件触发源。这里是外设请求信号啊,可以看到每个通道的硬件触发源都是不同的。如果你需要用 ADC 一来触发的话,那就必须选择通道一。如果需要定时器 2 的更新事件来触发的话,那就必须选择通道 2。剩下的也是同理哈。因为每个通道的硬件触发源都不同,所以如果你想使用某个硬件触发源的话,就必须使用它所在的通道,这就是硬件出发的注意事项。而如果使用软件出发的话,那通道就可以任意选择了。因为每个通道的软件出发都是一样的。

所以在 PPT 的这前面啊,写的是每个通道都支持软件出发和特定的硬件出发,这就是特定的意思选择硬件出发是要看通道的。

然后回来继续看看。这里通道一的硬件触发是 ADC1,定时器 2 的通道 3 和定时器 4 的通道一。那到底是选择哪个触发源呢?这个是对应的外设是否开启了 DMA 输出来决定的。比如你要使用 ADC1,那会有个库函数叫 ADC_ DMA_ CMD。 必须使用这个库函数开启 ADC1 的这一路输出,它才有效。如果想选择定时器 2 的通道 3,那也会有个 TIM_ DMA_ CMD 函数,用来进行 DMA 输出控制。所以这三个触发源啊对。你使用哪个,取决于你把哪个外设的 DMA 输出开起了。如果三个都开起了,那这边是一个或门哈,理论上三个硬件都可以进行出发。不过一般情况下我们都是开启其中一个,这就是这部分电路。

之后这七个出发源进入到仲裁器进行优先级判断,最终产生内部的 DMA1 请求。这个优先级的判断啊类似于中断的优先级,默认优先级是通道号越小优先级越高。当然也可以在程序中配置优先级啊,这个其实影响并不是很大,大家了解一下就行了。

之后我们再看一个细节问题啊,就是数据宽度与对齐。这里有个表,这个表是用来说明什么问题的呢?

我们可以看下前面这里,这里数据转移的两个站点都有一个数据宽度的参数。如果数据宽度都一样,那就是正常的一个个转运。如果数据宽度不一样,那会怎么处理呢?

这个表就是来说明这个问题的,我们看一下。这里第一列是源端宽度,第二列是目标宽度,第三列是传输数目。当源端和目标都是 8 位时,转运第一步,在源端的 0 位置读数据 B0,在目标的 0 位置写数据 B0,就是把这个 B0 从左边挪到右边。之后的步骤就是把 B1 从左边挪到右边,接着 B2 B3。这是源端和目标都是 8 位的情况,操作也很正常啊。

接着继续,源端是 8 位,目标是 16 位,那它的操作就是在源端 B0,在目标写 00B0,之后读 B1 写 00B1 等等。这个意思就是,如果你目标的数据宽度比源端的数据宽度大,那就在目标数据前面多出来的空位补 0,之后 8 位转运到 32 位也是一样的处理哈,前面空出来的都补 0。

然后下面,当目标数据宽度比源端数据宽度小时,比如由 16 位转到 8 位去,现象就是读 B1B0,只写入 B0。读 B 三 B 二只写入 B 二也就是把多出来的高位舍弃掉。之后的各种情况啊,也都是类似的操作。

总之这个表的意思就是,如果你把小的数据转到大的里面去,高位就会补 0,如果把大的数据转到小的里面去,高位就会舍弃掉。如果数据宽度一样,那就没事。这个操作我们也可以想象得到哈,就是跟 unit 8 杠 t, unit 16 杠 t 和 unit 32 杠 t 变量之间相互赋值一样,不够就补 0,超了就舍弃高位,这是一个道理哈。

那最后我们再来看两个例子,看看在这些实际的任务下, DMA 是如何工作的。第一个例子就是数据转运加 DMA, 第二个例子是 ADC 扫描模式加 DMA。 这两个例子和我们演示的两个程序是对应的哈。看一下第一个例子

这个例子的任务是将 SRAM 里的数组 data A 转运到另一个数组 data B 中。我们看一下这种情况下,这个基本结构里的各个参数该如何配置。首先是外设站点和存储器站点的起始地址,数据宽度,地址是否自增这三个参数。

那在这个任务里,外设地址显然应该填 data A 数组的首地址,存储器地址给 data B 数组的首地址。然后数据宽度,两个数组的类型都是 unit 8 杠 t, 所以数据宽度都是按 8 位的字节传输。之后地址是否自增,在中间可以看到啊。我们想要的效果是 data A 0 转到 data B 0, data A 1 转到 data B 1 等等,两个数组的位置一一对应啊。所以转运完 data A 0 和 data B 0 之后,两个站点的地址都应该自增。都移动到下一个数据的位置,继续转运 data A 一和 data B 一这样来进行。

如果你左边不自增,右边自增,效果就是这样的。转运完成后, data B 的所有数据都会等于 data A 零。如果左边自增,右边不自增,那效果就是这样的。转运完成后, data b 0 等于 data a 的最后一个数, data b 其他的数不变。

如果左右都不自增,那就一直是 data a 0 转到 data b 0,其他的数据不变。这就是地址是否自增的效果。

之后这里的方向参数,那显然就是外设站点转运到存储器站点了。如果你想把 data b 的数据转运到 data a,那可以把方向参数换过来,这样就是反向转运了。然后是传输计数器和是否要自动重装。在这里啊,显然要转运七次,所以传输计数器给七,自动重装暂时不需要。之后触发选择部分,这里我们要使用软件触发,因为这是存储器到存储器的数据转运,是不需要等待硬件时机的,尽快转运完成就行了。在最后调用 DMA cmd 给 DMA 使能,这样数据就会从 data A 转运到 data B 了。转运七次之后,传输计数器自减到零, DMA 停止,转运完成。这里的数据转运是一种复制转运哈,转运完成后, data A 的数据并不会消失,这个过程相当于是把 data A 的数据复制到了 data B 的位置。这就是第一个任务啊。存储器到存储器的数据转移。

接着看第二个任务, ADC 扫描模式加 DMA。

左边是 ADC 扫描模式的执行流程。在这里有七个通道,触发一次后七个通道依次进行 AD 转换,然后转换结果都放到 ADC DR 数据寄存器里面。那我们要做的就是在每个单独的通道转换完成后,进行一次 DMA 数据转移,并且目的地址进行自增,这样数据就不会被覆盖了。所以在这里, DMA 的配置就是,外设地址写入 ADC DR 这个寄存器的地址,存储器的地址可以在 SRAM 中定一个数组 A d Value ,然后把 A d Value的地址当做存储器的地址。之后数据宽度,因为 A d Value 和 SRAM 数组我们要的都是 unit 16杠 t 的数据,所以数据宽度都是十六位的半字传输。

继续地址是否自增,那从这个图里显然是外设地址不自增,存储器地址自增。传输方向是外设站点到存储器站点。传输计数器这里通道有七个,所以计数七次,计数器是否自动重装,这里可以看 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 固有的缺陷。哈,这个缺陷使 ADC 和 DMA 成了最常见的伙伴。 ADC 对 DMA 的需求是非常强烈的。

像其他的一些外设啊,使用 DMA 可以提高效率,是锦上添花的操作。但是不使用也是可以的,顶多是损失一些性能哈。

但是这个 ADC 的扫描模式,如果不使用 DMA 功能都会受到很大的限制哈。所以 ADC 和 DMA 的结合最为常见。好到这里我们 DMA 的 PPT 部分就讲完了。最后我们再来看一下参考手册哈,看看参考手册上都讲了哪些东西。

相关推荐
耳朵东先生2 小时前
STM32 开发利器:SEGGER RTT 日志打印与 Shell 实践解析
单片机·嵌入式硬件
ACP广源盛139246256732 小时前
IX6012 PCIe 交换芯片@ACP#RTX Spark 入门级 12 口存储外设扩展方案(对比 ASM1812)
大数据·人工智能·分布式·嵌入式硬件·gpt·spark·电脑
2601_958352902 小时前
对讲系统音频优化实战:解决回声、啸叫、环境噪音与远场拾音难题
嵌入式硬件·音视频·语音识别·降噪处理·音频处理模块·硬件开发模块
振南的单片机世界2 小时前
RS485组网三要素:负载、距离、终端电阻
arm开发·stm32·单片机·嵌入式硬件
小慧10242 小时前
Esp开发工具命令
单片机
redaijufeng2 小时前
stm32实现串口打印输出_stm32串口打印
stm32·单片机·嵌入式硬件
黑白园2 小时前
STM32CubeIDE配置FreeRTOS及Demo验证
stm32·单片机·嵌入式硬件
iCxhust3 小时前
8086 汇编位测试使用方法
汇编·单片机·嵌入式硬件·微机原理·8088单板机
SUNNYSPY0013 小时前
AO3404-ASEMI锂电池保护(BMS)专用AO3404
单片机