【正点原子STM32】DMA直接存储器访问(DMA传输、DMA请求、DMA通道、DMA优先级、DMA处理过程、DMA相关寄存器和HAL库驱动、DMA配置步骤、DMAMUX DMA请求复用器)

一、DMA介绍
二、DMA结构框图介绍

三、DMA相关寄存器介绍

四、DMA相关HAL库驱动介绍

五、DMA配置步骤

六、编程实战
七、总结

一、DMA介绍

在这里插入图片描述
DMA(直接存储器访问):

DMA是一种计算机系统中的重要技术,它允许数据在内存和外设之间直接传输,而无需CPU的直接干预。这种直接传输的方式可以显著提高数据传输的效率,因为它可以在数据传输过程中不占用CPU的时间,从而使CPU能够专注于执行其他任务。

一些额外的信息包括:

  1. 工作原理
    DMA工作原理是在CPU控制下配置好DMA的参数后,DMA控制器会独立地从一个地址空间(通常是内存)中获取数据,并将数据传输到另一个地址空间(可以是内存或者外设)。整个传输过程无需CPU的干预,只有在传输完成或者出错时,DMA才会通过中断或者其他方式通知CPU。

  2. 优点
    DMA的主要优点是能够显著减轻CPU的负担,提高系统的整体性能。它可以加速大量数据的传输,同时释放CPU用于执行其他任务,从而提高系统的并行处理能力和效率。

  3. 应用场景
    DMA广泛应用于需要大量数据传输的场景,比如网络通信、存储设备读写、音视频处理等。在这些场景中,DMA可以大幅提高系统的数据处理速度,同时降低CPU的负载。

  4. 注意事项
    虽然DMA可以提高系统的性能,但在使用时需要注意DMA传输过程中的数据一致性和安全性问题。由于DMA是直接访问内存的,因此需要合理管理数据的读写顺序和保证数据的正确性,以避免出现数据损坏或者不一致的情况。

总的来说,DMA是一种强大的数据传输技术,能够有效提高系统的性能和效率,但在应用中需要合理使用,并注意处理好相关的数据一致性和安全性问题。

二、DMA结构框图介绍

2.1、STM32F1 DMA框图

在这里插入图片描述
STM32F1系列的DMA(Direct Memory Access)结构包括以下关键部分:

  1. DMA请求:

    • DMA传输数据之前,外设需要向DMA控制器发送请求,以启动数据传输过程。
  2. DMA通道:

    • DMA通道是DMA控制器中用于处理不同外设请求的通道。
    • STM32F1系列中,DMA1有7个通道,而DMA2有5个通道。每个通道可以与一个外设相连,接收其数据传输请求。
  3. DMA优先级:

    • 当多个DMA通道同时发出请求时,需要确定响应处理的优先级顺序。
    • DMA控制器中的仲裁器负责管理DMA通道的优先级,以确保数据传输的顺序性和有效性。
    • 优先级管理可以在软件阶段进行设置,也可以在硬件阶段进行配置。

需要注意的是,STM32F1系列中的DMA2仅存在于某些大容量产品和互联型产品中,而不是所有的STM32F1芯片都具有DMA2。
在这里插入图片描述

DMA处理过程

DMA(Direct Memory Access)处理过程大致如下:

  1. 外设发送请求

    • 外设希望通过DMA进行数据传输时,会向DMA控制器发送请求。
  2. DMA控制器确认请求

    • DMA控制器收到外设的请求后,会向外设发送确认信号(ACK),表示已经收到请求。
  3. 外设释放请求

    • 在收到DMA控制器的确认信号后,外设会释放请求,允许DMA控制器进行数据传输。
  4. 数据传输

    • DMA控制器根据外设发送的请求,启动数据传输过程。
    • 数据传输包括从源地址(通常是外设的数据寄存器)读取数据,并将其写入目标地址(通常是内存或另一个外设的数据寄存器)。
    • 传输的数据量由DMA控制器提前配置好,通常是指定要传输的字节数或数据块的数量。
  5. 传输完成

    • 一旦传输完成,DMA控制器会发送信号通知外设和CPU传输已经完成。

这个过程中,CPU不需要直接参与数据传输的过程,而是可以继续执行其他任务,从而提高了系统的效率。DMA对于大量数据的传输以及需要高速响应的应用场景非常有用。

DMA通道

DMA通道在STM32中用于管理外设对存储器访问的请求,每个通道可以处理来自一个或多个外设的请求。DMA通道的数量取决于具体的STM32系列和型号,在STM32中通常有多个DMA通道可供选择。

每个DMA通道都有一个仲裁器(Arbiter),用于处理DMA请求之间的优先级。当多个DMA通道同时收到请求时,仲裁器会根据通道的优先级确定哪个通道优先处理请求。通常,可以通过软件配置DMA通道的优先级,也可以在硬件层面实现对DMA请求的优先级管理。这种方式可以确保DMA传输按照特定的优先级顺序进行,从而更好地满足系统的要求。
在这里插入图片描述

DMA优先级

在STM32中,DMA优先级的管理分为两个阶段:软件阶段和硬件阶段。

  1. 软件阶段:在软件阶段,每个DMA通道的优先级可以在相应的DMA_CCRx寄存器中进行设置。通常情况下,有四个优先级等级可供选择:最高优先级、高优先级、中优先级和低优先级。通过设置这些优先级等级,可以确保DMA通道在处理请求时按照指定的优先级顺序进行。

  2. 硬件阶段:如果多个DMA通道的请求具有相同的软件优先级,则会进入硬件阶段。在硬件阶段,仲裁器会对DMA请求进行优先级排序。通常情况下,如果两个请求具有相同的软件优先级,则具有较低编号的DMA通道会被赋予较高的优先级。这意味着较低编号的DMA通道会优先获得DMA控制器的处理权。

