• STM32实战总结:HAL之DMA


    什么是DMA?

    “DMA”是Direct Memory Access的缩写。不使用CPU,而是通过总线直接进行外围功能(模拟功能、通信功能等)和存储器间(闪存、ROM、RAM)的数据传输的功能。

    通常,数据传输由CPU执行,而在装有DMA的微型计算机中,DMA代表CPU传输数据。

    在这里插入图片描述

    因此,CPU只需要算术/逻辑运算等CPU才能完成的工作就可以了。其结果是,通过安装DMA,可以综合提高微型计算机的性能。

    DMA的最大优势是通过硬件直接传输数据,从而实现高速、大容量的数据传输。您可以在内存和外围功能中自由选择传输源和传输目的地(但受微型计算机的限制)。

    但是,由于只有一条总线和CPU分开使用,所以需要调整总线的使用权。这种“总线调整”在英语中被称为“总线仲裁(Bus Arbitration)”。

    DMA的基本操作

    在这里插入图片描述
    这是从RAM中提取数据并将其发送到通信功能的情况。通常,当CPU进行数据传输时(图(a)),首先从RAM中读取数据。读取的数据一旦通过CPU中的ALU。然后,ALU直接输出数据,而不对数据进行任何处理,并将数据发送到通信功能。DMA在不通过CPU的情况下从RAM读取数据并将其传输到通信功能。
    此时,CPU不会被使用,所以您可以使用ALU进行另一个计算。作为微型计算机,可以并行处理两项工作,非常高效。

    首先,CPU在DMA中设置数据数量、源/目的地地址、传输模式等。然后,当DMA传输开始触发时,DMA开始传输。DMA传输启动的触发器可以通过软件或硬件触发。DMA传输结束后,DMA向CPU发出中断,通知传输结束。

    仲裁类型(与CPU共享总线权限)
    DMA很方便但不是万能的。只有一条总线,所以当DMA使用总线时,CPU就不能使用总线了。如果DMA长时间使用总线来进行大量数据传输,CPU会在此期间一直无法使用总线,从而导致无法将计算结果存储在内存中的问题。因此,需要在CPU和DMA之间高效地使用总线进行调整。这叫做总线仲裁。
    还有,使用总线的权利叫做总线权利。总线仲裁是协调(调解)谁拥有总线的权利。

    总线仲裁的方式有几种。典型的是Round-Robin,Cycle stealing,burst。

    Round-Robin是按顺序让出总线权的方式。例如,如果总线主机是两个,如果CPU使用总线,下一个总线循环使用DMA,然后使用总线交替使用CPU使用。

    被称为Cycle stealing的方式是在CPU没有访问内存的总线周期之间涂上,DMA使用总线的方式。

    burst方式是指在一定时间内,一个bus Master占有总线权的方式。当你想快速传输优先级高的数据时使用。例如,当DMA发送10个高优先级数据时,DMA会占用总线,直到传输完所有这10个数据为止。

    使用上的注意事项
    举两个使用DMA时出现的问题的例子。一个是超限运行,另一个是与高速缓存一起使用时发生的主内存数据的丢失。

    由于通信功能等原因,CPU或DMA在CPU或DMA未读取接收缓冲器中的数据时,会捕获下一个数据,导致前一个接收数据丢失。如果总线仲裁是Round-Robin方式,则很难发生超限运行,但如果是Cycle stealing或burst方式,CPU或DMA就不能使用总线,等待总线权的时间就会变得更长。在此期间,通信功能在接收到以下数据时会出现超限运行。在其他情况下也会发生超限。

    在使用高速缓存的系统中,当DMA重写具有与高速缓存所具有的数据相同地址的主存储器时,高速缓存和主存储器的数据的一致性将丢失。

    例如,当CPU将数据写入内存时,如果采用直通方式,则将数据写入高速缓存,同时也将相同的数据写入主内存。如果DMA错误地重写了主内存上的数据,则缓存数据和主内存数据将不同。之后,当CPU读取该数据时,缓存中的数据将被读取,并读取与主内存值不同的值。如果你有一个机制来保持缓存和DMA之间的一致性,但如果没有机制,用户必须管理数据的一致性。

    STM32的DMA

    关于STM32的DMA参考:STM32—DMA存储器到外设_Aspirant-GQ的博客-CSDN博客 

    以下为引用内容::


    DMA(Direct Memory Access)——直接存储器存取,就像其名称一样,DMA的主要作用是搬数据,DMA可以把数据从存储器搬到外设、从外设搬到存储器、从存储器搬到存储器。DMA的特殊之处就是搬运数据不需要占用CPU,DMA控制器包含了DMA1和DMA2,其中DMA1由7个通道,DMA2有5个通道。
     

    DMA框图

    理解其工作框图:

    在这里插入图片描述

    功能框图主要分为三部分:


    1.DMA请求
    外设如果想要通过DMA传输数据,必先给DMA控制器发送DMA请求,DMA收到请求信号之后会传回给外设一个应答信号,当外设应答后且DMA控制器收到应答信号后,就会启动DMA的传输,直至传输完毕。但DMA有2个DMA控制器,15条通道,不同的通道对应着不同的外设请求,所以必须对通道和外设请求进行一一对口,各个通道对应的外设如下:
    在这里插入图片描述

    2.通道
    要注意的是DMA共有12个独立可编程的通道,DMA1有7个、DMA2有5个,每个通道对应着不同的外设的请求,但同一时间只能接收一个。


    3.仲裁器
    当多个通道同时请求时,就要处理先后响应的问题,就像中断里的优先级分组一样。仲裁器管理DMA通道请求时主要依据俩点:
    第一判断:在DMA_CCRx 寄存器中设置有 4 个等级:非常高、高、中和低,先根据此优先级判断响应的先后,如果优先级都一样,进行第二判断。
    第二判断:比较通道的编号,编号越低优先权越高。

    DMA传输数据分析

    使用DMA,核心技术就是配置数据的传输,主要分为三点:
    1.传输的方向
    2.传输的数量
    3.传输的模式

    1.传输的方向
    DMA有三种数据传输方向:
    一:存储器——>外设
    当我们使用从存储器到外设传输时,以串口向电脑端发送数据为例。 DMA 外设寄存器的地址对应的就是串口数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储通过串口发送到电脑的数据)的地址。方向我们设置外设为目
    标地址。
    二:外设——>存储器
    当我们使用从外设到存储器传输时,以 ADC 采集为例。 DMA 外设寄存器的地址对应的就是 ADC 数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(用来接收存储 AD 采集的数据)的地址。方向我们设置外设为源地址。
    三:存储器——>存储器
    当我们使用从存储器到存储器传输时,以内部 FLASH 向内部 SRAM 复制数据为例。DMA 外设寄存器的地址对应的就是内部 FLASH(我们这里把内部 FALSH 当作一个外设来看)的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部 FLASH 的数据)的地址。方向我们设置外设(即内部 FLASH)为源地址。跟上面两个不一样的是,这里需要把 DMA_CCR 位 14: MEM2MEM:存储器到存储器模式配置为 1,启动 M2M 模式。

    2.传输的数量
    以串口向电脑发送数据为例,我们可以一次性给电脑发送很多数据,具体多少由DMA_CNDTR 配置,这是一个 32 位的寄存器,一次最多只能传输 65535 个数据。而且源和目标的数据宽度必须一致,数据宽度可以设置为8/16/32位。
    除此之外,还要设置源和目标俩边数据指针的增量模式 ,即传输完一个数据后数据指针的移动模式,是加一?还是不变?以串口向电脑发送数据为例,要发送的数据很多,每发送完一个,那么存储器的地址指针就应该加 1,而串口数据寄存器只有一个,那么外设的地址指针就固定不变。

    3.传输的模式
    数据传输的情况可以通过查询标志位或者通过中断来鉴别,每个DMA通道在DMA传输过半、传输完成、传输错误时都会有相应的标志位,如果使能相关的中断后还会产生中断。一次数据传输完成后还分俩种模式:是一次传输还是循环传输。
    一次传输: 传输一次后就停止,要想再传的话,必须关闭DMA使能后重新配置后方能继续传输。
    循环传输: 一次传输完成后又恢复第一次传输时的配置循环传输,不断重复。


    注:以下基于ADC和DAC来讲DMA。

    完成功能:

    1、通过DMA来测量电压数据;

    2、通过DMA来输出正弦波形;

    之前用的是普通的查询模式,可参考:

    STM32实战总结:HAL之ADC_路溪非溪的博客-CSDN博客

    STM32实战总结:HAL之DAC_路溪非溪的博客-CSDN博客


    配置MX

    先配置ADC的DMA。

    手册中的简介:

    ADC实现的是DMA的“从外设到存储器”

    过程是这样的,输入的模拟量经过转换,存储在ADC_DR寄存器中,转换结束时产生DMA请求,DMA经过响应等过程后,将数据运输到储存器中(在程序中体现为一个变量地址)

    开始配置:

    ADC开启连续转换。

    之前是单次转换模式,每次转换时都要重新打开ADC来采集,从而不断采集数据。设置成连续模式,则每次转换后不会关闭ADC,也就不用再次开启,而是接着转换。

    DMA Settings中配置对应的DMA。

    这里有几个地方需要注意。

    1、DMA选择循环Circular模式。什么意思呢?这里的循环模式指的是,在一次运输过后,DMA不会被关闭,而是接着运输。

    2、地址增长处,外设处是固定的寄存器,内存中就始终有一个变量去接收数据即可,都不必使能自动地址增长。

    3、ADC是12位的,存放数据的寄存器是16位的,所以单次传送的数据宽度选择半字即可。

    4、上面的优先级有Low/Medium/High/Very High,根据重要程度去选择即可。

    开始配置DAC的DMA。

    先看手册中的介绍。

    根据上方红框中的说明可知,DAC的DMA必须要由外部触发,而不是软件触发。

    另外,这里是从存储器到外设的DMA传输。

    使能外部触发:

    这几个DMA的通道选择是自动的。

    为什么是自动的?上面我们讲过,DMA的通道是固定对应着某些外设的。如下表:

    继续配置DMA选项:

    因为输出一个正弦波,我们需要传输一个数组,所以,每次传输结束后内存地址都要自动+1

    另外,既然要外部触发条件,那么,我们这里选择定时器7来触发(定时器6之前已经被用过了)。

    配置定时器7:

    先查看定时器7的框图:

    TRGO配置成一个更新事件:

    在定时器计时到了,产生更新事件时,就会触发DAC。

    最后,再配置一下中断:

    上述配置完成后,则默认会将DMA的中断进行强制打开(灰色代表是系统自行选择的),这里的两个中断的意思是,每次DMA传输完成后,就会产生一个中断通知CPU。因为DMA传输很快,又开了循环模式,所以如果开中断,将会不断地去通知CPU,导致无效地占用CPU。所以,从性能角度来考虑,将这两个中断取消。

    先将上方强制选择的选项取消勾选,然后再取消勾选这两个。

    注意:TIM7的时钟可以去掉。因为其是触发事件,没用到中断。

    至此,完成了ADC和DAC的DMA配置。

    在DMA菜单中,能看到配置的两个DMA信息

    相对来说,还是比较麻烦的。

    确认无误后,生成代码。

    DMA相关库函数

    ADC相关库函数

    1. /* Non-blocking mode: DMA */
    2. HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
    3. HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc);

    DAC相关库函数

    1. HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel, uint32_t *pData, uint32_t Length, uint32_t Alignment);
    2. HAL_StatusTypeDef HAL_DAC_Stop_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel);

    这里面的*pData就是变量的指针,也就是DMA传输方向中的存储器。

    程序编写

    代码生成后,除了ADC和DAC相关文件,还多了dma.c和dma.h文件。

    使用DMA函数来开启ADC。这里主要是要定义一个变量(内存),然后将数据放到变量中。

    直接在之前的useracd.c里面修改。

    1. static void GetVol(void)
    2. {
    3. uint32_t adcResultNum = 0;
    4. HAL_ADC_Start_DMA(&hadc1, &adcResultNum, 1);
    5. HAL_Delay(1000);
    6. printf("value is %f\n\r", 3.3/4096*adcResultNum);
    7. }

    至于DAC:

    定时器7启动;

    DAC启动,传入要传输的变量的地址;

    硬件自动完成,定时器7每100us会产生一个更新事件,同时会触发DMA,所以DAC每100us会输出一个数。具体可用示波器查看。此处代码略过。

  • 相关阅读:
    基于粒子群优化算法的冷热电联供型综合能源系统运行优化附Matlab代码
    铜九铁十快来上分:vite+ts+vue3搭建你的组件库
    AnatoMask论文汇总
    Vue3+Element-Plus项目 el-table 拖拽排序实现,Vue3项目sortablejs的安装与使用
    Asahi Linux 采用开源驱动,在 Apple M1 下运行首个三角形渲染
    TCP/IP Illustrated Episode 7
    7 Spring Boot 整合 Spring Data JPA
    数组补全(秋季每日一题 10)
    MySQL数据类型之JSON
    Postman接口测试 —— 设置断言和集合运行
  • 原文地址:https://blog.csdn.net/qq_28576837/article/details/126851725