STM32 DMA

发布于:2025-05-23 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

DMA简介 

​编辑 DMA框图

DMA基本结构​编辑

DMA请求 

数据宽度与对齐 

Flash to SRAM

SRAM to 外设


 

DMA简介 

 DMA框图

总共就是CPU和存储器两个东西,Flash是主闪存,SRAM是运行内存,各个外设都可以看成寄存器也是一种SRAM存储器

寄存器是一种特殊的存储器,一方面,CPU可以对寄存器进行读写,就像读写运行内存一样,另一方面,寄存器的每一位的背后都连接了一根导线,这些导线可以用于控制外设电路的状态,比如置引脚高低电平,导通和断开开关,切换数据选择器或者多位结合起来,当做计数器,数据寄存器等,所以寄存器是连接软件和硬件的桥梁,软件读写寄存器,就相当于在控制硬件的执行

既然外设就是寄存器,寄存器本身就是是存储器,那使用DMA进行数据转运,就都可以归为一类问题了,就是从某个地址取内容,再放到另一个地址去

为了高效有条理地访问存储器,这里设计了一个总线矩阵,总线矩阵的左端是主动单元,拥有存储器的访问权,右边是被动单元,存储器只能被左边的主动单元读写,主动单元这里内核有DCode和系统总线,可以访问右边的存储器, 其中Dcode是专门访问Flash的,系统总线是访问其他东西的,另外由于DMA要转运数据,所以DMA也必须要有访问的主动权

DMA1DMA2里可以看到DMA1有七个通道DMA2有五个通道,各个通道可以分别设置它们转运数据的源地址和目的地址,这样就可以各自独立的工作了

下面有个仲裁器,这个是因为,虽然多个通道可以独立转运数据但是DMA总线只有一条,所以所有的通道只能分时复用这一条DMA总线,如果产生了冲突,那就会由仲裁器,根据通道的优先级来决定谁先用谁后用;另外在总线矩阵这里也会有个仲裁器,如果DMACPU都要访问同一个目标,那么DMA就会暂停CPU的访问,以防止冲突,不过总线仲裁器还是会保证CPU得到一半的总线带宽,使CPU也能正常的工作

AHB从设备也就是DMA自身的寄存器,因为DMA作为一个外设,它自己也这里连接在总线右边的AHB总线上,所有DMA既是总线矩阵的主动单元可以读写各种存储器,也是AHB总线上的被动单元,所以可以根据红线进行配置了

DMA请求,就是触发的意思,这条线的触发源是各个外设,所以这个DMA请求就是DMA的硬件触发源比如ADC转换完成,串口接收到数据,需要DMA转运数据的时候,就会通过蓝线向DMA发出硬件触发信号,之后DMA就可以执行数据转运的工作了,这就是DMA请求的作用·

 DMA基本结构

1,

起始地址如果写Flsah地址就会去Flsah里找,只是个名字

地址是否自增就相当于指针p++,转运完一次数据,是否将下一个地址数据转运

2,

红色圈就是数据转运的两大站点,左边是外设寄存器站点,右边是存储器站点,在STM32里所说的存储器一般是特指FlashSRAM,不包含外设寄存器,外设寄存器一般直接称为外设,所以就是外设到存储器,存储器到存储器这样来描述,虽然我们刚刚说了寄存器也是存储器的一种,但是STM32还是使用了外设和存储器来作为区分

DMA的数据转运可以是外设到存储器,也可以是存储器到外设,具体是向左还是向右有一个方向的参数可以进行控制,另外还一种转运方式是存储器到存储器,只有两种FlashSRAM SRAMSRAM是,因为Flash是只读的

 3,

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

传输计数器,用来指定我总共需要转运几次的,这个传输计数器是一个自减计数器,比如写个5,那DMA只能进行5次数据转运,另外减到0后,之前自增的地址也会恢复到起始地址的位置,以方便DMA开始新一轮的转运

 4,

DMA的触发控制,触发就是决定DMA需要在什么时机进行转运的,触发源有硬件触发和软件触发,具体选择哪个由M2M这个参数决定,当我们给M2M1时,DMA就会选择软件触发,这个软件触发不是调用某个函数一次触发一次,这个软件触发的执行逻辑是,以最快的速度,连续不断地触发DMA,争取早日把传输计数器清零完成这一轮转换(连续触发)软件触发和循环模式不能同时用,当M2M0就是硬件触发,硬件触发源可以选择ADC、串口、定时器等,使用硬件触发的转运一般都是与外设有关的转运,这些转运需要一些时机,比如ADC转换完成,串口收到数据,定时时间到,在硬件达到这些时机时传一个信号过来,来触发DMA进行转运

DMA请求 

上图DMA触发的部分

EN=0数据选择器不工作,EN=1数据选择器工作

M2M=1时选择软件触发

选择硬件触发是要看通道的

