STM32学习——DMA

一、什么是DMA

        DMADirect Memory Access)直接存储器存取。

        CPU作为计算机的大脑,像数据传输这种简单,重复性高的工作交给CPU来做就显得有些浪费,所以DMA就来充当这一角色,DMA可以在硬件外设和存储器之间,或者是存储器与存储器之间进行高速数据转运,不需要CPU的介入,节省了cpu资源。

        在STM32中,有DMA1和DMA2两个,其中DMA1有7个通道,DMA2有5个通道,但是根据芯片型号的不同,DMA资源也有区别,具体还要参考手册。对于每个DMA通道,如果是存储器和存储器之间转运数据,选用哪个通道倒是无所谓,但是如果是硬件外设和存储器之间进行转运,就要注意不同的硬件外设是有确定的DMA通道的,不可以乱用。

        不同外设对应不同的DMA通道:

二、DMA结构

        内核对外设的操作是通过寄存器来间接控制的,所以DMA转运数据就是从一个地址里面读取数据,在送到另外一个地址里。框图里的总线矩阵,其左侧拥有访问权,右侧的只能被访问。

        Dcode总线是访问Flash的,DMA要转运数据,就必须要有访问权,而且每个DMA只有一条总线,也就是说,虽然DMA1有7个通道,但是只有一条能与外界交流,而究竟是哪一条进行沟通,就取决于DMA内部的仲裁器,类似于中断的优先级,仲裁器决定哪一个通道先交流。同时,总线矩阵处也有仲裁器,在CPU和DMA同时要访问一个目标时,由仲裁器决定谁优先,如果是DMA优先,仲裁器仍然会给CPU一半的线宽,确保CPU能正常工作,只是在访问此目标时让DMA优先访问。

        DMA本身也是一个外设,CPU对其的控制也是要通过寄存器来控制的,AHB就是DMA的寄存器。AHB连接到总线矩阵右侧,说明DMA即可以主动读取,也可以被动被读取。

        “DMA请求”来自总线上挂载的各种外设,说明此请求来自外设,属于硬件请求。

三、存储器映像

3.1、ROM

1.程序存储器Flash

        起始地址:0x0800 0000。用于存储C语言编译后的代码。(const常量也被存储在Flash)

2.系统存储器

        起始地址:0x1FFF F000。存储BootLoader,用于串口下载。

3.选项字节

        起始地址:0x1FFF F800。存储一些独立于程序的配置参数。

3.2、RAM

1.运行内存SRAM

        起始地址:0x2000 0000。存储运行过程中的临时变量。

2.外设寄存器

        起始地址:0x4000 0000。存储各个外设的配置参数。

3.内核外设寄存器

        起始地址:0xE000 0000。存储内核各个外设的配置参数。

四、DMA基本结构

        “外设”和“存储器”相当于两个站点,内部都有三个部分,起始地址就是要读或者要写的起始地址,数据宽度就是要写几位,地址自增就是写完一位后,是否自动将地址加1。方向就是转运的方向。传输计数器是转运几次,每转运一位数据,计数器减一,减到0此次转运结束。自动重装就是计数器减到0后自动恢复到设置值,重新开始转运。

        M2M意思是存储器到存储器的转运,选中该模式时就是软件触发,否则就是硬件触发。软件触发是调用后,以最快速度将传输计数器清零,争取最快转运完成。所以软件触发和自动重装不可以一起用,软件触发将计数器清零,自动重装又重装,导致循环。

        最后就是开关控制,使用DMA就要打开DMA的开关,就是CMD函数,其次,如果是硬件外设与存储器的转运,还要打开对应的外设DMA开关。

五、K5代码

5.1、DMA相关函数

//DMA_DeInit        			恢复缺省配置
//DMA_Init								DMA初始化
//DMA_StructInit					DMA结构体初始化
//DMA_Cmd									DMA使能
//DMA_ITConfig						DMA中断许可
//DMA_SetCurrDataCounter	设置当前数据寄存器,就是给传输计数器写数据的
//DMA_GetCurrDataCounter	获取当前数据寄存器,就是获取传输计数器的数据
//DMA_GetFlagStatus				获取标志位状态
//DMA_ClearFlag						清除标志位
//DMA_GetITStatus					获取中断状态
//DMA_ClearITPendingBit		清除中断挂起位

5.2、初始化函数

/**
  * 函    数:DMA初始化
  * 参    数:AddrA 原数组的首地址
  * 参    数:AddrB 目的数组的首地址
  * 参    数:Size 转运的数据大小(转运次数)
  * 返 回 值:无
  */
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
	MyDMA_Size = Size;					//将Size写入到全局变量,记住参数Size
	
	/*开启时钟*/
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);						//开启DMA的时钟
	
	/*DMA初始化*/
	DMA_InitTypeDef DMA_InitStructure;										//定义结构体变量
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;						//外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据宽度,选择字节
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;			//外设地址自增,选择使能
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;							//存储器基地址,给定形参AddrB
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据宽度,选择字节
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器地址自增,选择使能
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;						//数据传输方向,选择由外设到存储器
	DMA_InitStructure.DMA_BufferSize = Size;								//转运的数据大小(转运次数)
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//模式,选择正常模式
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;								//存储器到存储器,选择使能
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//优先级,选择中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);							//将结构体变量交给DMA_Init,配置DMA1的通道1
	
	/*DMA使能*/
	DMA_Cmd(DMA1_Channel1, DISABLE);	//这里先不给使能,初始化后不会立刻工作,等后续调用Transfer后,再开始
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值