STM32 DMA

STM32 DMA


1 什么是DMA?

DMA(Direct memory access)意为直接内存访问,主要优秀在直接二字,之所以要用DMA,是为了在外设到内存或内存到内存能够迅速的传输数据,直接利用直接利用DMA去传输数据,从而避免掉CPU的动作,这样CPU就能腾出空来做其他事了。我们在知其然的基础上还要知其所以然,在参考其他文献时大家都说减少CPU的开支,那么DMA到底能不能减少CPU的开支,如果能,他是怎么做到的呢?

1.1 Bus Matrix

这里要介绍一个概念叫做Bus Matrix,直译过来叫做总线矩阵,这个东西是负责协调管理内核系统总线和DMA主机总线的通信优先权,采用了一种循环算法。也就是说如果出现系统总线和DMA同时访问同一个外设或者内存,这个时候Bus Matrix会循环调用他们,这样可以保证一半的系统总线带宽。但如果访问不同的外设,则可以同时进行,是真正意义上的多线程。这就是STM32F1使用DMA能够使传输数据变快的最根本原因。如下图可以清晰看出这个原理。

STM32 DMA_第1张图片

1.2 DMA数据传输

1.在调用后,外围设备会向DMA控制器发送请求信号,DMA控制器会根据频道的优先级来提供服务,一旦DMA控制器连接上外围设备,就会向外围设备发送应答信号,外围设备收到应答信号以后释放请求信号,一旦请求信号被释放,DMA控制器就释放应答信号,这样一个通信过程就完成了,如果存在多个请求,则初始化下次传输。 
2.概括来讲,每次DMA传输包含三个操作步骤:

  • 从外围设备的数据寄存器或内存中的某个位置加载数据,第一次传输获取数据的开始地址基于被存储DMA_CPARx或DMA_CMARx寄存器种的地址

  • 将刚刚加载的数据存储到指定位置。

  • DMA的DMA_CNDTRx寄存器做减法运算

1.3 DMA通道

每个通道都可以处理位于确定地址的外围设备传感器和内存地址,传输的数据总量是可编程的。STM32F1共有两个DMA控制器,其中DMA1有7个通道,DMA2有5个通道。如下图:

STM32 DMA_第2张图片

STM32 DMA_第3张图片

2 DMA使用

这里以UART发送为例讲解。

2.1 工程创建及代码修改

首先用cubeMX创建工程,这里需要注意的是USART1的配置,将发送和接收配置成DMA的方式,如下图所示:

STM32 DMA_第4张图片 
然后生成代码,在main函数中,添加以下代码:

uint8_t test_buffer[10] = "0123456789";

while (1)

{

HAL_UART_Transmit_DMA(&huart1, test_buffer, sizeof(test_buffer));

HAL_Delay(1000);

}

编译运行后发现只能发送一次,再更改一个地方,在UART_DMATransmitCplt()中添加一句话,如图所示:

STM32 DMA_第5张图片 
编译后运行正常。

2.2 代码解读

  • 在main函数中,调用HAL_UART_Transmit_DMA进行发送

  • 在HAL_UART_Transmit_DMA中,前面的部分都是一些参数配置,比如数据地址,大小,回调函数等等,然后调用HAL_DMA_Start_IT

  • 在HAL_DMA_Start_IT中,如果当前状态是HAL_DMA_STATE_READY,则调用DMA_SetConfig进行参数配置,传入DMA实体,源地址,目标地址以及数据长度

  • 在DMA_SetConfig中,我们才真正看到了寄存器

    static void DMA_SetConfig(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
    
    {
    
    /* Clear all flags */
    
    hdma->DmaBaseAddress->IFCR = (DMA_ISR_GIF1 << hdma->ChannelIndex);
    
    
    
    /* Configure DMA Channel data length */
    
    hdma->Instance->CNDTR = DataLength;
    
    
    
    /* Memory to Peripheral */
    
    if((hdma->Init.Direction) == DMA_MEMORY_TO_PERIPH)
    
    {
    
    /* Configure DMA Channel destination address */
    
    hdma->Instance->CPAR = DstAddress;
    
    
    
    /* Configure DMA Channel source address */
    
    hdma->Instance->CMAR = SrcAddress;
    
    }
    
    /* Peripheral to Memory */
    
    else
    
    {
    
    /* Configure DMA Channel source address */
    
    hdma->Instance->CPAR = SrcAddress;
    
    
    
    /* Configure DMA Channel destination address */
    
    hdma->Instance->CMAR = DstAddress;
    
    }
    
    }

     

在这里配置了数据地址及大小,然后使能DMA就开始发送了。

所以应用起来就是以下三个步骤: 
1.失能DMA 
2.配置数据地址及大小 
3.使能DMA


欢迎大家留言点赞,欢迎大家点击关注,大家的赞美是我创作的最大动力,谢谢!

你可能感兴趣的:(单片机)