STM32 DMA模块的配置与使用
扫描二维码
随时随地手机看文章
DMA有什么用?
直接存储器存取用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。
有多少个DMA资源?
有两个DMA控制器,DMA1有7个通道,DMA2有5个通道。
数据从什么地方送到什么地方?
外设到SRAM(I2C/UART等获取数据并送入SRAM);
SRAM的两个区域之间;
外设到外设(ADC读取数据后送到TIM1控制其产生不同的PWM占空比);
SRAM到外设(SRAM中预先保存的数据送入DAC产生各种波形);
……还有一些目前还搞不清楚的。
DMA可以传递多少数据?
传统的DMA的概念是用于大批量数据的传输,但是我理解,在STM32中,它的概念被扩展了,也许更多的时候快速是其应用的重点。数据可以从1~65535个。
直接存储器存取(Direct Memory Access,DMA)是计算机科学中的一种内存访问技术。它允许某些电脑内部的硬体子系统(电脑外设),可以独立地直接读写系统存储器,而不需绕道 CPU。在同等程度的CPU负担下,DMA是一种快速的数据传送方式。它允许不同速度的硬件装置来沟通,而不需要依于 CPU的大量中断请求。【摘自Wikipedia】
现在越来越多的单片机采用DMA技术,提供外设和存储器之间或者存储器之间的高速数据传输。当 CPU 初始化这个传输动作,传输动作本身是由DMA 控制器来实行和完成。STM32就有一个DMA控制器,它有7个通道,每个通道专门用来管理一个或多个外设对存储器访问的请求,还有一个仲裁器来协调各个DMA请求的优先权。
DMA 控制器和Cortex-M3核共享系统数据总线执行直接存储器数据传输。当CPU和DMA同时访问相同的目标(RAM或外设)时,DMA请求可能会停止 CPU访问系统总线达若干个周期,总线仲裁器执行循环调度,以保证CPU至少可以得到一半的系统总线(存储器或外设)带宽。
在发生一个事件后,外设发送一个请求信号到DMA控制器。DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问外设的时候,DMA控制器立即发送给外设一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。如果发生更多的请求时,外设可以启动下次处理。
简单来说:
DMA,全称为:Direct Memory Access,即直接存储器访问。DMA传输方式无需CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM 与I/O设备开辟一条直接传送数据的通路,能使CPU 的效率大为提高。
STM32中 DMA1有7个通道,DMA2有5个通道(DMA2 仅存在大容量产品中)。DMA挂载的时钟为AHB总线,其时钟为72Mhz,所以可以实现高速数据搬运。
STM32F103RBT6 只有1 个DMA控制器,DMA1 ,下面我们就针对DMA1 进行介绍。
从外设(TIMx、ADC、SPIx 、I2Cx 和USARTx )产生的DMA请求,通过逻辑或输入到DMA控制器,这就意味着同时只能有一个请求有效。外设的DMA请求,可以通过设置相应的外设寄存器中的控制位,被独立地开启或关闭。
DMA1各通道一览:
这里我们要使用的是串口 1 的 DMA 传送,也就是要用到通道 4。
1、DMA的配置
要配置的有DMA传输通道选择,传输的成员和方向、普通模式还是循环模式等等。
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
//DMA设置:
//设置DMA源:内存地址&串口数据寄存器地址
//方向:内存-->外设
//每次传输位:8bit
//传输大小DMA_BufferSize=SENDBUFF_SIZE
//地址自增模式:外设地址不增,内存地址自增1
//DMA模式:一次传输,非循环
//优先级:中
DMA_DeInit(DMA1_Channel4);//串口1的DMA传输通道是通道4
DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
DMA_InitStructure.DMA_DIR =DMA_DIR_PeripheralDST;//外设作为DMA的目的端
DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;//传输大小
DMA_InitStructure.DMA_PeripheralInc =DMA_PeripheralInc_Disable;//外设地址不增加
DMA_InitStructure.DMA_MemoryInc =DMA_MemoryInc_Enable;//内存地址自增1
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode =DMA_Mode_Circular;
//DMA_Mode_Normal(只传送一次),DMA_Mode_Circular (不停地传送)
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//(DMA传送优先级为中等)
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
}
注:
1、传输通道:通过查表,串口1的发送对应的是DMA的通道4,所以此处选择通道4.
2、DMA传输方式:
(1) DMA_Mode_Normal,正常模式,当一次DMA数据传输完后,停止DMA传送,对于上例而言,就是DMA_PeripheralDataSize_Byte个字节的传送完成后,就停止传送。
(2)DMA_Mode_Circular
循环模式,当传输完一次后,重新接着传送,永不停息。
2、外设的DMA方式设置
将串口1设置成DMA模式:
每一个外设都有一个类似以下的一个DMA调用函数:xxx_DMACmd();
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);//发送就为USART_DMAReq_Tx;读取就为USART_DMAReq_Rx
3、待传输数据的定义和初始化
#define SENDBUFF_SIZE 10240
vu8 SendBuff[SENDBUFF_SIZE];
for(i=0;i
SendBuff[i] = i%10+'0';
}
4、开始DMA传输(使能对应的DMA通道)
DMA_Cmd(DMA1_Channel4, ENABLE);
5、DMA传输的完成
while(DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET)
{
LED_1_REV; //LED翻转
Delay(); //浪费时间
}
当传输完成后,就会跳出上面的死循环。
当然,使用串口作为外设的时候,还需要对串口进行初始化。