这里通道1的硬件触发是ADC1、定时器2的通道3和定时器4的通道1,到底选择哪个触发源呢,这个是对应的外设是否开启DMA输出来决定的,比如你要使用ADC1那会有个库函数ADC_DMACmd,必须使用这个库函数开启ADC1的这一路输出,才有效

之后这7个触发源,进入到仲裁器进行优先级判断,最终产生内部的DMA1请求,默认优先级是通道号越小,优先级越高,当然也可以在程序中配置优先级

数据宽度与对齐 

前面DMA基本结构中数据宽,如果数据转运的两个站点,都有一个数据宽带的参数,如果数据宽带都一样,那就是正常的一个个转运,如果数据宽带不一样

如果把小的数据转到大的里面去,高位就会补0,如果把大的数据转到小的里面去,高位就会舍弃掉,如果数据宽带一样就没事

Flash to SRAM

内部Flash(CODE)的数据传输到内部的SRAM(变量)

DMA函数

//const关键字,把SRC_Buffer定义为常量类型,表示数据存储在内部Flash中
const uint32_t SRC_Buffer[4] = {
	0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
};

//定义DMA传输的目标存储器,存储在内部SRAM中
uint32_t DST_Buffer[4] = {

};



/*
typedef struct
{
  uint32_t DMA_PeripheralBaseAddr;          //外设地址
  uint32_t DMA_MemoryBaseAddr;             //存储器地址
  uint32_t DMA_DIR                                    //传输方向

  uint32_t DMA_BufferSize;                         //传输数目
  uint32_t DMA_PeripheralInc;                    //外设地址增值模式
  uint32_t DMA_MemoryInc;                       //存储器地址增值模式

  uint32_t DMA_PeripheralDataSize;          //外设数据宽度
  uint32_t DMA_MemoryDataSize;              //存储器数据宽度
  uint32_t DMA_Mode;                                 //模式选择
  uint32_t DMA_Priority;                               //通道优先级
  uint32_t DMA_M2M;                                   //存储器到存储器的模式
}DMA_InitTypeDef;
*/

void DMA_MTM_Init(void)
{
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
//这里DMA是挂载在AHB系统总线的
	
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Buffer;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设作为起点
	
	DMA_InitStructure.DMA_BufferSize = 4;//传输4个数
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//外设需要地址增量
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器地址也增加

	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;

	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//是否循环发送
	DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;//优先级最高
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//M2M使能

	DMA_Init(DMA1_Channel6,&DMA_InitStructure);

    //传输完了后,怎么判断是否传输成功呢
    //所以就要用到这个标志位,参数是通道6的发送结束
    DMA_ClearFlag(DMA1_FLAG_TC6);

    
    //使能DMA
    DMA_Cmd(DMAy_Channel6, ENABLE);



}	

//DMA怎么判断是否完全传输成功了呢
//写一个比较函数,通过对比两个存储器是否一样,通过返回值判断是否正确

uint8_t Buffercmp(const uint32_t* pBuffer1,uint32_t* pBuffer2,uint32_t BufferLength)
{
	while(BufferLength --)//数据长度递减
	{
		if(* pBuffer1 != * pBuffer2)//判断俩数据源是否相等
		{
			return 0;            //对应数据源不相等 返回0
		}
		pBuffer1++;           //增值俩数据源地址指针
		pBuffer2--;
		
		return 1;            //完成判断,并且数据相等,返回1
		
	}
	
}

主函数中

int main() 
{
 	extern const uint32_t SRC_Buffer[4];
	extern uint32_t DST_Buffer[4];
	uint8_t statue = 0;
	LED_Init();
	DMA_MTM_Init();

	statue = Buffercmp(SRC_Buffer,DST_Buffer,4);
	if(statue == 0 )
	{
		GPIO_ResetBits(GPIOC, GPIO_Pin_13);
	}
	else
	{
		
		GPIO_SetBits(GPIOC, GPIO_Pin_13);	
	}
	while(1)
		{
			
						
		}
 
			
}

SRAM to 外设

内部的SRAM(变量)传输到外设(串口)

DMA函数

void USART_DMA_Init(void)
{
	
		DMA_InitTypeDef DMA_InitStructure;
		RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
		DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DST_SendBuffer;
		DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_DR_ADDR;
		DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//存储器作为起始点

		DMA_InitStructure.DMA_BufferSize = DMA_SIZE;
		DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
		DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
	
		DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
		DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
	
		DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
		DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
		DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

		DMA_Init(DMA1_Channel4, &DMA_InitStructure);
		DMA_ClearFlag(DMA1_IT_TC4);
		DMA_Cmd(DMA1_Channel4, ENABLE);

}

 main函数

	extern uint8_t DST_SendBuffer[DMA_SIZE];

	uint16_t i = 0;
	for(i=0;i<DMA_SIZE;i++)
	{
		DST_SendBuffer[i] = 'o';

	}
	uart_Init();
	USART_DMA_Init();
	USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);

然后打开串口接收


网站公告

今日签到

点亮在社区的每一天
去签到