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能够使传输数据变快的最根本原因。如下图可以清晰看出这个原理。

image.png

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个通道。如下图:

image.png
image.png

2 DMA使用

这里以UART发送为例讲解。

2.1 工程创建及代码修改

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

image.png

然后生成代码,在main函数中,添加以下代码:

    uint8_t test_buffer[10] = "0123456789";
    while (1)
    {
        HAL_UART_Transmit_DMA(&huart1, test_buffer, sizeof(test_buffer));
        HAL_Delay(1000);
    }

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

image.png

编译后运行正常。

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


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

推荐阅读更多精彩内容

  • 什么是嵌入式 IEEE(Institute of Electrical and Electronics Engin...
    Leon_Geo阅读 1,977评论 1 20
  • ​​​本文主要介绍嵌入式系统的一些基础知识,希望对各位有帮助。 嵌入式系统基础 1、嵌入式系统的定义 (1)定义:...
    OpenJetson阅读 1,565评论 0 12
  • 1、嵌入式系统的定义 (1)定义:以应用为中心,以计算机技术为基础,软硬件可裁剪,适应应用系统对功能、可靠性、成本...
    荣卓然阅读 796评论 0 5
  • 总线 计算机的各个功能部件通过总线连接在一起构成完整的计算机系统,总线是多个系统功能部件之间进行数据传送的公共通路...
    无敌大灰狼me阅读 2,083评论 0 10
  • 大学的时候,帮朋友写的操作系统调研的作业,最近整理过去的文档时候偶然发现,遂作为博客发出来。 从串口驱动到Linu...
    Linkerist阅读 5,325评论 7 58