DMA简介
用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。
DMA特性
- 12个独立的可配置的通道:DMA1有7个通道,DMA2有5个通道
- 每个通道都有直接连接专用硬件DMA请求,每个通道都同样支持软件触发。这些通道可以通过软件来配置
- 在同一个DMA模块上,多个请求间的优先级通过软件设置,优先级相等时由其硬件决定
- 独立数据源和目标数据区的传输宽度(字节、半字、全字)源和目标地址必须按照数据传输宽度对齐
- 支持循环的缓冲器管理
- 每个通道有三个事件标志(DMA半传输、DMA传输完成、DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求
- 存储器和存储器间,外设和存储器,存储器和外设之间的传输
- 闪存,SRAM,外设的SRAM,APB1,APB2和APB均可作为访问的源和目标
- 可编程的数据传输数目:最大65535
DMA处理
当发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先权处理请求。当DMA控制器开发访问发出请求的外设时,DMA控制器立即发送给它应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。如果有多个请求,外设可以启动下一个周期
DMA通道配置过程
下面是配置DMA通道x的过程(x代表通道号):
- 在DMA_CPARx寄存器中设置外设寄存器的地址。发生外设数据传输请求时,这个地址将是数据传输的源或目标。如果使用USART1 则地址是USART1数据寄存器即USART1_DR(偏移地址0x04)
- 在DMA_CMARx寄存器中设置数据存储器的地址。发生外设数据传输请求时,传输的数据将从这个地址读出或写入这个地址。
- 在DMA_CNDTRx寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减。
- 在DMA_CCRx寄存器的PL[1:0]位中设置通道的优先级。
- 在DMA_CCRx寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、外
设和存储器的数据宽度、传输一半产生中断或传输完成产生中断。 - 设置DMA_CCRx寄存器的ENABLE位,启动该通道。
DMA中断请求
每个DMA通道都可以在DMA传输过半,传输完成和传输错误时产生中断。
各通道的DMA请求
DMA1
从外设(TIMx[x=1、 2、 3、 4]、 ADC1、 SPI1、 SPI/I2S2、 I2Cx[x=1、 2]和USARTx[x=1、 2、 3])
产生的7个请求
由上表可知,USART1_TX使用的是Channel4,所以在配置DMA的时候需要在固件库中选择DMA1_Channel4
DMA2
从外设(TIMx[5、 6、 7、 8]、 ADC3、 SPI/I2S3、 UART4、 DAC通道1、 2和SDIO)产生的5个请求
void USARTx_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
//开启DMA1时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
//USART1使用DMA1,所以地址为USART1_DR
DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_BASE+0x04;
// 内存到外设
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
// 内存外设
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
// 传输大小1000
DMA_InitStructure.DMA_BufferSize = 1000;
// 外设地址不增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// 内存地址增加
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
// 外设数据单位
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
// 内存数据单位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
// DMA模式,正常模式,一次即可
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
// 优先级
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
// 内存到内存进制
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
// 配置DMA
DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);
//使能DMA
DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
}