需要注意的是,多个DMA请求通过逻辑或门输入到DMA控制器时,只能有一个请求能够被有效处理。因此,在设置DMA优先级时,需要确保合适的优先级设置以及适当的DMA请求触发条件,以便系统能够按照预期的方式处理DMA传输请求。

2.2、F4 / F7 / H7 DMA结构框图

在这里插入图片描述
在这里插入图片描述

F4 F7 H7 DMA功能

在这里插入图片描述
在STM32F4和F7系列中,DMA控制器主要是通过DMA1和DMA2来实现数据传输。它们支持以下功能:

  1. DMA1:

    • 内存到外设(Memory to Peripheral)传输
    • 外设到内存(Peripheral to Memory)传输
  2. DMA2:

    • 内存到内存(Memory to Memory)传输
    • 内存到外设(Memory to Peripheral)传输
    • 外设到内存(Peripheral to Memory)传输

而在STM32H7系列中,DMA功能更加强大,具有更多的功能和灵活性:

  1. D1域(MDMA):

    • 内存到内存(Memory to Memory)传输
    • 内存到外设(Memory to Peripheral)传输
    • 外设到内存(Peripheral to Memory)传输
  2. D2域(DMA1和DMA2):

    • 内存到内存(Memory to Memory)传输
    • 内存到外设(Memory to Peripheral)传输
    • 外设到内存(Peripheral to Memory)传输
  3. D3域(BDMA):

    • 内存到内存(Memory to Memory)传输
    • 内存到外设(Memory to Peripheral)传输
    • 外设到内存(Peripheral to Memory)传输
    • 外设到外设(Peripheral to Peripheral)传输

此外,H7系列的DMA控制器还具有双缓冲模式、突发传输等功能,以提高数据传输的效率和性能。通道请求的灵活性也得到了增强,使得DMA在处理各种数据传输任务时更加灵活和高效。
在这里插入图片描述
在STM32F4/F7的DMA结构中,主要包括以下几个关键部分:

  1. AHB从器件编程接口:

    • DMA相关的控制寄存器通过这个接口设置,用于配置DMA的工作模式、数据传输方向等。
  2. 存储器端口和外设端口:

    • DMA控制器有两个端口,一个用于存储器访问,另一个用于外设访问。这两个端口分别用于处理从存储器到外设或从外设到存储器的数据传输。
  3. FIFO(First In, First Out):

    • DMA控制器内部有一个FIFO,通常包含4级32位存储器缓冲区。这个FIFO用作源数据传输到目标地址前的临时存储区。可以配置为FIFO模式或直接模式。在FIFO模式下,软件设置阈值,当FIFO达到阈值时触发数据传输;在直接模式下,立即启动对存储器的传输。
  4. DMA优先级(数据流优先级):

    • DMA控制器中的每个数据流都有一个优先级。在软件阶段,可以通过寄存器配置优先级,通常分为非常高、高、中、低四个等级。在硬件阶段,较低编号的数据流具有较高的优先级。
  5. DMA请求:

    • 每个数据流与一个DMA请求相关联,可以从8个通道中选出DMA请求。这些请求来自于外设,表示外设需要进行数据传输操作。

通过以上结构,DMA控制器能够高效地管理数据传输,减轻CPU的负担,提高系统性能。
在这里插入图片描述
在STM32F4/F7中,DMA请求管理主要涉及以下几个关键点:

  1. DMA_SxCR控制数据流使用通道:

    • DMA控制寄存器(如DMA_SxCR)用于配置数据流的工作模式、数据传输方向、数据大小等参数。每个数据流都对应一个通道,通过配置这些寄存器,可以控制数据流的行为。
  2. 外设请求占用通道:

    • 每个外设请求都会占用一个DMA通道,当外设需要进行数据传输时,它会发送一个请求,并占用一个特定的DMA通道。相同外设的不同请求可以占用不同的数据流通道。
  3. 请求占用通道的限制:

    • 当某个外设请求使用了某个数据流的通道时,该数据流的其他通道就不能被选择,变为不可用状态。这意味着如果一个外设需要使用多个通道进行数据传输,就需要考虑通道的分配和冲突问题。

通过DMA请求的管理,可以有效地分配和利用DMA通道,确保各个外设之间的数据传输能够顺利进行,提高系统的整体性能。
在这里插入图片描述
在STM32H7中,DMA请求管理主要由DMAMUX(DMA Multiplexer)模块负责,具体结构如下:

  1. DMA请求的分配:

    • 数据流0~7的通道请求由DMAMUX1管理,每个数据流都有多达115个通道请求可供选择。
    • H7具有两个DMAMUX模块,其中DMAMUX1负责DMA1/2,而DMAMUX2负责BDMA(Basic DMA)。
  2. DMAMUX的作用:

    • DMAMUX用于管理DMA通道的请求,它能够将外设的请求与DMA通道进行有效地映射,以满足不同外设对DMA传输的需求。
    • 通过DMAMUX的灵活配置,可以实现不同数据流通道与外设请求之间的映射关系,从而灵活地分配DMA资源,提高系统的数据传输效率。
  3. 多通道请求选择:

    • 每个数据流通道可以选择多达115个通道请求,这意味着系统可以同时处理多个外设的数据传输请求,从而实现并行传输,提高系统整体性能。

