DMA—直接储存区访问

发布于 2022-11-23  164 次阅读


DMA简介

DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据, 但是不需要占用CPU,即在传输数据的时候,CPU可以干其他的事情,好像是多线程一样。 数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是SRAM或者是FLASH。 DMA控制器包含了DMA1和DMA2,其中DMA1有7个通道,DMA2有5个通道,这里的通道可以理解为传输数据的一种管道。要注意的是DMA2只存在于大容量的单片机中。

DMA_InitTypeDef初始化结构体

typedef struct {

    uint32_t Direction;            //传输方向
    uint32_t PeriphInc;            //外设递增
    uint32_t MemInc;               //存储器递增
    uint32_t PeriphDataAlignment;  //外设数据宽度
    uint32_t MemDataAlignment;     //存储器数据宽度
    uint32_t Mode;                 //模式选择
    uint32_t Priority;             //优先级

} DMA_InitTypeDef;

储存器到储存器模式

 DMA宏定义及相关变量定义

/* 相关宏定义,使用存储器到存储器传输必须使用DMA2 */
 DMA_HandleTypeDef DMA_Handle;

 #define DMA_STREAM               DMA1_Channel6
 #define DMA_CHANNEL              DMA_CHANNEL_0 //存储器到存储器传输通道没有硬性规定,可以随意选择。
 #define DMA_STREAM_CLOCK()       __DMA1_CLK_ENABLE()

 #define BUFFER_SIZE              32

 /* 定义aSRC_Const_Buffer数组作为DMA传输数据源
 const关键字将aSRC_Const_Buffer数组变量定义为常量类型 */
 const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
     0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
     0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
     0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
     0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
     0x41424344,0x44564748,0x494A4B4C,0x4D4E4F50,
     0x51525345,0x55565758,0x595A5B5C,0x5D5E5F60,
     0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
     0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80
 };
 /* 定义DMA传输目标存储器 */
 uint32_t aDST_Buffer[BUFFER_SIZE];

aSRC_Const_Buffer[BUFFER_SIZE]定义用来存放源数据,并且使用了const关键字修饰,即常量类型,使得变量是存储在内部flash空间上

DMA数据配置

 static void DMA_Config(void)
 {
     HAL_StatusTypeDef DMA_status = HAL_ERROR;

     DMA_STREAM_CLOCK();
     //数据流选择
     DMA_Handle.Instance=DMA_STREAM;
     //存储器到外设HAL_DMA_Init(&DMA_Handle);
     DMA_Handle.Init.Direction=DMA_MEMORY_TO_MEMORY;
     //外设非增量模式/* Associate the DMA handle */
     DMA_Handle.Init.PeriphInc=DMA_PINC_ENABLE;
     //存储器增量模式__HAL_LINKDMA(&UartHandle, hdmatx,DMA_Handle);
     DMA_Handle.Init.MemInc=DMA_MINC_ENABLE;
     //外设数据长度:8位
     DMA_Handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_WORD;
     //存储器数据长度:8位
     DMA_Handle.Init.MemDataAlignment=DMA_MDATAALIGN_WORD;
     //外设普通模式,存储器到存储器模式通道选择没有具体规定,只能使用一次传输模式不能循环传输,最后我调用HAL_DMA_Init函数完成DMA数据流的初始化配置
     DMA_Handle.Init.Mode=DMA_NORMAL;
     //中等优先级
     DMA_Handle.Init.Priority=DMA_PRIORITY_MEDIUM;

     /* 完成DMA数据流参数配置 */
     HAL_DMA_Init(&DMA_Handle);
     //HAL_DMA_Start函数用于启动DMA数据流传输,源地址和目标地址使用之前定义的数组首地址,返回DMA传输状态。
     DMA_status = HAL_DMA_Start(&DMA_Handle,(uint32_t)aSRC_Const_Buffer,
                 (uint32_t)aDST_Buffer,BUFFER_SIZE);

     /* 判断DMA状态 */
     if (DMA_status != HAL_OK) {
         /* DMA出错就让程序运行下面循环:RGB彩色灯闪烁 */
         while (1) {
             LED_RED;
             Delay(0xFFFFFF);
             LED_RGBOFF;
             Delay(0xFFFFFF);
         }
     }
 }

存储器数据对比

uint8_t Buffercmp(const uint32_t* pBuffer,
                 uint32_t* pBuffer1, uint16_t BufferLength)
 {
     /* 数据长度递减 */
     while (BufferLength--) {
         /* 判断两个数据源是否对应相等 */
         if (*pBuffer != *pBuffer1) {
             /* 对应数据源不相等马上退出函数,并返回0 */
             return 0;
         }
         /* 递增两个数据源的地址指针 */
         pBuffer++;
         pBuffer1++;
     }
     /* 完成判断并且对应数据相对 */
     return 1;
 }

