STM32之DMA使用
下面分别以存储器到存储器和存储器到外设(串口)之间的使用进行演示
Learn From 江江STM32
一、DMA简介
直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。
两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。
二、DMA主要特性
12个独立的可配置的通道(请求):DMA1有7个通道,DMA2有5个通道
● 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
● 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。
● 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。
● 支持循环的缓冲器管理
● 每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。
● 存储器和存储器间的传输
● 外设和存储器、存储器和外设之间的传输
● 闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标。
● 可编程的数据传输数目:最大为65535
三、功能框图
四、DMA通道
每个通道都可以在有固定地址的外设寄存器和存储器地址之间执行DMA传输。
DMA传输的数据量是可编程的,最大达到65535。
包含要传输的数据项数量的寄存器,在每次传输后递减。
4.1可编程的数据量
外设和存储器的传输数据量可以通过DMA_CCRx寄存器中的PSIZE和MSIZE位编程。
4.24.2指针增量
4.34.3配置过程
下面是配置DMA通道x的过程(x代表通道号):
- 在DMA_CPARx寄存器中设置外设寄存器的地址。发生外设数据传输请求时,这个地址将是数据传输的源或目标。2. 在DMA_CMARx寄存器中设置数据存储器的地址。发生外设数据传输请求时,传输的数据将从这个地址读出或写入这个地址。3. 在DMA_CNDTRx寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减。4. 在DMA_CCRx寄存器的PL[1:0]位中设置通道的优先级。5. 在DMA_CCRx寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、外设和存储器的数据宽度、传输一半产生中断或传输完成产生中断。6. 设置DMA_CCRx寄存器的ENABLE位,启动该通道。
一旦启动了DMA通道,它既可响应连到该通道上的外设的DMA请求。
当传输一半的数据后,半传输标志(HTIF)被置1,当设置了允许半传输中断位(HTIE)时,将产生一个中断请求。在数据传输结束后,传输完成标志(TCIF)被置1,当设置了允许传输完成中断位(TCIE)时,将产生一个中断请求。
4.4循环模式
4.5存储器到存储器模式
五、DMA处理
在发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期。
总之,每次DMA传送由3个操作组成:
● 从外设数据寄存器或者从当前外设/存储器地址寄存器指示的存储器地址取数据,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。
● 存数据到外设数据寄存器或者当前外设/存储器地址寄存器指示的存储器地址,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。
● 执行一次DMA_CNDTRx寄存器的递减操作,该寄存器包含未完成的操作数目
六、DMA中断
七、DMA请求映象
八、DMA寄存器
代码演示
dma.h
#ifndef _DMA_H_
#define _DMA_H_
#include "stm32f10x.h"
void DMA_MTM_Init(void);
uint8_t Buffercmp(const uint32_t * pBuffer1,uint32_t *pBuffer2,uint32_t BufferLength);
void DMA_USART_Init(void);
#define USART_DR_ADDR (USART1_BASE + 0x04)
#define BUFFER_SIZE 4
#define Send_SIZE 50
#endif
dma.c
#include "dma.h"
const uint32_t SRC_Buffer[BUFFER_SIZE] = {0x00000001,0x10101010,0x00111010,0x00111101};
uint32_t DES_Buffer[BUFFER_SIZE];
uint8_t Send_Buffer[Send_SIZE];
void DMA_MTM_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
//时钟配置
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
//DMA结构体配置
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) SRC_Buffer; //外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) DES_Buffer; //存储器地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向
DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; //传输数目
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//外设地址增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器地址增量模式
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; //存储器数据宽度
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//外设数据宽度
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //模式选择
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //通道优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable; //存储器到存储器模式
DMA_Init(DMA1_Channel6,&DMA_InitStructure);
DMA_ClearFlag(DMA1_FLAG_TC6);
DMA_Cmd(DMA1_Channel6,ENABLE);
}
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; //完成 并相等
}
void DMA_USART_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
//时钟配置
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
//结构体配置
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Send_Buffer;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDR;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = Send_SIZE;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4,&DMA_InitStructure);
DMA_ClearFlag(DMA1_FLAG_TC4);
DMA_Cmd(DMA1_Channel4,ENABLE);
}
main.c
#include "stm32f10x.h"
#include "led.h"
#include "dma.h"
#include "SysTick.h"
#include "usart.h"
extern const uint32_t SRC_Buffer[BUFFER_SIZE] ;
extern uint32_t DES_Buffer[BUFFER_SIZE];
extern uint8_t Send_Buffer[Send_SIZE];
void Delay(unsigned int t)
{
while(t--);
}
int main(void)
{
uint16_t i = 0;
Usart_Init();
DMA_USART_Init();
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
//打开串口助手可以观察到数据
for(i = 0; i< Send_SIZE;i++)
{
Send_Buffer[i] = '0';
}
/*
uint8_t status = 0;
DMA_MTM_Init();
Led_Init();
status = Buffercmp(SRC_Buffer,DES_Buffer,BUFFER_SIZE);
if(status == 0)
{
GPIO_SetBits(GPIOC,GPIO_Pin_13);
}
else
{
GPIO_ResetBits(GPIOC,GPIO_Pin_13);
}
*/
while(1)
{
}
}