通过DMAMUX的灵活配置和管理,STM32H7能够高效地处理来自多个外设的DMA请求,并且能够满足复杂应用场景下的数据传输需求。
在这里插入图片描述
DMAMUX(DMA Multiplexer)DMA请求复用器在STM32H7中起着关键的作用,其主要特点和功能如下:

  1. 外设请求

    • DMAMUX接收来自外设的DMA请求信号,这些请求信号可能来自STM32H7芯片内部的不同外设模块,共有107个外设请求信号可供选择。
  2. 通道选择

    • DMAMUX具有用于选择DMA请求通道的功能,通过配置DMAMUX,可以将外设的DMA请求信号映射到DMA控制器的具体通道上。
  3. 同步控制

    • DMAMUX还提供了同步控制功能,用于同步DMA请求信号。这个功能可以在一定程度上优化DMA传输的效率,但在某些情况下可能不需要使用,可以关闭以节省系统资源。
  4. 请求信号输出

    • DMAMUX最终将经过选择和同步的DMA请求信号输出给DMA控制器,由DMA控制器根据接收到的信号进行数据传输操作。
  5. 通道映射

    • 对于DMAMUX1而言,共有15个通道可供选择,分别对应DMA1的通道07和DMA2的通道07,共计15个通道。
    • 通道07用于DMA1,通道815用于DMA2。
    • 对于DMAMUX2,通道数和通道映射情况可能有所不同,具体取决于其管理的DMA模块(比如BDMA)。

通过DMAMUX的灵活配置,可以有效管理和优化系统中的DMA请求信号,实现不同外设模块之间的数据传输需求,并且提高系统整体的性能和效率。

三、DMA相关寄存器介绍

3.1、DMA相关寄存器 F1

在这里插入图片描述
以下是STM32F1系列中与DMA相关的寄存器介绍:

  1. DMA_CCRx:DMA通道x配置寄存器。用于配置DMA通道的工作模式、传输方向、数据大小、内存和外设增量模式、循环模式等参数。每个DMA通道都有一个对应的CCR寄存器。

  2. DMA_ISR:DMA中断状态寄存器。用于查询当前DMA传输的状态,包括传输完成、半传输完成、传输错误等状态。

  3. DMA_IFCR:DMA中断标志清除寄存器。用来清除DMA_ISR寄存器中的中断标志位,以便重新开始新的DMA传输。

  4. DMA_CNDTRx:DMA通道x传输数量寄存器。用于设置DMA通道x每次传输的数据量,即传输的数据数量。

  5. DMA_CPARx:DMA通道x外设地址寄存器。用于存储与DMA通道x相关联的外设的地址,指定数据的来源或目的地。

  6. DMA_CMARx:DMA通道x存储器地址寄存器。用于存放与DMA通道x相关联的存储器的地址,指定数据的来源或目的地。

  7. USART_CR3:USART控制寄存器3。用于配置USART串口的工作模式,其中包括使能串口DMA发送功能,允许USART通过DMA进行数据发送。

这些寄存器在DMA控制和配置过程中起着关键作用,通过合理设置这些寄存器的值,可以实现灵活高效的DMA数据传输操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2、F4 / F7 / H7 DMA相关寄存器

在这里插入图片描述
以下是F4/F7/H7系列的DMA相关寄存器介绍:

F4/F7/H7 DMA寄存器:

  1. DMA_SxCR(DMA数据流x配置寄存器):

    • 用于配置DMA数据流的控制参数,包括传输方向、数据大小、传输模式、循环模式等。
  2. DMA_(H/L)ISR(DMA(低/高)中断状态寄存器):

    • 用于查询当前DMA传输状态和中断标志,包括传输完成、传输错误等。
  3. DMA_(H/L)IFCR(DMA(低/高)中断标志清除寄存器):

    • 用来清除DMA_(H/L)ISR中对应的中断标志位。
  4. DMA_SxNDTR(DMA数据流x传输数量寄存器):

    • 用于设置DMA数据流x每次传输的数据量,即传输的数据大小。
  5. DMA_SxPAR(DMA数据流x外设地址寄存器):

    • 用于存储外设的地址,DMA将从这个地址读取数据进行传输。
  6. DMA_SxM0AR(DMA数据流x存储器地址寄存器):

    • 用于存放存储器的地址,DMA将把数据传输到这个地址。

H7 DMAMUX寄存器:

对于H7系列的DMAMUX,只需要了解DMAMUX请求发生器通道x配置寄存器的使用即可,具体寄存器名称和作用可能与DMA控制器的寄存器有所不同,但作用是用于配置DMAMUX通道和DMA控制器之间的连接关系,从而实现DMA请求的管理和分配。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、DMA相关HAL库驱动介绍

4.1、DMA相关HAL库驱动 F1

