赞
踩
目录
关于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的优先级。
- 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; //外设数据宽度
- #define DMA_PeripheralDataSize_Byte //一个字节
- #define DMA_PeripheralDataSize_HalfWord //两个字节
- #define DMA_PeripheralDataSize_Word //四个字节
uint32_t DMA_MemoryDataSize; //存储器数据宽度
- #define DMA_MemoryDataSize_Byte //一个字节
- #define DMA_MemoryDataSize_HalfWord //两个字节
- #define DMA_MemoryDataSize_Word //四个字节
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);
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)
- {
-
- }
-
- }

实验描述:该实验通过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次。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。