储存器到外设模式

 USART和DMA相关宏定义

//串口波特率
#define DEBUG_USART_BAUDRATE                    115200
//引脚定义
/*******************************************************/
#define DEBUG_USART                             USART1
#define DEBUG_USART_CLK_ENABLE()                 __HAL_RCC_USART1_CLK_ENABLE();
#define DEBUG_USART_RX_GPIO_PORT                GPIOA
#define DEBUG_USART_RX_GPIO_CLK_ENABLE()           __HAL_RCC_GPIOA_CLK_ENABLE()
#define DEBUG_USART_RX_PIN                      GPIO_PIN_10


#define DEBUG_USART_TX_GPIO_PORT                GPIOA
#define DEBUG_USART_TX_GPIO_CLK_ENABLE()           __HAL_RCC_GPIOA_CLK_ENABLE()
#define DEBUG_USART_TX_PIN                      GPIO_PIN_9


#define DEBUG_USART_IRQHandler                  USART1_IRQHandler
#define DEBUG_USART_IRQ                 		USART1_IRQn
/************************************************************/
//DMA
#define SENDBUFF_SIZE                     		1000//发送的数据量
#define DEBUG_USART_DMA_CLK_ENABLE()      		__HAL_RCC_DMA1_CLK_ENABLE();	
#define DEBUG_USART_DMA_STREAM            		DMA1_Channel4

USART GPIO 配置

void Debug_USART_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
      
  DEBUG_USART_RX_GPIO_CLK_ENABLE();
  DEBUG_USART_TX_GPIO_CLK_ENABLE();
  /* 使能 UART 时钟 */
  DEBUG_USART_CLK_ENABLE();
  
   /* 配置Tx引脚为复用功能  */
  GPIO_InitStruct.Pin = DEBUG_USART_TX_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

  HAL_GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStruct);
  
  /* 配置Rx引脚为复用功能 */
  GPIO_InitStruct.Pin = DEBUG_USART_RX_PIN;
    GPIO_InitStruct.Mode=GPIO_MODE_AF_INPUT;	//模式要设置为复用输入模式!	
  HAL_GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStruct);
  
  
  UartHandle.Instance          = DEBUG_USART;
  UartHandle.Init.BaudRate     = DEBUG_USART_BAUDRATE;
  UartHandle.Init.WordLength   = UART_WORDLENGTH_8B;
  UartHandle.Init.StopBits     = UART_STOPBITS_1;
  UartHandle.Init.Parity       = UART_PARITY_NONE;
  UartHandle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
  UartHandle.Init.Mode         = UART_MODE_TX_RX;
  
  HAL_UART_Init(&UartHandle); 
}
///重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
	/* 发送一个字节数据到串口DEBUG_USART */
	HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, 1000);	
	
	return (ch);
}

///重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		
	int ch;
	HAL_UART_Receive(&UartHandle, (uint8_t *)&ch, 1, 1000);	
	return (ch);
}

USART1 发送请求DMA设置

void USART_DMA_Config(void)
{
  DEBUG_USART_DMA_CLK_ENABLE();  
     
  DMA_Handle.Instance=DEBUG_USART_DMA_STREAM;                            //数据流选择
                             
  DMA_Handle.Init.Direction=DMA_MEMORY_TO_PERIPH;             //存储器到外设HAL_DMA_Init(&DMA_Handle);
  DMA_Handle.Init.PeriphInc=DMA_PINC_DISABLE;                 //外设非增量模式/* Associate the DMA handle */
  DMA_Handle.Init.MemInc=DMA_MINC_ENABLE;                     //存储器增量模式__HAL_LINKDMA(&UartHandle, hdmatx, DMA_Handle); 
  DMA_Handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;    //外设数据长度:8位
  DMA_Handle.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE;       //存储器数据长度:8位
  DMA_Handle.Init.Mode=DMA_NORMAL;                            //外设普通模式
  DMA_Handle.Init.Priority=DMA_PRIORITY_MEDIUM;               //中等优先级
  
  HAL_DMA_Init(&DMA_Handle);
  /* 关联 DMA 句柄 */
  __HAL_LINKDMA(&UartHandle, hdmatx, DMA_Handle); 
  
}

USART1 向 DMA发出TX请求

 HAL_UART_Transmit_DMA(&UartHandle, (uint8_t *)SendBuff ,
                         SENDBUFF_SIZE);

HAL_UART_Transmit_DMA函数用于启动USART的DMA传输。只需要指定源数据地址及长度,运行该函数后USART的DMA发送传输就开始了,根据配置它会通过USART循环发送数据。


白日梦想家