#stm32DMA

DMA 简介

DMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。
STM32F407 最多有 2 个 DMA 控制器(DMA1 和 DMA2),两个 DMA 控制器总共有 16 个数据流。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个 DMA 请求的优先权。
STM32F407 的 DMA 有以下一些特性:
① 双 AHB 主总线架构,一个用于存储器访问,另一个用于外设访问。
② 仅支持 32 位访问的 AHB 从编程接口。
③ 每个 DMA 控制器有 8 个数据流,每个数据流有多达 8 个通道(请求)。
④ 每个数据流有单独的四级 32 位先进先出存储器缓冲区(FIFO),可用于 FIFO 模式或直接模式。
⑤ 通过硬件可以将每个数据流配置为:
	1,支持外设到存储器、存储器到外设和存储器到存储器传输的常规通道。
	2,支持在存储器方双缓冲的双缓冲区通道。
⑥ 8 个数据流中的每一个都连接到专用硬件 DMA 通道(请求)。
⑦ DMA 数据流请求之间的优先级可用软件编程(4 个级别:非常高、高、中、低),在软件优先级相同的情况下可以通过硬件决定优先级(例如,请求 0 的优先级高于请求 1)
⑧ 每个数据流也支持通过软件触发存储器到存储器的传输(仅限 DMA2 控制器)
⑨可供每个数据流选择的通道请求数多达 8 个。此选择可由软件配置,允许多个外设启动DMA 请求。
⑩ 要传输的数据项的数目可以由 DMA 控制器或外设管理:
	1,DMA 流控制器:要传输的数据项的数目是 1 到 65535,可用软件编程。
	2,外设流控制器:要传输的数据项的数目未知并由源或目标外设控制,这些外设通过硬件发出传输结束的信号。
⑪ 独立的源和目标传输宽度(字节、半字、字):源和目标的数据宽度不相等时,DMA自动封装/解封必要的传输数据来优化带宽。这个特性仅在 FIFO 模式下可用。
⑫ 对源和目标的增量或非增量寻址。
⑬ 支持 4 个、8 个和 16 个节拍的增量突发传输。突发增量的大小可由软件配置,通常等于外设 FIFO 大小的一半。
⑭ 每个数据流都支持循环缓冲区管理。
⑮ 5 个事件标志(DMA 半传输、DMA 传输完成、DMA 传输错误、DMA FIFO 错误、直接模式错误),进行逻辑或运算,从而产生每个数据流的单个中断请求。

框图:

在这里插入图片描述
① DMA 控制器的从机编程接口,通过该接口可以对 DMA 的相关控制寄存器进行设置,从而配置 DMA,实现不同的功能。
② DMA 控制器的外设接口,用于访问相关外设,特别的,当外设接口设置的访问地址是内存地址的时候,DMA 就可以工作在内存到内存模式了。
③ DMA 控制器的 FIFO 区,每个数据流(总共 8 个数据流)都有一个独立的 FIFO,可以实现存储器接口到外设接口之间的数据长度非对齐传输。
④ DMA 控制器的存储器接口,用于访问外部存储器,特别的当存储器地址是外设地址的时候,可以实现类似外设到外设的传输效果。
⑤ DMA 控制器的仲裁器,用于仲裁数据流 0~7 的请求优先级,保证数据有序传输。
⑥ DMA 控制器数据流多通道选择,通过 DMA_SxCR 寄存器控制,每个数据流有多达 8个通道请求可以选择,如图 30.1.1.2 所示
在这里插入图片描述
外设的 8 个请求独立连接到每个通道,由 DMA_SxCR 控制数据流选择哪一个通道,每个数据流有 8 个通道可供选择,每次只能选择其中一个通道进行 DMA 传输。DMA1 各数据流通道映射具体见表 30.1.1.1,DMA2 各数据流通道映射具体见表 30.1.1.2。
在这里插入图片描述
在这里插入图片描述

寄存器

DMA 中断状态寄存器(DMA_LISR 和 DMA_HISR)

