目录
1.DMA功能讲解
2.DMA初始化结构体
3.DMA实验1 (M->M)
4.DMA实验2(M->P)
1.DMA功能讲解
关于DMA的功能手册上是这么说的。
说白了就是DMA可以把数据从一个地方传输到另一个地方,并且不占用CPU。举个例子,比如我们想通过串口发送数据,正常来讲是存放到Flash或SRAM的代码通过总线矩阵,由Cortex-M3的内核进行控制,再回到系统总线的外设当中。如果有DMA的话,Flash和SRAM中的数据可以直接通过总线矩阵访问到DMA可以直接和串口的外设发送数据,可以不用经过CPU。
DMA1有七个通道如下图所示:
并且DMA1支持P->P(内存到内存的访问,如Flash到SRAM或SRAM到Flash),P->M(外设到内存的访问,如ADC的采样),M->P(内存到外设的访问,如将变量发送到串口的寄存器当中)。DMA2也拥有同样的功能,但是只存在大容量,互联型(F105/F107)的产品中。
而DMA2只有五个通道:
多个DMA同时请求
1.软件阶段:可以通过配置DMA通道x配置寄存器(DMA_CCRx)的PL位设置通道优先级。
2.硬件阶段:如果用到两个通道且通道优先级一致,那么我们可以通过比较编号的大小来比较优先级,DMA1的优先级大于DMA2的优先级。再比如都是同一个通道内的,串口1在通道4,串口2在通道7,那么串口1的优先级就要大于串口2的优先级。
2.DMA初始化结构体
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; //外设地址
uint32_t DMA_MemoryBaseAddr; //存储器地址
uint32_t DMA_DIR; //传输方向
uint32_t DMA_BufferSize; //传输数目
uint32_t DMA_PeripheralInc; //外设地址增量模式
uint32_t DMA_MemoryInc; //存储器地址增量模式
uint32_t DMA_PeripheralDataSize; //外设数据宽度
uint32_t DMA_MemoryDataSize; //存储器数据宽度
uint32_t DMA_Mode; //模式选择
uint32_t DMA_Priority; //通道优先级
uint32_t DMA_M2M; //存储器到存储器模式
}DMA_InitTypeDef;
可以将以上的结构体分为三类理解。
1.数据输入与输出
uint32_t DMA_PeripheralBaseAddr; //外设地址
uint32_t DMA_MemoryBaseAddr; //存储器地址
uint32_t DMA_DIR; //传输方向
1.配置外设地址参考外设地址寄存器:DMA_CPARx
2.配置存储器地址参考外设存储器地址寄存器 DMA_CMARx
3.配置传输方式参考DMA通道配置寄存器DIR位。
2.数据大小以及传输单位
uint32_t DMA_BufferSize; //传输数目
uint32_t DMA_PeripheralInc; //外设地址增量模式
uint32_t DMA_MemoryInc; //存储器地址增量模式
uint32_t DMA_PeripheralDataSize; //外设数据宽度
uint32_t DMA_MemoryDataSize; //存储器数据宽度
传输数目参考DMA通道数据存储器,该寄存器总共有16位,所以可以传输2的16次方(65535)个数据。每个数据的大小可以通过DMA通道配置寄存器PSIZE位。接收数据存储器的宽度同样也可以通过MSIZE位来配置。 一般性情况接收和发送的大小我们配成一样的即可。
配置外设地址增量模式和存储器地址增量模式可以配置MINC位和PINC位,用来决定发送的时候存储器地址是否会递增。
3.传输结束
uint32_t DMA_Mode; //模式选择
模式选择:配置DMA_CCRx寄存器的CIRC位。
循环模式:发送完一组数据之后是否再重新发送该数据。
如果不配置循环模式则发送完一次后就不再发送了。
结构体参数详解
uint32_t DMA_DIR; //传输方向
#define DMA_DIR_PeripheralDST //外设作为目标
#define DMA_DIR_PeripheralSRC //外设作为源
uint32_t DMA_PeripheralInc; //外设地址增量模式
#define DMA_PeripheralInc_Enable //DMA外设增量使能
#define DMA_PeripheralInc_Disable //DMA外设增量失能
uint32_t DMA_MemoryInc; //存储器地址增量模式
#define DMA_MemoryInc_Enable //存储器地址增量使能
#define DMA_MemoryInc_Disable //存储器地址增量失能
uint32_t DMA_PeripheralDataSize; //外设数据宽度
uint32_t DMA_Mode; //模式选择
#define DMA_Mode_Circular //循环模式
#define DMA_Mode_Normal //只发送一次
uint32_t DMA_Priority; //通道优先级
uint32_t DMA_M2M; //存储器到存储器模式
DMA结构体初始化
DMA_Init(DMA1_Channel6,&DMA_InitStructure);
DMA传输标志位--------------选择发送接收标志位
DMA_ClearFlag(DMA1_FLAG_TC6);
DMA使能
DMA_Cmd(DMA1_Channel6, ENABLE);
3.DMA实验1 (M->M)
M->M:Flash to SRAM,内部Flash(CODE)的数据传输到内部的SRAM(变量)。
对于STM32来说在SRAM中存储的是变量,Flash中存储的是常量,该实验是将常量放到SRAM中。
实验现象:如果成功将定义在Flash中的数组存储在SRAM中,则开发板上的LED灯会亮。
dma.c
#include "stm32f10x.h"
#include "dma.h"
const uint32_t SRC_Buffer[Buffer_Size]={
0x01020304,0x01220364,0x01F20304,0x01E20205,
0x01E25204,0x01A20B54,0x0E02F324,0x01E2630F,
0x0602A30B,0x0E0B0564,0x0E0B0794,0x07060E0F,
0x01AB040D,0x0F09070E,0x0A02536F,0x0904060E
}; //const关键字把数组Buffer定义为常量类型,表示数据存储在内部Flash里
uint32_t DST_Buffer[Buffer_Size]; //定义DMA传输的目标存储器,存在内部SRAM里
void DMA_MTM_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Buffer;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_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_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_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; //完成判断并且对应数据相等,返回1
dma.h
#ifndef _DMA_H_
#define _DMA_H_
#define Buffer_Size 16
void DMA_MTM_Init(void);
uint8_t Buffercmp(const uint32_t *pBuffer1,uint32_t *pBuffer2,uint32_t BufferLength);
#endif
main.c
#include "stm32f10x.h"
#include "main.h"
#include "dma.h"
#include "led.h"
int main(void)
{
uint8_t status = 0;
extern const uint32_t SRC_Buffer[Buffer_Size];
extern uint32_t DST_Buffer[Buffer_Size];
LED_Init();
DMA_MTM_Init();
status = Buffercmp(SRC_Buffer, DST_Buffer, Buffer_Size);
if(status == 0){
GPIO_SetBits(GPIOB, GPIO_Pin_5);
}
else {
GPIO_ResetBits(GPIOB, GPIO_Pin_5);
}
while(1)
{
}
}
4.DMA实验2(M->P)
实验描述:该实验通过DMA串口发送数据,在串口助手上看到响应的现象。
该实验是将外设作为目标存储器,将内存中的常量发送到外设。
在dma.c的文件下添加下段代码,该段代码跟实验1中的DMA结构体配置方法一致,但跟实验1不同的是需要把传输方法改成将外设作为目标,外设地址增量模式关闭,因为地址是固定的,都是放在了串口数据寄存器当中。还需要把数据宽度改成8位,因为串口数据寄存器的大小就是8位的。最后就是要关闭M->M。
void USART_DMA(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Sendbuf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = SEND_SIZE;
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_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4,&DMA_InitStructure);
DMA_ClearFlag(DMA1_FLAG_TC4);
DMA_Cmd(DMA1_Channel4, ENABLE);
}
dma.h
#ifndef _DMA_H_
#define _DMA_H_
#define Buffer_Size 16
#define USART_DR_ADDR (USART1_BASE+0x04) //数据寄存器地址=基地址加偏移量
#define SEND_SIZE 500 //大小定义为500
void DMA_MTM_Init(void);
uint8_t Buffercmp(const uint32_t *pBuffer1,uint32_t *pBuffer2,uint32_t BufferLength);
void USART_DMA(void);
#endif
main.c
#include "stm32f10x.h"
#include "main.h"
#include "dma.h"
#include "led.h"
#include "usart.h"
int main(void)
{
extern uint8_t Sendbuf[SEND_SIZE];
uint16_t i = 0;
for(i=0;i<SEND_SIZE;i++) //发送500次
{
Sendbuf[i] = 'o'; //发送的数据为o
}
usart_init();
USART_DMA();
USART_DMACmd(USART1, USART_DMAReq_Tx,ENABLE); //串口DMA使能
while(1)
{
}
}
实验结果:
可以看出发送的数据为o,并且发送了500次。
评论(0)
您还未登录,请登录后发表或查看评论