当前位置:   article > 正文

学习笔记—STM32之DMA简介及相关结构体概述_dma结构体

dma结构体

目录

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初始化结构体

  1. typedef struct
  2. {
  3. uint32_t DMA_PeripheralBaseAddr; //外设地址
  4. uint32_t DMA_MemoryBaseAddr; //存储器地址
  5. uint32_t DMA_DIR; //传输方向
  6. uint32_t DMA_BufferSize; //传输数目
  7. uint32_t DMA_PeripheralInc; //外设地址增量模式
  8. uint32_t DMA_MemoryInc; //存储器地址增量模式
  9. uint32_t DMA_PeripheralDataSize; //外设数据宽度
  10. uint32_t DMA_MemoryDataSize; //存储器数据宽度
  11. uint32_t DMA_Mode; //模式选择
  12. uint32_t DMA_Priority; //通道优先级
  13. uint32_t DMA_M2M; //存储器到存储器模式
  14. }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;  //传输方向

  1. #define DMA_DIR_PeripheralDST //外设作为目标
  2. #define DMA_DIR_PeripheralSRC //外设作为源

uint32_t DMA_PeripheralInc;  //外设地址增量模式

  1. #define DMA_PeripheralInc_Enable //DMA外设增量使能
  2. #define DMA_PeripheralInc_Disable //DMA外设增量失能

uint32_t DMA_MemoryInc;   //存储器地址增量模式

  1. #define DMA_MemoryInc_Enable //存储器地址增量使能
  2. #define DMA_MemoryInc_Disable //存储器地址增量失能

 uint32_t DMA_PeripheralDataSize;   //外设数据宽度 

  1. #define DMA_PeripheralDataSize_Byte //一个字节
  2. #define DMA_PeripheralDataSize_HalfWord //两个字节
  3. #define DMA_PeripheralDataSize_Word //四个字节

  uint32_t DMA_MemoryDataSize;       //存储器数据宽度

  1. #define DMA_MemoryDataSize_Byte //一个字节
  2. #define DMA_MemoryDataSize_HalfWord //两个字节
  3. #define DMA_MemoryDataSize_Word //四个字节