在这里插入图片描述
以下是STM32F1系列中与DMA相关的HAL库驱动介绍:

  1. __HAL_RCC_DMAx_CLK_ENABLE(...): 该函数用于使能DMAx时钟,其中x表示DMA控制器的编号。通过该函数可以启用对应DMA控制器的时钟。

  2. HAL_DMA_Init(...): 该函数用于初始化DMA传输通道。通过配置DMA_InitTypeDef结构体中的参数,可以初始化DMA通道的工作模式、传输方向、数据大小、内存和外设增量模式等。

  3. HAL_DMA_Start_IT(...): 该函数用于启动DMA传输,并通过中断方式进行传输。它需要配置DMA传输通道的相关寄存器,包括CCR寄存器、CPAR寄存器、CMAR寄存器和CNDTR寄存器。

  4. __HAL_LINKDMA(...): 该函数用于将DMA传输通道与外设句柄连接起来,以便外设能够通过DMA进行数据传输。

  5. HAL_UART_Transmit_DMA(...): 该函数用于启用USART串口的DMA发送功能,并启动数据传输。它需要配置DMA传输通道的相关寄存器以及USART的CR3寄存器,以使能DMA发送。

  6. __HAL_DMA_GET_FLAG(...): 该函数用于查询DMA传输通道的状态,可以检查DMA_ISR寄存器中的中断标志位,以确定DMA传输是否完成或出现错误。

  7. __HAL_DMA_ENABLE(...)__HAL_DMA_DISABLE(...): 这两个函数分别用于使能和失能DMA外设。它们通过设置DMA通道的CCR寄存器中的EN位来控制DMA外设的使能状态。

此外,还有两个与DMA相关的结构体:DMA_HandleTypeDefDMA_InitTypeDef。DMA_HandleTypeDef结构体用于存储DMA传输通道的相关信息,而DMA_InitTypeDef结构体用于配置DMA传输通道的初始化参数。
在这里插入图片描述

4.2、F4 / F7 / H7 DMA相关HAL库驱动

在这里插入图片描述
以下是F4/F7/H7系列的DMA相关HAL库驱动函数介绍:

F4/F7/H7 DMA HAL库驱动:

  1. __HAL_RCC_DMAx_CLK_ENABLE(…)

    • 关联寄存器:RCC_AHBENR
    • 功能描述:使能指定的DMA时钟。
  2. HAL_DMA_Init(…)

    • 关联寄存器:DMA_SxCR
    • 功能描述:初始化DMA数据流配置,包括传输方向、数据大小、传输模式等参数的设置。
  3. HAL_DMA_Start_IT(…)

    • 关联寄存器:DMA_SxCR、DMA_SxPAR、DMA_SxM0AR、DMA_SxNDTR
    • 功能描述:开始DMA传输,并启用中断进行传输完成等事件的处理。
  4. __HAL_LINKDMA(…)

    • 功能描述:用来连接DMA和外设句柄,建立DMA传输的关联关系。
  5. HAL_UART_Transmit_DMA(…)

    • 关联寄存器:DMA_SxCR、DMA_SxPAR、DMA_SxM0AR、DMA_SxNDTR、USART_CR3
    • 功能描述:使能DMA发送,并启动传输,通常用于串口数据传输。
  6. __HAL_DMA_GET_FLAG(…)

    • 关联寄存器:DMA_ISR
    • 功能描述:查询DMA传输通道的状态,例如传输完成标志等。
  7. __HAL_DMA_ENABLE(…)

    • 关联寄存器:DMA_CCR(EN)
    • 功能描述:使能指定的DMA外设。
  8. __HAL_DMA_DISABLE(…)

    • 关联寄存器:DMA_CCR(EN)
    • 功能描述:失能指定的DMA外设。
  9. HAL_UART_DMAStop(…)

    • 关联寄存器:USART_CR3
    • 功能描述:停止DMA传输,通常用于串口DMA传输的停止。

在这里插入图片描述

五、DMA配置步骤

5.1、以DMA方式传输串口数据配置步骤 F1

在这里插入图片描述
以下是使用DMA方式传输串口数据的配置步骤:

  1. 使能DMA时钟:
    使用 __HAL_RCC_DMA1_CLK_ENABLE() 函数使能DMA1的时钟,确保DMA功能正常运行。

  2. 初始化DMA:
    使用 HAL_DMA_Init() 函数初始化DMA传输通道,配置DMA_InitTypeDef结构体中的参数,包括DMA模式、数据方向、数据大小、传输地址等。

  3. 连接DMA和外设:
    使用 __HAL_LINKDMA() 函数将DMA传输通道与串口外设连接起来,以确保外设能够通过DMA进行数据传输。

  4. 使能串口的DMA发送并启动传输:
    使用 HAL_UART_Transmit_DMA() 函数使能串口的DMA发送功能,并启动数据传输。该函数需要配置DMA传输通道的相关寄存器以及串口的CR3寄存器,以启用DMA发送。

  5. 查询DMA传输状态:

    • 使用 __HAL_DMA_GET_FLAG() 函数查询DMA传输通道的状态,以确定传输是否完成或出现错误。
    • 使用 __HAL_DMA_GET_COUNTER() 函数获取当前传输剩余数据量,以便监测传输进度。
  6. DMA中断使用:

    • 使用 HAL_NVIC_EnableIRQ() 函数使能DMA传输通道的中断,并设置优先级。
    • 编写中断服务函数,例如 xxx_IRQHandler(),用于处理DMA传输完成或错误的中断事件。

通过以上步骤,可以实现串口数据的DMA传输,并通过中断方式进行传输状态的监测和处理。

5.2、F4 / F7/ H7 DMA配置步骤