DMA 中断状态寄存器,该寄存器总共有 2 个:DMA_LISR 和 DMA_HISR,每个寄存器管理 4 数据流(总共 8 个),DMA_LISR 寄存器用于管理数据流 0~3,而 DMA_HISR 用于管理数据流 4~7。这两个寄存器各位描述都完全一模一样,只是管理的数据流不一样。

在这里插入图片描述
如果开启了 DMA_LISR 中这些位对应的中断,则在达到条件后就会跳到中断服务函数里面去,即使没开启,我们也可以通过查询这些位来获得当前 DMA 传输的状态。这里我们常用的是 TCIFx 位,即数据流 x 的 DMA 传输完成与否标志。注意此寄存器为只读寄存器,所以在这些位被置位之后,只能通过其他的操作来清除。DMA_HISR 寄存器各位描述和 DMA_LISR寄存器各位描述完全一样,只是对应数据流 4~7,这里我们就不列出来了。

DMA 中断标志清除寄存器(DMA_LIFCR 和 DMA_HIFCR)

DMA 中断标志清除寄存器,该寄存器同样有 2 个:DMA_LIFCR 和 DMA_HIFCR,同样是每个寄存器控制 4 个数据流,DMA_LIFCR 寄存器用于管理数据流 0~3,而 DMA_HIFCR 用于管理数据流 4~7。这两个寄存器各位描述都完全一模一样,只是管理的数据流不一样。这里,我们仅以 DMA_LIFCR 寄存器为例进行介绍,DMA_LIFCR 各位描述如图 30.1.2.2所示:

在这里插入图片描述
DMA_LIFCR 的各位就是用来清除 DMA_LISR 的对应位的,通过写 1 清除。在 DMA_LISR被置位后,我们必须通过向该位寄存器对应的位写入 1 来清除。DMA_HIFCR 的使用同DMA_LIFCR 类似,这里就不做介绍了。

DMA 数据流 x 配置寄存器 (DMA_SxCR) (x = 0…7)

在这里插入图片描述
该寄存器控制着 DMA 的很多相关信息,包括数据宽度、外设及存储器的宽度、优先级、增量模式、传输方向、中断允许、使能等都是通过该寄存器来设置的。所以 DMA_SxCR 是 DMA 传输的核心控制寄存器。

DMA 数据流 x 数据项数寄存器 (DMA_SxNDTR) (x = 0…7)

在这里插入图片描述
位 31:16 保留,必须保持复位值。
位 15:0 NDT[15:0]:要传输的数据项数目 (Number of data items to transfer)
要传输的数据项数目(0 到 65535)。只有在禁止数据流时,才能向此寄存器执行写操作。
使能数据流后,此寄存器为只读,用于指示要传输的剩余数据项数。每次 DMA 传输后,此寄存器将递减。传输完成后,此寄存器保持为零(数据流处于正常模式时),或者在以下情况下自动以先前
编程的值重载:
— 以循环模式配置数据流时。
— 通过将 EN 位置“1”来重新使能数据流时
如果该寄存器的值为零,则即使使能数据流,也无法完成任何事务。

DMA 数据流 x 外设地址寄存器 (DMA_SxPAR) (x = 0…7)

在这里插入图片描述

位 31:0 PAR[31:0]:外设地址 (Peripheral address)
读/写数据的外设数据寄存器的基址。
这些位受到写保护,只有 DMA_SxCR 寄存器中的 EN 为“0”时才可以写入。

DMA 数据流 x 存储器 0 地址寄存器 (DMA_SxM0AR) (x = 0…7)

DMA stream x memory 0 address register
偏移地址:0x1C + 0x18 × 数据流编号
复位值:0x0000 0000
在这里插入图片描述
位 31:0 M0A[31:0]:存储器 0 地址 (Memory 0 address)
读/写数据的存储区 0 的基址。
这些位受到写保护,只有在以下情况下才可以写入:
— 禁止数据流(DMA_SxCR 寄存器中的位 EN=“0”)或
— 使能数据流(DMA_SxCR 寄存器中的 EN=“1”)并且 DMA_SxCR 寄存器中的
位 CT =“1”(在双缓冲区模式下)。

