STM32学习笔记(九)| DMA直接存储器存取详解

本篇文章包含的内容

  • 一、什么是DMA?
  • 1.1 DMA简介
  • 1.2 DMA通道的触发方式
  • 1.3 STM32存储器组织与映像
  • 二、DMA的结构和工作原理
  • 2.1 DMA的结构框图
  • 2.2 DMA的编程结构
  • 2.3 DMA使用时的细节问题
  • 2.3.1 DMA请求
  • 2.3.2 数据宽度与对齐要求
  • 三、DMA应用实例
  • 3.1 DMA实现数据转运
  • 3.2 DMA+AD多通道数据转运
  • ​  本次课程采用单片机型号为STM32F103C8T6。
    ​  课程链接:江科大自化协 STM32入门教程


      往期笔记链接:
      STM32学习笔记(一)丨建立工程丨GPIO 通用输入输出
      STM32学习笔记(二)丨STM32程序调试丨OLED的使用
      STM32学习笔记(三)丨中断系统丨EXTI外部中断
      STM32学习笔记(四)丨TIM定时器及其应用(定时中断、内外时钟源选择)
      STM32学习笔记(五)丨TIM定时器及其应用(输出比较丨PWM驱动呼吸灯、舵机、直流电机)
      STM32学习笔记(六)丨TIM定时器及其应用(输入捕获丨测量PWM波形的频率和占空比)
      STM32学习笔记(七)丨TIM定时器及其应用(编码器接口丨用定时器实现编码器测速)
      STM32学习笔记(八)丨ADC模数转换器(ADC单、双通道转换)


    一、什么是DMA?

    1.1 DMA简介

      DMA(Direct Memory Access),即“直接存储器存取/访问”,可以提供外设和存储器之间,或存储器和存储器之间直接的高速数据传输。这里的外设指外设寄存器指外设的数据寄存器Data Register——DR,例如ADC的数据寄存器,串口的数据寄存器等;存储器指运行内存SRAM和程序存储器Flash,是存储变量、数组和程序代码的地方。需要注意的是,原理上DMA执行存储器到存储器的数据转运,只不过STM32特殊制定了可以将外设的存储器看作普通的存储器进行数据转运。这个传输过程无需CPU干预,节省了CPU资源。 这里可以理解为CPU将搬运数据这种“杂活”外包给一个小秘书,CPU剩下时间和空间,用来完成其他更复杂和专业的操作。
      STM32的DMA有12个可以独立配置的通道,通道就是数据转运的路径,从一个地方到另一个地方就可以看作一个通道。其中DMA1有7个通道,DMA2有5个通道
      STM32F103C8T6的DMA资源:DMA1(7个通道)。

    1.2 DMA通道的触发方式

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

  • 软件触发:DMA执行从存储器到存储器的数据转运,例如把Flash中的一批数据转运到SRAM中去,这时DMA会把要转运的数据以最快的速度全部转运完成。
  • 特定的硬件触发:DMA执行外设和存储器之间的数据传输,由于外设中需要转运的数据存在一定的时机,所以就需要用到硬件触发转运。例如在ADC工作时,需要在ADC每个通道AD转换完成后,硬件触发依次DMA,DMA才会执行转运。“特定”指每个DMA的通道的硬件触发源都不同。
  • 1.3 STM32存储器组织与映像

      学习DMA的工作原理前,首先要了解STM32的存储器是如何组织的。

  • ROM 只读存储器:是一种非易失性,掉电不丢失的存储器。
    1. 程序存储器Flash:即主闪存,用途是存储C语言编译后的程序指令码,即下载程序的位置。运行程序一般都是从主闪存Flash开始的。
    2. 系统存储器:用来存储Bootloader程序,用于串口下载。该程序是芯片出厂自动写入的,一般不允许用户修改,它和选项字节一同存储在ROM的最后面。存储介质同样是Flash。
    3. 选项字节:用来存储一些独立于代码的配置参数,例如Flash的读保护和写保护、看门狗配置等。它和系统存储器一起存储在ROM区的最后面。下载程序后可以不刷新选项字节的内容,这样选项字节的内容就会保持不变。
  • RAM 随机存储器:是一种易失性,掉电丢失的存储器。
    1. 运行内存SRAM(静态随机存取储存器,Static RAM):存储程序运行过程中定义的临时变量,如变量、数组、结构体。
    2. 外设存储器:存储各个外设的配置参数。初始化外设相当于对其中的数据进行读写。外设存储器也是存储器的一种,它的存储介质同样是SRAM。
    3. 内核外设存储器:存储内核各个外设的配置参数。内核外设指NVIC和Systic。由于内核外设和其他外设在设计时不是一个厂家设计的,所以他们的存储地址不是连续的。


      上图也是STM32的存储器映像图。可以看到,STM32最大拥有32位地址线,所以有4GB的寻址空间。但是图中灰色的部分都表示该地址保留,所以它的地址利用率还不到1%。
      0x0000 0000实际上没有存储器,它根据BOOT0和BOOT1引脚别名到了Flash或系统存储器。程序总是从0x0000 0000地址开始运行的,所以在程序运行前,需要将想要执行的程序映射到0地址来,如果映射在Flash区,就是从Flash执行;如果映射在系统存储器区,就是从系统存储器运行Bootloader程序;如果映射在SRAM区,就是从SRAM启动。
      在外设寄存器区中存储所有外设的配置信息,同样也可以在上图中找到任何一个外设的存储区的起始地址。每一个外设又可以细分到每个寄存器的地址,每个寄存器又可以细分到每一位的地址。通过这样的组织方式,就可以找到任何一个寄存器的任何一位了。

    二、DMA的结构和工作原理

    2.1 DMA的结构框图


      上图展现了DMA在STM32内部和其他部件之间的主要联系和工作原理。首先,上图展现了STM32的Cortex-M3内核,可以将除该内核以外的其他部分都看作存储器。 DMA的主要结构分为:

  • DMA总线
  • 数据转运通道
  • 仲裁器
  • AHB从设备
  • DMA请求
  •   寄存器是一种特殊的存储器,它的存储介质同运行内存一样是SRAM。一方面,CPU可以对寄存器进行正常的读写操作,与操作存储器中的存储单元类似;另一方面,可以简单地认为寄存器的每一位后面都可以连接一根“线”用于控制外部的硬件设备,例如设置引脚的高低电平,控制开关的通断,切换数据选择器,或者多位组合起来作为计数器、数据选择器等。总之,寄存器是连接软件和硬件之间的桥梁,CPU执行软件程序对寄存器进行读写就相当于直接对硬件进行操作。 STM32中外设的寄存器有些是只读的,有些是只写的,具体要参考数据手册的说明。不过使用DMA进行数据转运时一般都操作数据寄存器DR,DR都是可以正常读写的,这里不再作额外区分。

      在使用DMA进行数据转运时,如果将外设中的数据寄存器看作存储器,那么就可以将使用DMA进行数据转运归为同一类操作:把一个地址的数据取出,送到另一个地址去
      如上图所示,为了高效访问各类存储器,STM32存在一个总线矩阵,它的左侧是主动单元(拥有总线矩阵控制权的单元,图中所示为Cortex-M3内核,DMA,以太网MAC模块),右侧是被动单元(只能被读写的存储器单元)。Cortex-M3内核可以通过DCode和系统总线对存储器进行操作,其中DCode主要对Flash进行读操作,系统总线对SRAM、外设寄存器进行读写操作。此外,由于DMA要访问数据,所以DMA也拥有对总线矩阵的控制权,DMA1和DMA2各有一条DMA总线。以太网外设可以通过自己私有的DMA外设控制总线矩阵,但以太网不是本节的主要内容,这里仅作了解即可。
      上图同样可以看到STM32中的两个DMA内部的部分结构。其中DMA1有7个通道,DMA2有5个通道,每个通道都可以独立配置数据转运的源地址和目的地址。DMA拥有多个转运通道,可以独立转运数据,但是每个DMA与外部连接的DMA总线却只有一条,这就要求所有通道要分时复用这一条DMA总线,当多个DMA通道转运数据是产生了冲突,就需要由仲裁器按照DMA通道的优先级来决定各个通道使用DMA总线的次序(总线矩阵中同样有一个仲裁器,如果DMA和CPU都要访问同一个目标,那么DMA就会通过总线仲裁器暂停CPU的访问以防止冲突,同时给CPU留有一般的带宽保证CPU可以正常工作)。DMA内部的“AHB从设备”指CPU可以通过系统总线控制总线矩阵,然后通过AHB总线对DMA内部的配置寄存器进行读写操作,从而完成对DMA的配置。所以DMA既是总线矩阵的主动单元,可以读写存储器,又是AHB总线的被动单元。
      图中DMA右侧的“DMA请求”指DMA的硬件触发源。当ADC转换完成、串口接收到数据、定时器中断触发后,就会通过“DMA请求”线路发出硬件触发信号,请求DMA转运数据。

      Flash是一种ROM,即只读存储器。无论是CPU还是DMA都不能对Flash进行写操作,只能对Flash进行读操作。如果DMA的目的地址填写了Flash区域的内存,在数据转运时就会出错。但Flash也不是绝对的不可以写入,可以通过配置“Flash接口控制器”对Flash进行写操作,需要对Flash按页进行擦除,之后再写入数据,比较麻烦。不过这部分内容属于另一个课题,这里仅作简单了解即可。

    2.2 DMA的编程结构


      DMA进行数据转运时,首先应该定义两个站点。在数据手册中,ST公司将两个站点分别描述为“外设寄存器”和“存储器”,但实际上两个站点并不一定要写入外设寄存器的地址和存储器的地址,所以可以简单将两个站点理解为站点A站点B。数据是从A传输到B,还是从B传输到A,由两个站点之间的“方向”参数确定。每一个站点拥有三个参数,他们分别是:

  • 起始地址:存储将要传输数据的地址。需要注意,这里的“起始”地址并不一定指被传输数据的地址,它同样可以是数据被送往的目的地址。
  • 数据宽度:一次被传输的数据宽度,它可以是字节Byte(8位)、半字HalfWord(16位)、字Word(32位)。(STM32的字长为32位)
  • 地址是否自增:完成一次数据传输后地址指针是否自增。一般而言,在执行存储器到存储器的数据传输源地址和目的地址均需要自增,执行外设和存储器之间的数据传输时存储器地址需要自增,外设地址不需要自增(因为外设寄存器的地址是固定的)。
  •   上文已经介绍,DMA可以执行存储器和存储器之间的数据传输,或者执行外设和存储器之间的数据传输。在数据手册中,“存储器”一般特指Flash和SRAM,不包含外设寄存器;而外设寄存器一般被简称为“外设”。这里只是名称叫法上的区别,在完成数据传输时,DMA并不能识别传输的数据来自何方,去向何方,它只能将数据从“一个地址”送往“另一个地址”。数据的来源是由程序员在配置DMA时对两个站点的“起始地址”参数进行填写时确定的。如上图所示,DMA可以完成在外设寄存和SRAM之间的数据传输、SRAM和SRAM之间的数据传输、从Flash到SRAM的数据传输,但目的地址不可以是Flash(Flash是只读存储器)。
      此外,DMA内部还拥有传输计数器自动重装器两个寄存器。传输计数器是用来指定传输次数的寄存器,它是一个自减计数器,当它的值为0时,DMA就不会进行数据转运了,由于传输时自增后的地址也会恢复到起始地址的位置,方便下一次的数据转运。自动重装器的作用是:当传输计数器自减到0后,可以配置自动重装器来确定是否自动使传输计数器回到初始定义的值,它决定了DMA转运数据的模式是单次模式或者循环模式(完成一次工作以后,是否立即开始下一次工作)。如果想把一个数组的数据转运到另一个数组,一般就要使用单次模式;如果要在ADC扫描模式并且连续转换时使用DMA转运数据,就要配合ADC使用循环模式。
      DMA的触发控制由M2M(Memory to Memory)位进行控制。当M2M为1时,执行软件触发;当M2M为0时,执行特定通道的硬件触发。需要特别注意的是,这里的软件触发与之前ADC、外部中断等的软件触发的定义不同,这里的软件触发的逻辑是:以最快的速度,连续不断地连续触发DMA,直到传输计数器为0为止,即连续触发。很显然,软件触发和DMA的循环模式不能同时使用,否则DMA将无法停止工作。软件触发一般完成从存储器到存储器的数据转运,于此对应,硬件触发一般完成从外设到存储器的数据转运,原因是该种转运时需要转运的数据一般会在一个特定的时机来临,但是在编程时程序员并不知道这个时机具体何时到来,例如ADC完成一次转换,串口USART收到数据,定时器达到一定的时间等。
      图中右下角还有一个“开关控制”位,它指在编程时使用的DMA_Cmd()函数。DMA执行数据转运时,必须满足以下三个条件:

    1. DMA_Cmd()函数必须设置为使能ENABLE
    2. 传输计数器不能为0;
    3. 一定要有触发信号。

      当传输计数器自减到0后,并且在单次模式下(自动重装器不会自动重装),无论是否触发,DMA都不会工作。此时如果要重新开启DMA,就需要在DMA_Cmd函数中配置DISABLE,关闭DMA,之后手动修改传输计数器的值,再通过DMA_Cmd函数开启DMA。注意:在对传输计数器进行写操作时必须先关闭DMA,之后再对传输计数器进行写操作,即不能在DMA工作时手动更改传输计数器的值,这是STM32关于DMA使用的规定

    2.3 DMA使用时的细节问题

    2.3.1 DMA请求


      上图展示了DMA1的7个通道的触发源映像,默认编号较小的通道优先级较高(优先级也可以手动重新配置,但在这里区别不是很大,仅作简单了解即可)。每个通道都可以通过一个数据选择器配置为硬件触发或者软件触发。但是上图的数据选择器的画法与常规的数据选择器稍有出入,数据选择器下方的EN位不是通道的选择控制端,这里应该指控制该数据选择器是否工作的端口。
      每个触发器都可以配置为软件触发,如上图所示,是否选择软件触发由M2M位控制,当M2M位为1时该通道配置为软件触发,当M2M为0时软件触发通道关闭,可以配置为硬件触发。
      当需要配置某通道的触发源为硬件触发时,需要注意每个通道可选择的硬件触发源都是固定的,换言之,如果想要选择某个硬件触发源,就必须选择与该触发源相对应的DMA通道。对每个通道而言,具体选择哪个通道,需要由对应的外设是否开启了DMA输出来决定的,并不由DMA决定。如果使用ADC1时进行数据转运,就要在ADC的库函数ADC_DMACmd()中开启DMA1的通道1输出,其他外设同理。

    2.3.2 数据宽度与对齐要求


      当两个数据执行数据转运时,如果数据宽度相同,则数据正常转运。但是如果数据宽度不同,就可能会对数据转运的宽度造成影响。上表展示了不同传输宽度不同时对数据造成的影响(均以执行4次转运为例),简单而言:如果低宽度转运到高宽度,在每个目标单位的高位补0;如果高宽度转运到低宽度,源单位的高位数据将丢失,与uint8_t,uint16_t,uint32_t类型的数据相互赋值的规则类似。

    三、DMA应用实例

    3.1 DMA实现数据转运

    3.2 DMA+AD多通道数据转运


      持续更新完善中……(博主开学了,课业也很重,之后会尽力抽时间更新的哈)


      原创笔记,码字不易,欢迎点赞,收藏~ 如有谬误敬请在评论区不吝告知,感激不尽!博主将持续更新有关嵌入式开发、机器学习方面的学习笔记~

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32学习笔记(九)| DMA直接存储器存取详解

    发表评论