DMA学习

简介

        直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传 输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。 两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自 于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

结构框图: 

从总线、通道、仲裁器这三个方面去分析

 

 

1、从第一部分可以看出,DMA是挂载在AHB总线上的一个外设 

2、一般设备有DMA1,在大容量、互联型中多出一个DMA2,在DMA1中拥有7个通道,DMA2中有5个通道,每个通道对应着各自的外设,意思是当外设想要使用DMA时,需要走专属的通道。同一时间只有一个通道能用。

3、仲裁器,类似于中断优先级的概念,用来处理多个外设同时请求时,先后顺序的问题。首先,DMA1通道所属的外设比DMA2上的优先级高,其次根据软件优先级(非常高、高、中、低)、再之后根据通道编号,数字越小,优先级越高。

初始化结构体

接下来由在固件库中定义的DMA结构体来介绍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;

一般传输时,会涉及到发送端和接收端的概念;

在DMA中传输方向有 从外设到存储器,从存储器到外设,从存储 器到存储器。具体的方向 DMA_CCR 位 4 DIR 配置:0 表示从外设到存储器,1 表示从存 储器到外设。这里面涉及到的外设地址由 DMA_CPAR 配置,存储器地址由DMA_CMAR 配置。

uint32_t DMA_PeripheralBaseAddr; 外设地址

uint32_t DMA_MemoryBaseAddr;     存储器地址

这两个结构体成员完成了传输时两个端是谁的说明

uint32_t DMA_DIR;

这句话完成了方向的说明

uint32_t DMA_BufferSize;           // 传输数目

数据的数量

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

增量模式意思是外设、存储器地址在传输的时候,是否需要改动地址

举个例子,外设 -> 存储器

外设是个数组,存储器是个变量,在传输时,外设需要传完第一个元素后,传下一个,这时就需要数组名+1,来指向下一个元素

  uint32_t DMA_PeripheralDataSize;   // 外设数据宽度
  uint32_t DMA_MemoryDataSize;       // 存储器数据宽度

字面意思,传输时数据单位的大小,这时要求没有特殊情况,要求数据大小一致,不然会产生数据丢失的情况

  uint32_t DMA_Mode;                 // 模式选择

传完数据之后,是停止,还是要一直循环传输下去
  uint32_t DMA_Priority;             // 通道优先级

对应仲裁器中介绍的内容,设置软件优先级
  uint32_t DMA_M2M;                  // 存储器到存储器模式

如果是存储器 -> 存储器,需要这里单独开启

实践部分

编程目的:

1、实现DMA的初始化配置

2、使用比较函数来判断外设地址和存储器地址是否相同,通过LED灯来在单片机上反映 

初始化代码:

void Dma_Config_MTM(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	// 开启外设DMA时钟
	RCC_AHBPeriphClockCmd(MTM_DMA_CLK, ENABLE);
	
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;
	DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)aDST_Buffer;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	
	DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
	DMA_InitStructure.DMA_MemoryInc  = DMA_PeripheralInc_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_Channel1, &DMA_InitStructure);
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
}

比较函数: 

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;  
}

使用的传输源、目标(看方向的,比如外设到存储器,那么外设就是源,存储器就是目标) 

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,0x45464748,0x494A4B4C,0x4D4E4F50,
                                    0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
                                    0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
                                    0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};


uint32_t aDST_Buffer[BUFFER_SIZE];

主函数:

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_dma.h"

#define SOFT_DELAY Delay(0x0FFFFF);

void Delay(__IO u32 nCount); 

extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];

int main(void)
{	
	int state;
	/* LED 端口初始化 */
	LED_GPIO_Config();	 
	Dma_Config_MTM();
	LED_YELLOW;
	SOFT_DELAY;
	
	state = Buffercmp(aSRC_Const_Buffer,aDST_Buffer, BUFFER_SIZE);
	if( state == 0 ) {
		LED_RED;
	} else {
		LED_GREEN;
	}
}

void Delay(__IO uint32_t nCount)	 //简单的延时函数
{
	for(; nCount != 0; nCount--);
}

