DMA---直接存储器访问

本文详细介绍了DMA(DirectMemoryAccess)的概念、工作方式、结构框图、相关寄存器功能以及在STM32中如何配置和使用DMA进行内存到内存的数据传输。此外,还提供了DMA配置步骤和编程实战示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.DMA简介

DMA,全称Direct Memory Access,即直接存储器访问;

DMA传输:将数据从一个地址空间复制到另一个地址空间,数据搬运工

传输方式:

  • 内存--->外设
  • 外设--->内存
  • 内存--->内存

DMA传输无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。

作用:为CPU减负。

二.DMA结构框图介绍

                                                DMA就是建立数据传输通道

三.DMA相关寄存器介绍

寄存器名称作用
DMA_CCRxDMA通道x配置寄存器用于配置DMA(核心控制寄存器)
DMA_ISRDMA中断状态寄存器用于查询当前DMA传输状态
DMA_IFCRDMA中断标志清除寄存器用来清除DMA_ISR对应位
DMA_CNDTRxDMA通道x传输数量寄存器用于控制DMA通道x每次传输的数据量
DMA_CPARxDMA通道x外设地址寄存器用于存储STM32外设地址
DMA_CMARxDMA通道x存储器地址寄存器用于存放存储器的地址
USART_CR3USART控制寄存器3用于使能串口DMA发送

数据传输方向:位14、位4;

通道优先级:位13:12;

循环模式:位5;

外设/存储器增量模式:位7、位6;

外设/存储器数据宽度:位11:10、位9:8;

中断使能:位3、位2、位1;

通道开启:位0

 查询当前DMA传输状态;

最大数据传输数目:65535;

非循环模式下传输结束后,要开始新的DMA传输,需要在关闭DMA通道情况下, 在该寄存器下重新写入传输数目。

 从哪里传?传哪里去?

 四.DMA相关HAL库驱动介绍

驱动函数关联寄存器功能描述
__HAL_RCC_DMAx_CLK_ENABLE()RCC_AHBENR使能DMAx时钟
HAL_DMA_Iinit()DMA_CCR初始化DMA
HAL_DMA_Start_IT()DMA_CCR/CPAR/CMAR/CNDTR开始DMA传输
__HAL_LINKDMA()用来连接DMA和外设句柄
HAL_UART_Transmit_DMA()CCR/CPAR/CMAR/CNDTR/USART_CR3使能DMA发送,启动传输
__HAL_DMA_GET_FLAG()DMA_ISR查询DMA传输通道的状态
__HAL_DMA_ENABLE()DMA_CCR(EN)使能DMA外设
__HAL_DMA_DISABLE()DMA_CCR(EN)失能DMA外设

 DMA外设相关结构体:DMA_HandleTypeDef 和 DMA_InitTypeDef

五.DMA配置步骤(串口)

  1.  使能DMA时钟:__HAL_RCC_DMA1_CLK_ENABLE;
  2. 初始化DMA:HAL_DMA_Init函数初始化DMA相关参数,__HAL_LINKDMA函数连接DMA和外设;
  3. 使能串口的DMA发送,启动传输:HAL_UART_Transmit_DMA;
  4. 查询DMA传输状态:__HAL_DMA_GET_FLAG查询通道传输状态;__HAL_DMA_GET_COUNTER获取当前传输剩余数据量;
  5. DMA中断使用:HAL_NVIC_EnableIRQ,HAL_NVIC_SetPriority,编写中断服务函数xxx_IRQHandler

六.编程实战

实验目标:实现内部RAM中内存到内存的数据传输。

dma.c

#include "dma.h"

uint8_t src_buf[10] = {0x0a,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};
uint8_t dest_buf[10] = {0};

DMA_HandleTypeDef	dma_handler;



void dma_init(void)/*内存到内存*/
{
	__HAL_RCC_DMA1_CLK_ENABLE();/*使能DMA1时钟*/
	
	dma_handler.Instance = DMA1_Channel1;/*选择DMA通道*/
	
	dma_handler.Init.Direction = DMA_MEMORY_TO_MEMORY;/*选择DMA传输方向*/
	dma_handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;/*数据传输宽度*/
	dma_handler.Init.MemInc = DMA_MINC_ENABLE;/*选择是否增量模式*/
	
	dma_handler.Init.Mode = DMA_NORMAL;/*是否循环,内存到内存是不支持的*/
	dma_handler.Init.PeriphDataAlignment =DMA_MDATAALIGN_BYTE; /*外设数据宽度*/
	dma_handler.Init.PeriphInc = DMA_MINC_ENABLE;/*外设地址是否增量*/
	dma_handler.Init.Priority = DMA_PRIORITY_HIGH; /*DMA通道优先级*/
	
	HAL_DMA_Init(&dma_handler);/*初始化DMA*/
	
	HAL_DMA_Start(&dma_handler,(uint32_t)src_buf,(uint32_t)dest_buf,0);/*确定源地址与目标地址*/
}

void dma_transmit(uint16_t cndtr)/*DMA发送数据函数*/
{
	__HAL_DMA_DISABLE(&dma_handler);/*失能DMA通道*/
	
	dma_handler.Instance->CNDTR = cndtr;/*传输数据量*/
	
	__HAL_DMA_ENABLE(&dma_handler);/*使能DMA通道*/
}

dma.h

#ifndef __DMA_H
#define __DMA_H

#include "stm32f1xx.h"
extern DMA_HandleTypeDef	dma_handler;
extern uint8_t src_buf[10];
extern uint8_t dest_buf[10];


void dma_init(void);/*内存到内存*/
void dma_transmit(uint16_t cndtr);/*DMA发送数据函数*/
#endif

main.c

#include "stm32f1xx_hal.h"
#include "sys.h"
#include "usart.h"
#include "stdio.h"
#include "delay.h"
#include "dma.h"
#include "led.h"
#include "key.h"
#include "string.h"
int main()
{

	
	HAL_Init();                              /* 初始化HAL库 */
	sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
	usart_init(115200);
	delay_init();
	dma_init();
	led_init();
	key_init();
	
	while(1)
	{	
		delay_us(50000);
		memset(dest_buf,0,10);/*对目的进行初始化*/
		dma_transmit(10);
		while(1)
		{
			if(__HAL_DMA_GET_FLAG(&dma_handler,DMA_FLAG_GL1))
			{
				__HAL_DMA_CLEAR_FLAG(&dma_handler,DMA_FLAG_GL1);
				printf("传输完成!\r\n");
				break;
			}
		}
	}

}
	

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值