STM32使用DMA数据搬运

272 阅读5分钟

DMA数据搬运

在上篇文章中,我们已经初步了解了ADC是什么,以及ADC是如何工作的,那么在这篇文章中我们来了解一下什么是DMA。

DMA

DMA的概念

DMA(直接存储器访问)是一种允许外设直接访问系统内存的技术,从而实现高速数据传输。它的主要功能是将数据从一个地址空间复制到另一个地址空间,减少CPU的负担,使其能够专注于更复杂的计算任务。DMA通过内部控制器实现内存和外设之间的数据传输,提升了系统的效率和性能。

简单来说,在嵌入式的系统中CPU就是一个大忙人,当我们通过传感器采集到多个数据后,CPU就需要去将数据从ADC寄存器搬运到内存中来,如果一直采集的话,CPU就余姚一直搬运数据,但是CPU的作用不仅仅是用于数据搬运,还有其他各种复杂的事情余姚CPU处理,那么我们还有什么方法能够减少CPU进行数据搬运的工作呢?这时DMA就走进了我们的视野,DMA就好比一个数据搬运工,当采集到足够的数据时,DMA总线会向CPU发送一个信号,等待CPU处理完这个信号后,数据就无需通过CPU直接通过DMA将数据搬运到内存中,因而大大节省了CPU搬运数据的时间,使得CPU更加高效!

DMA框图

接下来我们通过DMA框图来了解一下DMA

QQ_1749642988193.png 在DMA框图中我们可以将其分为两部分,Cortex-M3核心是第一部分-CPU,其他的所有东西我们可以看为第二部分-存储器.在中间有一个总线矩阵,在总线矩阵的左端是主动单元,就是拥有对存储器的访问权限,右端是被动单元,只能被左端的主动单元读写。DCode是用来访问Flash的,系统总线是用来访问其他存储器的,因为DMA要进行数据搬运,所以DMA也要访问的主动权。在图中我们可以看到DMA1有7个通道,DMA2有5个通道,各个通道又能分别设置转运数据的源地址以及目标地址,所以各个DMA通道可以独立的进行工作。虽然各个DMA通道都可以独立的工作,但是我们嫩看到DMA总线就一个,那么当他们一起吧数据传输过来的时候我们应该改怎么办呢?这是我们可以看到一个仲裁器,其作用就是当多个DMA通道来申请DMA总线的时候,仲裁器根据各个通道的优先级来的分配DMA总线。 外设可以通过DMA请求来请求DMA进行数据搬运。 我们了解了DMA的基本结构,接下来我们来看看DMA是如何工作的。

QQ_1749644921703.png 我们从上图可以看到DMA数据搬运的方向可以是Falsh和SRAM-外设寄存器或外设寄存器-Flash和SRAM,在进行传输时我们就要对其起始地址、数据宽度以及地址是否自增进行配置。然后我们到有一个传输计数器,其实是一个自减寄存器,传输一个数据减1,当减到零的时候存储器自增的地址回到起始地址方便下一次读写。我们通过这些已经了解到DMA工作的大概方式,接下来我们就对DMA进行配置。 `//打开DMA的时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

//初始化DMA
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_BufferSize=2;//数据传输的次数,而非缓冲区的大小
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//DMA的方向从外设到内存
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//禁止内存到内存的传输方式
DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)AD_Value;//设置数据传输后保存的地址
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//数据的大小
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//是否启用内存自增
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;
DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//只使用了ADC一个外设,因此不用自增
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;
DMA_Init(DMA1_Channel1,&DMA_InitStructure);`

DMA_InitStructure.DMA_BufferSize这个决定了我们在上面涮熟计数器的值,而不是数据缓冲区的大小,表示DMA一共需要搬运几次数据。

DMA_InitStructure.DMA_DIR这个意思是传输的数据方向,其又两个参数DMA_DIR_PeripheralSRC从外设到内存和DMA_DIR_PeripheralDST从内存到外设。

DMA_InitStructure.DMA_M2M用于配置是否使能存储器到存储器。在这里我们需要从外设到内存因此对其失能。

DMA_InitStructure.DMA_MemoryBaseAddr用于指定数据传输后保存的地址,因为在这里我们将ADC采集的数据保存在AD_Value中,所以这里的参数为AD_Value。

DMA_InitStructure.DMA_MemoryDataSize用于指定保存的数据大小,因为STM32ADC采集的数据位数为12位,在配置ADC的时候我们配置的ADC采集数据的大小位16位且右对齐,所以我们的数据大小位为12位。

DMA_InitStructure.DMA_MemoryInc用来指定是否启用内存自增,因为我们采集到的数据通过DMA从外设搬运到内存中,内存中一个存储单元存储一个数据但是我们一次采集了多个数据,所以我们要采用内存自增。

DMA_InitStructure.DMA_Mode用来配置DMA的工作模式,在这里面有两个参数,一个参数DMA_Mode_Circular循环模式,另一个参数为DMA_Mode_Normal,他们两个的区别就是在DMA进行传输的时候,正常模式的话DMA搬运一次就停止搬运了适合只用进行一次数据搬运的场合,但是循环模式的话当搬运完成后,其各种地址指针以及DMA_BufferSize会重新开始,不需要重新配置,适用于不断检测数据的场所。

DMA_PeripheralBaseAddr用于指定外设地址,数据采集后放在的是ADC的DR寄存器中,因此到ADC的DR寄存器中去取。

DMA_InitStructure.DMA_PeripheralInc用来表示在对外设数据读取的时候是否需要自增,因为ADC只有一个数据寄存器,所以DMA进行数据搬运的时候只用来ADC的寄存器中取数据就好,不用自增。 根据这样我们就对DMA进行了配置,当ADC采集到数据后DMA就可以进行数据的搬运。