以下是F4/F7/H7系列的DMA配置步骤:

  1. 使能DMA时钟:

    __HAL_RCC_DMAx_CLK_ENABLE();
    
    • 使用该宏使能所需的DMA时钟。
  2. 初始化DMA:

    HAL_DMA_Init(&hdma); // hdma为DMA_HandleTypeDef类型的结构体变量,其中包含DMA的配置参数
    
    • 使用HAL_DMA_Init函数初始化DMA相关参数,包括传输方向、数据大小、传输模式等。
  3. 连接DMA和外设:

    __HAL_LINKDMA(huart, hdmatx, hdma_tx); // huart为UART_HandleTypeDef类型的结构体变量,hdmatx为外设的DMA传输句柄,hdma_tx为DMA的句柄
    
    • 使用__HAL_LINKDMA函数连接DMA和外设,建立DMA传输的关联关系。
  4. 使能串口的DMA发送,启动传输:

    HAL_UART_Transmit_DMA(&huart, pData, Size); // huart为UART_HandleTypeDef类型的结构体变量,pData为发送数据的指针,Size为发送数据的大小
    
    • 使用HAL_UART_Transmit_DMA函数使能串口的DMA发送,并启动数据传输。
  5. 查询DMA传输状态:

    __HAL_DMA_GET_FLAG(hdma, DMA_FLAG_TC); // hdma为DMA_HandleTypeDef类型的结构体变量,DMA_FLAG_TC为传输完成标志
    
    • 使用__HAL_DMA_GET_FLAG函数查询DMA传输通道的状态,例如传输完成标志等。
    __HAL_DMA_GET_COUNTER(hdma); // hdma为DMA_HandleTypeDef类型的结构体变量
    
    • 使用__HAL_DMA_GET_COUNTER函数获取当前传输剩余数据量。
  6. DMA中断使用:

    HAL_NVIC_EnableIRQ(DMAx_Streamx_IRQn); // DMAx_Streamx_IRQn为DMA中断的中断号
    HAL_NVIC_SetPriority(DMAx_Streamx_IRQn, 0, 0); // DMAx_Streamx_IRQn为DMA中断的中断号,0为抢占优先级,0为子优先级
    
    • 使用HAL_NVIC_EnableIRQ函数使能DMA中断。
    • 使用HAL_NVIC_SetPriority函数配置DMA中断的优先级。

    编写中断服务函数:

    void DMAx_Streamx_IRQHandler(void)
    {
        HAL_DMA_IRQHandler(&hdma); // hdma为DMA_HandleTypeDef类型的结构体变量
    }
    
    • 编写DMA中断服务函数,处理DMA中断事件。

六、编程实战

在这里插入图片描述
在这里插入图片描述

F1 按键输入

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/KEY/key.h"
#include "string.h"

DMA_HandleTypeDef g_dma_handler;

