STM32 DMA原理详解及代码示例

1.什么是DMA?

DMA:Direct Memory Access,直接存储器存取。是一种计算机系统中用于高效地实现数据传输的技术。DMA允许外设设备(如硬盘、显卡、网络适配器等)直接访问主内存,而不需要CPU的干预。

2.DMA的作用

DMA传输是将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。这种数据传输不经过CPU,因此CPU可以专注于处理其他复杂的事物,提高了CPU的利用率。DMA最常见的用法是配合ADC的扫描模式,因为ADC规则组扫描有数据覆盖的缺陷。

3.DMA通道

STM32系列最多有12个独立可配置的通道: DMA1(7个通道)DMA2(5个通道);每个通道都支持软件触发特定的硬件触发(红框)

DMA请求映象

在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推)。

4.DMA框图

下面对该框图进行详细解释:

DMA的工作流程:

    1.首先外设向DMA发送请求;
    2.DMA控制器收到请求,触发DMA工作;
    3.DMA控制器从AHB外设获取ADC采集的数据,存储到DMA通道中;
    4.DMA控制器的DMA总线与总线矩阵协调,使用AHB把外设ADC采集的数据经由DMA通道存放到SRAM中。这个数据的传输过程中,完全不需要内核的参与,也就是不需要CPU的参与,实现了硬件自动化。绿色箭头代表了整体的线路流程。

Cortex-M3内核:包含CPU、内核外设等。

Dcode总线:专门访问Flash

系统总线:访问其他东西

DMA1有7个通道:各个通道可以分别设置他们转运的源地址与目的地址,所以各个通道可以独立的工作。

仲裁器:虽然多个通道可以独立转运数据,但是DMA总线只有一条,所以所有的通道都只能分时复用这一条DMA总线。如果产生了冲突,就会由仲裁器,根据通道的优先级,决定谁先用谁后用。

总线矩阵也有个仲裁器,如果DMA与CPU都想访问同一个目标,那么DMA就会暂停CPU的访问,防止发生冲突,不过总线仲裁器,仍然会保证CPU得到一般的带宽,使CPU能正常工作。这就是仲裁器的作用(调度各个通道)。

AHB从设备:就是DMA的寄存器。配置DMA参数。CPU可以通过CPU→系统→总线矩阵→AHB从设备;配置DMA。

DMA请求:就是DMA硬件触发源。

Flash接口控制器:因为Flash是只读的,不能写,配置Flash接口,可以向Flash写入数据

SRAM:是运行内存,读写都没问题

5.DMA简化结构图

起始地址:决定了方向(从哪里来到哪里去)

数据宽度:指定一次转运要按照多大的数据宽度来进行,数据宽度如下:

字节(Byte)8位、半字(HalfWord)16位、字(Word)32位

地址是否自增:指定一次转运完成之后,下一次转运,是不是要把地址移动到下一个位置去,相当于是指针p++的意思,比如ADC扫描模式。

传输计数器:指定总共需要转运几次。是一个自减计数器,比如写个5,DMA只能进行5次数据转运,转运过程中,每转运一次,计数器就自动减1,当传输计数器减到0之后,DMA就不会再进行数据转运了,另外,减到0之后,之前自增的地址,也会恢复到起始地址的位置。以方便之后DMA开始新一轮的转运。如果转运完成,传输计数器清0,这时再想给传输计数器赋值的话,先DMA失能-→写传输计数器→DMA使能。

自动重装器:传输计数器减到0之后,是否恢复到最初的值,如果不用自动重装器,转运一次,DMA就结束了。如果使用自动重装器,计数器减到0后,就会立即重装到初始值5。自动重装器决定了转运的模式:单次模式or循环模式。

DMA的触发控制:触发,就是决定DMA需要在什么时机进行转运的。触发源有硬件触发or软件触发。具体选择哪个触发源,由M2M(Memory to Memory)参数决定。

    1:软件触发,执行逻辑是:以最快的速度,连续不断地触发DMA,争取早日传输书计数器清零,完成这一轮的转换。软件触发与循环模式不能同时使用,因为软件触发就是想把计数器清零,循环模式是清零后自动重装。如果同时用的话,DMA就停不下来了。

    2:硬件触发:通过硬件的触发源触发DMA转运。

注:DMA的每个通道都有一个数据选择器,可以选择软件触发or硬件触发。每个通道的硬件触发源都是不同的。如果是软件触发的话,选择哪个通道都无所谓了。

6.数据转运+DMA

该图展示了源地址与目的地址都自增的情况,通过软件触发DMA转运。外设与存储器每进行一次转运后,地址都对应++。

7.ADC扫描模式+DMA

左边有7个通道,触发一次后,七个通道依次进行AD转换,转换结果都放到ADC_DR寄存器中,我们要做的是,在每个单独的通道转换完成之后,进行一次DMA数据转运,并且目的地址进行自增,这样数据就不会被覆盖了。

ADC如果是单次扫描,那DMA的传输计数器可以不自动重装,转换一轮就停止;如果ADC是连续扫描,那DMA就可以使用自动重装,在ADC启动下一轮转换的时候,DMA也启动下一轮转运,ADC与DMA同步工作。

8.DMA代码

#include "stm32f10x.h"                  // Device header

uint16_t MyDMA_Size;

void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
	MyDMA_Size = Size;
	//第一步:RCC开启DMA时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	//第二步:调用DMA_Init,初始化DMA各个参数
	DMA_InitTypeDef DMA_InitStructure;
	//外设站点的起始地址、数据宽度、是否自增
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
	//存储器的起始地址、数据宽度、是否自增
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	
	//缓冲区大小(传输计数器)
	DMA_InitStructure.DMA_BufferSize = Size;
	//传输方向
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	//选择是否是存储器到存储器(硬件出发还是软件触发)
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
	//传输模式(是否使用自动重装)
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	//优先级
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	
	DMA_Init(DMA1_Channel1,&DMA_InitStructure);
	//给DMA通电
	DMA_Cmd(DMA1_Channel1,DISABLE);
}


//给传输计数器重新赋值
void MyDMA_Transfer(void)
{
	DMA_Cmd(DMA1_Channel1,DISABLE);  //修改计数时,需要先把DMA_Cmd失能
	DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);//设置计数器的值
	DMA_Cmd(DMA1_Channel1,ENABLE);//使能
	
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);  //等待转运完成
	DMA_ClearFlag(DMA1_FLAG_TC1);//清除标志位(需要软件清除)
}

以上代码是DMA初始化部分,可直在main函数中调用,实现自己想要的功能。

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 DMA原理详解及代码示例

发表评论