您的位置:首页 > 大数据 > 物联网

寒假学习之stm32(15)----DMA(direct memory access)

2017-02-10 17:09 197 查看

DMA的基础科普

老规矩,最先进行的是基础知识的科普

DMA(百度百科):

http://baike.baidu.com/subview/32471/5048463.htm

DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。DMA 传输对于高效能 嵌入式系统算法和网络是很重要的。

在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。

1. DMA请求
CPU对DMA控制器初始化,并向I/O接口发出操作命令,I/O接口提出DMA请求。




2. DMA响应
DMA控制器对DMA请求判别优先级及屏蔽,向总线裁决逻辑提出总线请求。当CPU执行完当前总线周期即可释放总线控制权。此时,总线裁决逻辑输出总线应答,表示DMA已经响应,通过DMA控制器通知I/O接口开始DMA传输。




3. DMA传输
DMA控制器获得总线控制权后,CPU即刻挂起或只执行内部操作,由DMA控制器输出读写命令,直接控制RAM与I/O接口进行DMA传输。
在DMA控制器的控制下,在存储器和外部设备之间直接进行数据传送,在传送过程中不需要中央处理器的参与。开始时需提供要传送的数据的起始位置和数据长度。




4. DMA结束
当完成规定的成批数据传送后,DMA控制器即释放总线控制权,并向I/O接口发出结束信号。当I/O接口收到结束信号后,一方面停 止I/O设备的工作,另一方面向CPU提出中断请求,使CPU从不介入的状态解脱,并执行一段检查本次DMA传输操作正确性的代码。最后,带着本次操作结果及状态继续执行原来的程序。




由此可见,DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,使CPU的效率大为提高。

stm32的DMA特性

聊完了总的性质,接下来该谈谈DMA在stm32上的特性了

stm32下的 DMA主要特性

1. 12个独立的可配置的通道(请求): DMA1有7个通道, DMA2有5个通道

每个通道对应的具体外设如中文参考手册所给出的图所示:

DMA1:



DMA2:



每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过

软件来配置。

在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、

中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。

独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目

标地址必须按数据传输宽度对齐。

支持循环的缓冲器管理

每个通道都有3个事件标志(DMA半传输、 DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。

存储器和存储器间的传输

外设和存储器、存储器和外设之间的传输

闪存、 SRAM、外设的SRAM、 APB1、 APB2和AHB外设均可作为访问的源和目标。

可编程的数据传输数目:最大为65535

祭出DMA的程序框图,先看看总体印象:



库函数的配置过程:

1.CPU对IO的初始化指令:

void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq,FunctionalState NewState);
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void DAC_DMACmd(uint32_t DAC_Channel, FunctionalState NewState);
void I2C_DMACmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void SDIO_DMACmd(FunctionalState NewState);
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq,FunctionalState NewState);
void TIM_DMAConfig(TIM_TypeDef* TIMx, uint16_t TIM_DMABase,uint16_t TIM_DMABurstLength)
void TIM_DMACmd(TIM_TypeDef* TIMx, uint16_t TIM_DMASource,FunctionalState NewState);


CPU对DMA的初始化指令以及 2.DMA响应的过程:

时钟的初始化:

由图片可以看出,stm32的dma是在AHB总线上的,所以在时钟初始化的时候:

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

DMA初始化:

DMA_DeInit(DMA_CHx);   //将DMA的通道x寄存器重设为缺省值
DMA_Init(DMA_CHx,&DMA_InitStruct);
其中,DMA_InitStruct的结构体成员如下:

1. DMA_PeripheralBaseAddr:外设的基地址(寄存器的地址)
2. DMA_MemoryBaseAddr: 内存的基地址(可以是代码中定义的数组的地址)
3. DMA_BufferSize : 传送数据的数量,其有效参数需要自己设置(0~65535)
4. DMA_DIR :DMA的数据传输方向,其有效参数为:DMA_DIR_PeripheralDST(外设作为数据发送的目的地)或者DMA_DIR_PeripheralSRC(外设作为数据发送的源头)
5. DMA_PeripheralInc:外设的自增模式(每次传输一个数据之后,指针自动+1),有效的参数为:DMA_PeripheralInc_Enable,DMA_PeripheralInc_Disable
6. DMA_MemoryInc:内存的自增模式,有效参数同5类似
7. DMA_PeripheralDataSize:外设的每个数据的大小,其有效参数为:DMA_MemoryDataSize_Byte, DMA_MemoryDataSize_HalfWord, DMA_MemoryDataSize_Word
8. DMA_MemoryDataSize,内存的每个数据大小,其概念类似于7
9. DMA_Mode:DMA的数据传输模式,DMA_Mode_Circular(循环传递),DMA_Mode_Normal(只传递一次)
10. DMA_Priority:DMA的数据传递优先级别,其有效参数为:DMA_Priority_VeryHigh(非常高),DMA_Priority_High(高),DMA_Priority_Medium(中),DMA_Priority_Low(低)


使能DMA(在每次数据传输结束以后,数据传输数量都会被清零,所以,每次传输数据,都必须使能一次DMA)

void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
DMA_Cmd(DMA_CHx, DISABLE );  //关闭USART1 TX DMA1 所指示的通道
DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
DMA_Cmd(DMA_CHx, ENABLE);  //使能USART1 TX DMA1 所指示的通道
}


3.DMA传输,4.DMA传输结束

while(1)
{
if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=RESET) //判断通道4传输完成
{
DMA_ClearFlag(DMA1_FLAG_TC4);//清除通道4传输完成标志
break;
}
pro=DMA_GetCurrDataCounter(DMA1_Channel4);//得到当前还剩余多少个数据
pro=1-pro/SEND_BUF_SIZE;//得到百分比
pro*=100;      //扩大100倍,得到真正的数据
}


总体的过程:

DMA_InitTypeDef DM
b924
A_InitStructure;

u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度
//DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输

DMA_DeInit(DMA_CHx); //将DMA的通道1寄存器重设为缺省值

DMA1_MEM_LEN=cndtr;
DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向,从内存读取发送到外设
DMA_InitStructure.DMA_BufferSize = cndtr; //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输

DMA_Init(DMA_CHx, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道
//USART1_Tx_DMA_Channel所标识的寄存器

}
//开启一次DMA传输
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx) { DMA_Cmd(DMA_CHx, DISABLE ); //关闭USART1 TX DMA1 所指示的通道 DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小 DMA_Cmd(DMA_CHx, ENABLE); //使能USART1 TX DMA1 所指示的通道 }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息