uint8_t src_buf[10] = {0x0a, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
uint8_t dest_buf[10] = {0};

/* 内存到内存 DMA传输 */
void dma_init(void)
{
    /* 使能DMA1时钟 */
    __HAL_RCC_DMA1_CLK_ENABLE();
    
    g_dma_handler.Instance = DMA1_Channel1;
    g_dma_handler.Init.Direction = DMA_MEMORY_TO_MEMORY;
    
    /* 与目标地址相关 */
    g_dma_handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    g_dma_handler.Init.MemInc = DMA_MINC_ENABLE;

    /* 与源地址相关 */
    g_dma_handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    g_dma_handler.Init.PeriphInc = DMA_PINC_ENABLE;
    
    g_dma_handler.Init.Priority = DMA_PRIORITY_HIGH;
    g_dma_handler.Init.Mode = DMA_NORMAL;   /* 内存到内存是不支持循环模式 */
    
    HAL_DMA_Init(&g_dma_handler);
    HAL_DMA_Start(&g_dma_handler, (uint32_t)src_buf, (uint32_t)dest_buf, 0);
}

void dma_enable_transmit(uint16_t cndtr)
{
    __HAL_DMA_DISABLE(&g_dma_handler);
    
//    DMA1_Channel1->CNDTR = cndtr;
    g_dma_handler.Instance->CNDTR = cndtr;
    
    __HAL_DMA_ENABLE(&g_dma_handler);
}

int main(void)
{
    uint8_t key;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    delay_init(72);                         /* 延时初始化 */
    usart_init(115200);						/* 串口初始化 */
    led_init();                             /* 初始化LED */
    key_init();                             /* 初始化按键 */
    beep_init();                            /* 初始化蜂鸣器 */
    LED0(0);                                /* 先点亮LED0 */
    dma_init();								/* DMA初始化 */
    
    while(1)
    {
        key = key_scan(0);                  /* 得到键值 */
        
        if (key == KEY0_PRES)
        {
            memset(dest_buf, 0, 10);
            dma_enable_transmit(10);
            
            while(1)
            {
                if (__HAL_DMA_GET_FLAG(&g_dma_handler, DMA_FLAG_TC1))
                {
                    __HAL_DMA_CLEAR_FLAG(&g_dma_handler, DMA_FLAG_TC1);
                    printf("传输完成 \r\n");
                    break;
                }
            }
        }

        LED0_TOGGLE();
        delay_ms(200);
    }
}

在这里插入图片描述

F1_HAL实验17 DMA

dma.c

#include "./BSP/DMA/dma.h"
#include "./SYSTEM/delay/delay.h"

DMA_HandleTypeDef  g_dma_handle;            /* DMA句柄 */
extern UART_HandleTypeDef g_uart1_handle;   /* UART句柄 */


/**
 * @brief       串口TX DMA初始化函数
 *   @note      这里的传输形式是固定的, 这点要根据不同的情况来修改
 *              从存储器 -> 外设模式/8位数据宽度/存储器增量模式
 *
 * @param       dmax_chy    : DMA的通道, DMA1_Channel1 ~ DMA1_Channel7, DMA2_Channel1 ~ DMA2_Channel5
 *                            某个外设对应哪个DMA, 哪个通道, 请参考<<STM32中文参考手册 V10>> 10.3.7节
 *                            必须设置正确的DMA及通道, 才能正常使用! 
 * @retval      无
 */
void dma_init(DMA_Channel_TypeDef* DMAx_CHx)
{
    if ((uint32_t)DMAx_CHx > (uint32_t)DMA1_Channel7)     /* 大于DMA1_Channel7, 则为DMA2的通道了 */
    {
        __HAL_RCC_DMA2_CLK_ENABLE();                      /* DMA2时钟使能 */
    }
    else 
    {
        __HAL_RCC_DMA1_CLK_ENABLE();                      /* DMA1时钟使能 */
    }
    
    __HAL_LINKDMA(&g_uart1_handle, hdmatx, g_dma_handle);           /* 将DMA与USART1联系起来(发送DMA) */
    
    /* Tx DMA配置 */
    g_dma_handle.Instance = DMAx_CHx;                               /* USART1_TX使用的DMA通道为: DMA1_Channel4 */
    g_dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;             /* DIR = 1 , 存储器到外设模式 */
    g_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;                 /* 外设非增量模式 */
    g_dma_handle.Init.MemInc = DMA_MINC_ENABLE;                     /* 存储器增量模式 */
    g_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;    /* 外设数据长度:8位 */
    g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;       /* 存储器数据长度:8位 */
    g_dma_handle.Init.Mode = DMA_NORMAL;                            /* 外设流控模式 */
    g_dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;               /* 中等优先级 */

    HAL_DMA_Init(&g_dma_handle);
}

dma.h

#ifndef __DMA_H
#define	__DMA_H

#include "./SYSTEM/sys/sys.h"

void dma_init(DMA_Channel_TypeDef* DMAx_CHx);
    
#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/DMA/dma.h"


const uint8_t TEXT_TO_SEND[] = {"正点原子 STM32 DMA 串口实验"}; /* 要循环发送的字符串 */
#define SEND_BUF_SIZE       (sizeof(TEXT_TO_SEND) + 2) * 200    /* 发送数据长度, 等于sizeof(TEXT_TO_SEND) + 2的200倍. */

uint8_t g_sendbuf[SEND_BUF_SIZE];           /* 发送数据缓冲区 */
extern DMA_HandleTypeDef  g_dma_handle;     /* DMA句柄 */
extern UART_HandleTypeDef g_uart1_handle;   /* UART句柄 */

int main(void)
{
    uint8_t  key = 0;
    uint16_t i, k;
    uint16_t len;
    uint8_t  mask = 0;
    float pro = 0;          /* 进度 */

    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    led_init();                         /* 初始化LED */
    lcd_init();                         /* 初始化LCD */
    key_init();                         /* 初始化按键 */
    dma_init(DMA1_Channel4);            /* 初始化串口1 TX DMA */

    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "DMA TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "KEY0:Start", RED);

    len = sizeof(TEXT_TO_SEND);
    k = 0;
    
    for (i = 0; i < SEND_BUF_SIZE; i++) /* 填充ASCII字符集数据 */
    {
        if (k >= len)   /* 入换行符 */
        {
            if (mask)
            {
                g_sendbuf[i] = 0x0a;
                k = 0;
            }
            else
            {
                g_sendbuf[i] = 0x0d;
                mask++;
            }
        }
        else     /* 复制TEXT_TO_SEND语句 */
        {
            mask = 0;
            g_sendbuf[i] = TEXT_TO_SEND[k];
            k++;
        }
    }
 
    i = 0;

    while (1)
    {
        key = key_scan(0);

        if (key == KEY0_PRES)       /* KEY0按下 */
        {
            printf("\r\nDMA DATA:\r\n");
            lcd_show_string(30, 130, 200, 16, 16, "Start Transimit....", BLUE);
            lcd_show_string(30, 150, 200, 16, 16, "   %", BLUE);    /* 显示百分号 */

            HAL_UART_Transmit_DMA(&g_uart1_handle, g_sendbuf, SEND_BUF_SIZE);
            /* 等待DMA传输完成,此时我们来做另外一些事情,比如点灯  
             * 实际应用中,传输数据期间,可以执行另外的任务 
             */
            while (1)
            {
                if ( __HAL_DMA_GET_FLAG(&g_dma_handle, DMA_FLAG_TC4))   /* 等待 DMA1_Channel4 传输完成 */
                {
                    __HAL_DMA_CLEAR_FLAG(&g_dma_handle, DMA_FLAG_TC4);
                    HAL_UART_DMAStop(&g_uart1_handle);                  /* 传输完成以后关闭串口DMA */
                    break;
                }

                pro = DMA1_Channel4->CNDTR; /* 得到当前还剩余多少个数据 */
                len = SEND_BUF_SIZE;        /* 总长度 */
                pro = 1 - (pro / len);      /* 得到百分比 */
                pro *= 100;                 /* 扩大100倍 */
                lcd_show_num(30, 150, pro, 3, 16, BLUE);
            } 
            lcd_show_num(30, 150, 100, 3, 16, BLUE);    /* 显示100% */
            lcd_show_string(30, 130, 200, 16, 16, "Transimit Finished!", BLUE); /* 提示传送完成 */
        }

        i++;
        delay_ms(10);

        if (i == 20)
        {
            LED0_TOGGLE();  /* LED0闪烁,提示系统正在运行 */
            i = 0;
        }
    }
}

H750 DMA源码

dma.c

#include "./BSP/DMA/dma.h"
#include "./SYSTEM/delay/delay.h"


DMA_HandleTypeDef  g_dma_handle;                  /* DMA句柄 */

extern UART_HandleTypeDef g_uart1_handle;         /* UART句柄 */

/**
 * @brief       串口TX DMA初始化函数
 *   @note      这里的传输形式是固定的, 这点要根据不同的情况来修改
 *              从存储器 -> 外设模式/8位数据宽度/存储器增量模式
 *
 * @param       dma_stream_handle : DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
 * @retval      无
 */
void dma_init(DMA_Stream_TypeDef *dma_stream_handle, uint32_t ch)
{ 
    if ((uint32_t)dma_stream_handle > (uint32_t)DMA2)     /* 得到当前stream是属于DMA2还是DMA1 */
    {
        __HAL_RCC_DMA2_CLK_ENABLE();                      /* DMA2时钟使能 */
    }
    else 
    {
        __HAL_RCC_DMA1_CLK_ENABLE();                      /* DMA1时钟使能 */
    }

    __HAL_LINKDMA(&g_uart1_handle,hdmatx, g_dma_handle);  /* 将DMA与USART1联系起来(发送DMA) */

    /* Tx DMA配置 */
    g_dma_handle.Instance = dma_stream_handle;                    /* 数据流选择 */
    g_dma_handle.Init.Request = ch;                               /* DMA通道选择 */
    g_dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;           /* 存储器到外设 */
    g_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;               /* 外设非增量模式 */
    g_dma_handle.Init.MemInc = DMA_MINC_ENABLE;                   /* 存储器增量模式 */
    g_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;  /* 外设数据长度:8位 */
    g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;     /* 存储器数据长度:8位 */
    g_dma_handle.Init.Mode = DMA_NORMAL;                          /* 外设流控模式 */
    g_dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;             /* 中等优先级 */
    g_dma_handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;            /* 关闭FIFO模式 */
    g_dma_handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;    /* FIFO阈值配置 */
    g_dma_handle.Init.MemBurst = DMA_MBURST_SINGLE;               /* 存储器突发单次传输 */
    g_dma_handle.Init.PeriphBurst = DMA_PBURST_SINGLE;            /* 外设突发单次传输 */

    HAL_DMA_DeInit(&g_dma_handle);   /* 取消DMA句柄的初始化 */
    HAL_DMA_Init(&g_dma_handle);	/* 重新对DMA句柄进行初始化 */
}

dma.h

#ifndef __DMA_H
#define __DMA_H

#include "./SYSTEM/sys/sys.h"

extern DMA_HandleTypeDef  g_dma_handle; 

void dma_init(DMA_Stream_TypeDef *dma_stream_handle, uint32_t ch);  /* 配置DMAx_CHx */

#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/MPU/mpu.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/DMA/dma.h"


const uint8_t TEXT_TO_SEND[] = {"正点原子 MiniPRO STM32H7 DMA 串口实验"};   /* 要循环发送的字符串 */

#define SEND_BUF_SIZE       (sizeof(TEXT_TO_SEND) + 2) * 200   /* 发送数据长度, 等于sizeof(TEXT_TO_SEND) + 2的200倍. */

uint8_t g_sendbuf[SEND_BUF_SIZE];       /* 发送数据缓冲区 */

int main(void)
{
    uint8_t  key = 0;
    uint16_t i, k;
    uint16_t len;
    uint8_t  mask = 0;
    float pro = 0;                      /* 进度 */

    sys_cache_enable();                 /* 打开L1-Cache */
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(240, 2, 2, 4); /* 设置时钟, 480Mhz */
    delay_init(480);                    /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    mpu_memory_protection();            /* 保护相关存储区域 */
    led_init();                         /* 初始化LED */
    lcd_init();                         /* 初始化LCD */
    key_init();                         /* 初始化按键 */

    dma_init(DMA2_Stream7, DMA_REQUEST_USART1_TX);  /* 初始化DMA */

    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "DMA TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "KEY0:Start", RED);

    len = sizeof(TEXT_TO_SEND);
    k = 0;
    
    for (i = 0; i < SEND_BUF_SIZE; i++) /* 填充ASCII字符集数据 */
    {
        if (k >= len)   /* 入换行符 */
        {
            if (mask)
            {
                g_sendbuf[i] = 0x0a;
                k = 0;
            }
            else
            {
                g_sendbuf[i] = 0x0d;
                mask++;
            }
        }
        else     /* 复制TEXT_TO_SEND语句 */
        {
            mask = 0;
            g_sendbuf[i] = TEXT_TO_SEND[k];
            k++;
        }
    }
 
    i = 0;

    while (1)
    {
        key = key_scan(0);

        if (key == KEY0_PRES)   /* KEY0按下 */
        {
            printf("\r\nDMA DATA:\r\n");
            lcd_show_string(30, 130, 200, 16, 16, "Start Transimit....", BLUE);
            lcd_show_string(30, 150, 200, 16, 16, "   %", BLUE);    /* 显示百分号 */

            HAL_UART_Transmit_DMA(&g_uart1_handle,g_sendbuf,SEND_BUF_SIZE); /* 开始一次DMA传输! */

            /* 等待DMA传输完成,此时我们来做另外一些事情,比如点灯  
             * 实际应用中,传输数据期间,可以执行另外的任务 
             */
            while (1)
            {
                if (__HAL_DMA_GET_FLAG(&g_dma_handle, DMA_FLAG_TCIF3_7)) /* 等待DMA2_Stream7传输完成 */
                {
                    __HAL_DMA_CLEAR_FLAG(&g_dma_handle, DMA_FLAG_TCIF3_7); /* 清除DMA2_Stream7传输完成标志 */
                    HAL_UART_DMAStop(&g_uart1_handle);      /* 传输完成以后关闭串口DMA */
                    break;
                }

                pro = __HAL_DMA_GET_COUNTER(&g_dma_handle);   /* 得到当前还剩余多少个数据 */
                len = SEND_BUF_SIZE;        /* 总长度 */
                pro = 1 - (pro / len);      /* 得到百分比 */
                pro *= 100;                 /* 扩大100倍 */
                lcd_show_num(30, 150, pro, 3, 16, BLUE);
            }
            lcd_show_num(30, 150, 100, 3, 16, BLUE);    /* 显示100% */
            lcd_show_string(30, 130, 200, 16, 16, "Transimit Finished!", BLUE); /* 提示传送完成 */
        }

        i++;
        delay_ms(10);

        if (i == 20)
        {
            LED0_TOGGLE();  /* LED0闪烁,提示系统正在运行 */
            i = 0;
        }
    }
}

在这里插入图片描述

七、总结

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

### 回答1: 正点原子H7 DMA接收是指在正点原子H7开发板上使用DMA直接内存访问)技术进行数据接收。DMA是一种用于实现高速数据传输的技术,它可以在不需要CPU干预的情况下直接将外设的数据传输到内存或者内存中的数据传输到外设。 在正点原子H7开发板上,通过配置DMA通道,可以将外设(例如串口)接收到的数据直接传输到内存中,而不需要CPU的干预。这种方式可以大大提高数据接收的效率,并减轻CPU的负载。 使用DMA接收数据的过程一般包括以下几个步骤: 1. 配置DMA通道:通过编程配置H7DMA控制器,选择一个合适的DMA通道进行数据传输。 2. 配置外设:设置外设的数据传输模式,使其能够将数据直接传输DMA通道。 3. 配置内存地址:设置DMA通道的目的地址,即数据接收后存储的内存地址。 4. 启动DMA传输:通过编程启动DMA传输,开始接收数据。 5. 处理接收到的数据:当DMA传输完成后,可以在中断或轮询方式下读取内存中的数据,并进行相应的处理。 总之,正点原子H7 DMA接收是一种高效的数据接收方式,通过直接内存访问技术实现数据的快速传输。使用DMA接收数据可以提高系统性能效率,同时减轻CPU的负载。这种技术在很多应用场景中都得到了广泛的应用。 ### 回答2: H7 DMA接收是指STM32H7微控制器中的DMA(Direct Memory Access)模块进行数据接收的功能DMA是一种在不需要CPU干预的情况下,直接从外部设备(如外设或存储器)向内部存储器进行数据传输的技术。 正点原子是一家专门从事嵌入式系统开发的公司,其产品包括各种开发板、模块配件。正点原子研发的H7 DMA接收模块专门适用于STM32H7微控制器,能够实现高效的数据接收。 H7 DMA接收模块的工作原理是通过配置DMA通道相关寄存器,使其能够将来自外设的数据直接传输到指定的内存地址中。这样可以大大提高数据传输的速度,减少CPU的负载。 H7 DMA接收模块具有以下特点:高性能、高带宽、低功耗以及可靠性。它可以处理高速传输的数据流,减轻CPU的负担,提高系统的响应速度效率。 H7 DMA接收模块的应用范围广泛,可用于各种数据接收场景,如音频、视频、通信等领域。它可以与其他外设配合使用,实现复杂的数据处理传输功能。 总之,H7 DMA接收模块是一种高效的数据接收技术,可以提高系统的性能效率。正点原子研发的H7 DMA接收模块在STM32H7微控制器上得到了广泛的应用,为各种嵌入式系统的开发提供了便利支持。 ### 回答3: 正点原子H7是一款基于STM32H743核心的单片机开发板,具有丰富的外设强大的性能。DMA直接内存访问)是一种能够在不需要CPU参与的情况下,实现外设与内存之间的数据传输的技术。 正点原子H7板载了丰富的外设,包括多个USART、SPI、I2C、CAN等串行通信接口,以及多个GPIO口、ADC、PWM等通用IO接口。这些外设与内存之间的数据传输可以通过编程来实现,但使用DMA能够更加高效、快速地完成数据的传输DMA接收指的是当外设向内存传输数据时,使用DMA来实现数据的接收。在正点原子H7上,可以通过设置DMA控制器的相关寄存器,设置数据源目的地的地址以及数据长度,使得DMA能够自动完成数据的传输,并将数据存储到指定的内存地址中。 使用DMA接收数据可以极大地减轻CPU的负担,提高系统的性能效率。此外,DMA还支持多通道配置,可以同时处理多个外设多个内存区域的数据传输,进一步提高数据传输的速度并发处理能力。 总结来说,正点原子H7 DMA接收是指利用DMA技术实现外设向内存的数据传输。通过使用DMA,可以极大地提高系统性能效率,实现高速、稳定的数据传输
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖喱年糕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值