DMA是什么意思啊 DMA的使用思路

存储技术

600人已加入

描述

DMA(Direct Memory Access, 直接存储区访问) 为实现数据高速在外设寄存器与存储器之间或者存 储器与存储器之间传输提供了高效的方法。

之所以称之为高效,是因为 DMA 传输实现高速数据 移动过程无需任何 CPU 操作控制。

从硬件层次上来说,DMA 控制器是独立于 Cortex-M4 内核的, 有点类似 GPIO、USART 外设一般,只是 DMA 的功能是可以快速移动内存数据。

DMA的通俗介绍

DMA(Direct Memory Access)直接存储通道,这是现在的MCU里基本都会标配的外设,但是很多工程师其实对它还是有些许陌生,因为这个东西就算不使用,也不影响外设的开发和使用。可是一旦对DMA的使用很熟练,那丝毫不亚于C语言熟练使用指针,能极大的提升外设的使用效率。

那这是一个什么玩意呢?简单的举个例子,应用层代码相当于商家,CPU相当于中间商,外设相当于批发商,在没有DMA时,如果应用代码想要获取外设信息(ADC采样/UART数据/SPI接收数据等),那么需要通知一下中间商CPU想进点货,然后CPU再去找外设批发商要求发货。

批发商发货给中间商PCU,然后再由CPU给到应用层。不难看出这样的交互过程,中间商CPU忙的够呛,要和商家和批发商两头沟通,还要管下单(外设数据请求)和物流(外设数据传输)

ADC采样

随着技术进步,现在的MCU都有比较快的运行速度,所以在没有极致速度要求时,CPU也忙的过来,大家也相安无事。

可是试想一下,一旦碰上双十一和春节抢车票这种的场景,这样低效率的方式则会严重耽误事,所以DMA则是一个提供了绕过中间商,商家直达批发商,达到厂家直销的效果。省去了中间大量的沟通成本,实现双赢。

ADC采样

DMA的使用思路

DMA的使用也没有那么邪乎,很符合大家常规的一个思考。基本可以浓缩为当发生一个啥事的时候,需要某个人A给另一个人B捎个口信,让B知道是啥信息。套用个场景就是狗蛋来到旺财家门口让旺财妈知会一下,想和旺财一起出门玩(事件发生:狗蛋来找旺财玩),旺财妈妈朝屋里大喊一声:“旺财狗蛋喊你出门玩嘞!”(信息直接传递),然后旺财就知道了狗蛋来找他玩这件事。DMA的数据传输长度类似就是旺财妈那句话的断句,她可以一口气直接喊:“旺财狗蛋喊你出门玩嘞“,也可以喊:“旺财!狗蛋~ 喊你!出门~玩嘞!“这样两个字两个字的喊,虽然话是两个字的往外蹦,但是整体意思也是完整传达到了。那么这个说法可以套用到一个UART接收DMA传输,设置好事件条件是UART收到数据,那么就把UART外设数据寄存器(收到的数据)按照一个byte的长度直接发送到事先指定的RAM内存位置。

实际代码分析

综上可以看出DMA的使用配置最重要的内容就是触发条件,然后谁把信息告诉谁,每次传递多少数据。如下的范例代码,是配置了ADC每次完成采样后,能够将采样到的数据依次偏移放置在制定的数组种。

下面的代码主要关注的是结构体 DMA_InitStructure的配置。如下代码,可以看到触发条件是完成ADC采样后,ADC数据寄存器中的值就会直接通过DMA更新至指定的数组中,并且会自动完成数组中的指针偏移。在实际的使用中就能达到一次配置后,存放ADC数据的数组就会自动一直在刷新对应通道的ADC数值了。

void BspAdcDmaInit(ADC_Module* pADCx)  
{  
  DMA_InitType DMA_InitStructure;  
  DMA_ChannelType* DMAyChx;  
  u8 u8Offset = 0;  
  u8 u8Num = 0;  

  if(pADCx == ADC1)  
  {  
    u8Offset = 0;  
    DMAyChx = DMA1_CH1;  
    u8Num = ADC1CH_NUM;  
  }  
  else if(pADCx == ADC2)  
  {  
    u8Offset = ADC1CH_NUM;  
    DMAyChx = DMA1_CH8;  
    u8Num = ADC2CH_NUM;  
  }  
  else if(pADCx == ADC3)  
  {  
    u8Offset = ADC1CH_NUM + ADC2CH_NUM;  
    DMAyChx = DMA2_CH5;  
    u8Num = ADC3CH_NUM;  
  }  
  else if(pADCx == ADC4)  
  {  
    u8Offset = ADC1CH_NUM + ADC2CH_NUM + ADC3CH_NUM;  
    DMAyChx = DMA2_CH8;  
    u8Num = ADC4CH_NUM;  
  }  

  RCC_EnableAHBPeriphClk( RCC_AHB_PERIPH_DMA1|RCC_AHB_PERIPH_DMA2, ENABLE);  

  DMA_DeInit(DMAyChx);  
DMA_InitStructure.PeriphAddr   = (uint32_t)&pADCx- >DAT; //ADC数据寄存器  
DMA_InitStructure.MemAddr   = (uint32_t)&gu16AdcData[u8Offset]; //制定存放的数据地址  
DMA_InitStructure.Direction   = DMA_DIR_PERIPH_SRC;//传输方向为外设指向内存  
DMA_InitStructure.BufSize   = u8Num; //该ADC的一次全采样通道需要DMA传输次数  
DMA_InitStructure.PeriphInc   = DMA_PERIPH_INC_DISABLE;//DMA传输完无需变更外设数据寄存器地址  
DMA_InitStructure.DMA_MemoryInc   = DMA_MEM_INC_ENABLE;//指向内存的指针每完成一次DMA传输需要自动偏移  
DMA_InitStructure.PeriphDataSize   = DMA_PERIPH_DATA_SIZE_HALFWORD; //每次从外设获取的数据长度  
DMA_InitStructure.MemDataSize   = DMA_MemoryDataSize_HalfWord; //每次从内存获取的数据长度  
DMA_InitStructure.CircularMode   = DMA_MODE_CIRCULAR;//DMA会自行循环触发,直到完成BufSize完成后会自行恢复Mem最初指针并重新DMA传输  
DMA_InitStructure.Priority   = DMA_PRIORITY_HIGH;  
DMA_InitStructure.Mem2Mem   = DMA_M2M_DISABLE;  
DMA_Init(DMAyChx, &DMA_InitStructure);  
    
DMA_EnableChannel(DMAyChx, ENABLE);  

  ADC_EnableDMA(pADCx, ENABLE); //使能ADC完成采样触发DMA数据传输  
}
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分