DMA 数据流 x 存储器 1 地址寄存器 (DMA_SxM1AR) (x = 0…7)

DMA stream x memory 1 address register 偏移地址:0x20 + 0x18 × 数据流编号
复位值:0x0000 0000
在这里插入图片描述
位 31:0 M1A[31:0]:存储器 1 地址(用于双缓冲区模式)(Memory 1 address (used in case of Double buffer mode))
读/写数据的存储区 1 的基址。
此寄存器仅用于双缓冲区模式。只有在双缓冲区有效
这些位受到写保护,只有在以下情况下才可以写入:
— 禁止数据流(DMA_SxCR 寄存器中的位 EN=“0”)或
— 使能数据流(DMA_SxCR 寄存器中的 EN=“1”)并且 DMA_SxCR 寄存器中的位
CT =“0”。

代码

int main(void)
{
	uint8_t key=0;
	uint16_t i=0;
	uint16_t len;

	float pro=0;

  HAL_Init();

  SystemClock_Config();
  MX_GPIO_Init();
	usart_Init();
	key_init();
	dma_init(DMA2_Stream7, DMA_CHANNEL_4);
	printf("demo!!!\r\n");
	len = sizeof(TEXT_TO_SEND);

  while (1)
  {
   key = key_scan(0);
		if(key==KEY0_PRES)
		{
			printf("DMA_DATA:\r\n");
			HAL_UART_Transmit_DMA(&g_uart_handle1,TEXT_TO_SEND,(uint16_t)sizeof(TEXT_TO_SEND));
			
			while(1)
			{
				if(__HAL_DMA_GET_FLAG(&g_dma_handle,DMA_FLAG_TCIF3_7))
				{
					__HAL_DMA_CLEAR_FLAG(&g_dma_handle,DMA_FLAG_TCIF3_7);
					HAL_UART_DMAStop(&g_uart_handle1);
					break;
				}
				pro = __HAL_DMA_GET_COUNTER(&g_dma_handle);
				len = sizeof(TEXT_TO_SEND);
				pro =1-(pro/len);
				pro*=100;
				printf("%2f\r\n",pro);
			}
		}
		i++;
		HAL_Delay(10);
		if(i==20)
		{
			HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
			i=0;
		}
  }
  /* USER CODE END 3 */
}
#include "DMA.h"
#include "usart.h"
#include "stdio.h"
DMA_HandleTypeDef g_dma_handle;


void dma_init(DMA_Stream_TypeDef *dma_stream_handle, uint32_t ch)
{
	if((uint32_t)dma_stream_handle > (uint32_t)DMA2)//判断外设地址大于DMA2 使能DMA2是时钟
	{
		__HAL_RCC_DMA2_CLK_ENABLE();
	}
	else
	{
		__HAL_RCC_DMA1_CLK_ENABLE();
	}
	__HAL_LINKDMA(&g_uart_handle1,hdmatx,g_dma_handle);//注意第三个参数不是取地址 将DMA与uart1联系起来
	
	g_dma_handle.Instance = dma_stream_handle;//传入形参选择DMA1/DMA2
	
	g_dma_handle.Init.Channel = ch;//传入形参选择通道
	
	g_dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;//方向寄存器到外设
	
	g_dma_handle.Init.MemInc = DMA_MINC_ENABLE;//寄存器地址递增
	
	g_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;//外设地址不递增
	
	g_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //外设数据8字节对齐
	
	g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//寄存器数据8字节对其
	
	g_dma_handle.Init.Mode = DMA_NORMAL; //配置为正常模式
	
	g_dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//中等优先级
	
	g_dma_handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;//FIFO失能
	
	g_dma_handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;//FIFO域值为满
	
	g_dma_handle.Init.MemBurst = DMA_MBURST_SINGLE;//突发传输单次传输
	
	g_dma_handle.Init.PeriphBurst = DMA_PBURST_SINGLE;
	
	
	HAL_DMA_DeInit(&g_dma_handle);
	
		
	HAL_DMA_Init(&g_dma_handle);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值