uint32_t DMA_Mode;                 //模式选择 

  1. #define DMA_Mode_Circular //循环模式
  2. #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

  1. #include "stm32f10x.h"
  2. #include "dma.h"
  3. const uint32_t SRC_Buffer[Buffer_Size]={
  4. 0x01020304,0x01220364,0x01F20304,0x01E20205,
  5. 0x01E25204,0x01A20B54,0x0E02F324,0x01E2630F,
  6. 0x0602A30B,0x0E0B0564,0x0E0B0794,0x07060E0F,
  7. 0x01AB040D,0x0F09070E,0x0A02536F,0x0904060E
  8. }; //const关键字把数组Buffer定义为常量类型,表示数据存储在内部Flash里
  9. uint32_t DST_Buffer[Buffer_Size]; //定义DMA传输的目标存储器,存在内部SRAM里
  10. void DMA_MTM_Init(void)
  11. {
  12. DMA_InitTypeDef DMA_InitStructure;
  13. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  14. DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Buffer;
  15. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
  16. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  17. DMA_InitStructure.DMA_BufferSize = Buffer_Size;
  18. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
  19. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  20. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  21. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  22. DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  23. DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  24. DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
  25. DMA_Init(DMA1_Channel6,&DMA_InitStructure);
  26. DMA_ClearFlag(DMA1_FLAG_TC6);
  27. DMA_Cmd(DMA1_Channel6, ENABLE);
  28. }
  29. uint8_t Buffercmp(const uint32_t *pBuffer1,uint32_t *pBuffer2,uint32_t BufferLength)
  30. {
  31. while(BufferLength--) //数据长度递减
  32. {
  33. if(*pBuffer1 != *pBuffer2) //判断两个数据源是否相等
  34. {
  35. return 0; //对应数据源不相等返回0
  36. }
  37. pBuffer1++; //递增两个数据源的地址指针
  38. pBuffer2++;
  39. }
  40. return 1; //完成判断并且对应数据相等,返回1

dma.h

  1. #ifndef _DMA_H_
  2. #define _DMA_H_
  3. #define Buffer_Size 16
  4. void DMA_MTM_Init(void);
  5. uint8_t Buffercmp(const uint32_t *pBuffer1,uint32_t *pBuffer2,uint32_t BufferLength);
  6. #endif

main.c

  1. #include "stm32f10x.h"
  2. #include "main.h"
  3. #include "dma.h"
  4. #include "led.h"
  5. int main(void)
  6. {
  7. uint8_t status = 0;
  8. extern const uint32_t SRC_Buffer[Buffer_Size];
  9. extern uint32_t DST_Buffer[Buffer_Size];
  10. LED_Init();
  11. DMA_MTM_Init();
  12. status = Buffercmp(SRC_Buffer, DST_Buffer, Buffer_Size);
  13. if(status == 0){
  14. GPIO_SetBits(GPIOB, GPIO_Pin_5);
  15. }
  16. else {
  17. GPIO_ResetBits(GPIOB, GPIO_Pin_5);
  18. }
  19. while(1)
  20. {
  21. }
  22. }

4.DMA实验2(M->P)

实验描述:该实验通过DMA串口发送数据,在串口助手上看到响应的现象。

该实验是将外设作为目标存储器,将内存中的常量发送到外设。

在dma.c的文件下添加下段代码,该段代码跟实验1中的DMA结构体配置方法一致,但跟实验1不同的是需要把传输方法改成将外设作为目标,外设地址增量模式关闭,因为地址是固定的,都是放在了串口数据寄存器当中。还需要把数据宽度改成8位,因为串口数据寄存器的大小就是8位的。最后就是要关闭M->M。

  1. void USART_DMA(void)
  2. {
  3. DMA_InitTypeDef DMA_InitStructure;
  4. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  5. DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDR;
  6. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Sendbuf;
  7. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  8. DMA_InitStructure.DMA_BufferSize = SEND_SIZE;
  9. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  10. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  11. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  12. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  13. DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  14. DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  15. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  16. DMA_Init(DMA1_Channel4,&DMA_InitStructure);
  17. DMA_ClearFlag(DMA1_FLAG_TC4);
  18. DMA_Cmd(DMA1_Channel4, ENABLE);
  19. }

dma.h

  1. #ifndef _DMA_H_
  2. #define _DMA_H_
  3. #define Buffer_Size 16
  4. #define USART_DR_ADDR (USART1_BASE+0x04) //数据寄存器地址=基地址加偏移量
  5. #define SEND_SIZE 500 //大小定义为500
  6. void DMA_MTM_Init(void);
  7. uint8_t Buffercmp(const uint32_t *pBuffer1,uint32_t *pBuffer2,uint32_t BufferLength);
  8. void USART_DMA(void);
  9. #endif

main.c

  1. #include "stm32f10x.h"
  2. #include "main.h"
  3. #include "dma.h"
  4. #include "led.h"
  5. #include "usart.h"
  6. int main(void)
  7. {
  8. extern uint8_t Sendbuf[SEND_SIZE];
  9. uint16_t i = 0;
  10. for(i=0;i<SEND_SIZE;i++) //发送500次
  11. {
  12. Sendbuf[i] = 'o'; //发送的数据为o
  13. }
  14. usart_init();
  15. USART_DMA();
  16. USART_DMACmd(USART1, USART_DMAReq_Tx,ENABLE); //串口DMA使能
  17. while(1)
  18. {
  19. }
  20. }

实验结果:

可以看出发送的数据为o,并且发送了500次。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/888776
推荐阅读
相关标签
  

闽ICP备14008679号