<think>好的,用户想学习关于HAL DMA的知识,特别是入门教程和学习资料。首先,我需要整理HAL DMA的基础概念和相关资源。 首先,用户提供的引用中有提到DMA的基本定义和配置,比如引用[1]强调了DMA配置的重要性,建议阅读原理部分。引用[2]解释了__HAL_LINKDMA宏的作用,用于关联DMA和设备句柄。引用[3]展示了DMA在串口接收不定长数据中的应用,涉及中断处理和NDTR寄存器的使用。 接下来,我需要根据这些信息构建回答的结构。应该从基础概念开始,比如DMA的定义、工作原理,然后介绍HAL库中的相关函数和配置步骤。同时,结合用户提供的代码示例,说明实际应用中的注意事项,比如如何计算数据长度,处理IDLE中断等。 用户可能需要具体的步骤和示例,所以需要分步骤讲解,比如初始化流程、DMA配置、中断处理等。同时,提供学习资料,如官方文档、教程链接,以及常见问题的解决方法。 还要注意用户提到的系统级指令,比如使用正确的中文回答,生成相关问题。问题需要围绕HAL DMA的配置、函数使用、常见错误等,确保覆盖用户可能遇到的难点。 最后,检查是否引用了用户提供的三个引用,确保每个引用在适当的位置被提及,比如在讲解宏定义时引用[2],在串口接收示例时引用[3],以及在配置建议时引用[1]。</think>### HAL DMA 学习指南 #### 一、DMA基础概念 **DMA(直接内存访问)** 是微控制器中用于在**不占用CPU资源**的情况下实现外设与内存、内存与内存之间数据传输的模块。其核心作用是提升系统效率,减少中断延迟[^1]。 #### 二、HAL库中的DMA操作 1. **关键函数与宏** - **`__HAL_LINKDMA`**:用于将外设句柄(如UART、SPI)与DMA句柄绑定,例如: ```c __HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx); // 绑定UART1接收DMA ``` 该宏建立了外设与DMA的关联关系,便于后续传输控制[^2]。 2. **配置流程** - **CubeMX配置**:在图形界面中启用DMA通道,设置传输方向(内存到外设/外设到内存)、优先级和传输模式(循环/普通)。 - **代码初始化**:调用`HAL_DMA_Init()`初始化DMA参数,包括外设地址、内存地址和数据长度。 #### 三、典型应用场景 1. **串口不定长数据接收** - 通过**IDLE中断**检测数据帧结束,配合DMA计数器`NDTR`计算实际数据长度: ```c dmaDataTail = UART1_DMA_DATA_LEN - hdma_usart1_rx.Instance->NDTR; dmaDataLenght = (dmaDataTail - dmaDataHead) % UART1_DMA_DATA_LEN; // 环形缓冲区计算 ``` 此方法可避免频繁中断,提升实时性[^3]。 2. **内存到外设传输** - 使用`HAL_UART_Transmit_DMA()`实现非阻塞传输,需注意**传输完成中断(TC)** 的处理。 #### 四、学习资料推荐 1. **官方文档** - 《STM32 HAL库用户手册》(UM1850):详解DMA API接口 - 《参考手册》(RM0008):芯片特定DMA寄存器说明 2. **实践教程** - [STM32CubeIDE DMA配置教程](https://www.st.com/resource/en/user_manual/dm00105879.pdf) - [GitHub开源项目《HAL_DMA_Examples》](https://github.com/STM32Cube/STM32CubeF4/tree/master/Projects) #### 五、常见问题 1. **数据未更新** - 检查DMA传输方向是否配置错误(如外设为源却配置为目的地址) - 验证`__HAL_LINKDMA`是否遗漏绑定 2. **传输中断不触发** - 确认NVIC中断优先级设置 - 检查DMA中断使能标志(如TCIE、HTIE) §§ 相关问题 §§ 1. 如何判断DMA传输是否完成? 2. 多通道DMA如何避免冲突? 3. DMA传输时如何保证数据一致性? 4. 使用DMA时如何实现双缓冲机制?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值