(STM32笔记)十二、DMA的基础知识与用法 第三部分

我用的是正点的STM32F103来进行学习,板子和教程是野火的指南者

之后的这个系列笔记开头未标明的话,用的也是这个板子和教程。

DMA的基础知识与用法

三、DMA程序验证

1、DMA 存储器到存储器模式实验

(1)DMA结构体解释

c 复制代码
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;
  1. DMA_PeripheralBaseAddr

    外设地址,设定 DMA_CPAR 寄存器的值;一般设置为外设的数据寄存器地址,如果是存储器到存储器模式则设置为其中一个存储器地址。

  2. DMA_Memory0BaseAddr

    存储器地址,设定 DMA_CMAR 寄存器值;一般设置为我们自定义存储区的首地址。

  3. DMA_DIR

    传输方向选择,可选外设到存储器、存储器到外设。它设定 DMA_CCR 寄存器的DIR[1:0] 位的值。这里并没有存储器到存储器的方向选择,当使用存储器到存储器时,只需要把其中一个存储器当作外设使用即可。

  4. DMA_BufferSize

    设定待传输数据数目,初始化设定 DMA_CNDTR 寄存器的值。

    这个DMA_BufferSize 就是要传输的次数。

    这个并不是指字节大小,而是指DMA的传输次数,传输的字节大小由DMA_PeripheralDataSize设置。

    DMA传输数据时,从DMA_MemoryBaseAddr+0地址开始放,直到DMA_MemoryBaseAddr+DMA_BufferSize-1,此时 DMA_CNDTR 变成0,传输结束。

  5. DMA_PeripheralInc

    如果配置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定 DMA_CCR 寄存器的 PINC 位的值;一般外设都是只有一个数据寄存器,所以一般不会使能该位。

  6. DMA_MemoryInc

    如果配置为 DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定 DMA_CCR 寄存器的 MINC 位的值;我们自定义的存储区一般都是存放多个数据的,所以要使能存储器地址自动递增功能。

  7. DMA_PeripheralDataSize

    外设数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_CCR 寄存器的 PSIZE[1:0] 位的值。

  8. DMA_MemoryDataSize

    存储器数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_CCR 寄存器的 MSIZE[1:0] 位的值。当外设和存储器之间传数据时,两边的数据宽度应该设置为一致大小。

  9. DMA_Mode

    DMA 传输模式选择,可选一次传输Normal或者循环传输Circular,它设定 DMA_CCR 寄存器的CIRC 位的值。例程我们的 ADC 采集是持续循环进行的,所以使用循环传输模式。

  10. DMA_Priority

    软件设置通道的优先级,有 4 个可选优先级分别为非常高、高、中和低,它设定 DMA_CCR 寄存器的 PL[1:0] 位的值。DMA 通道优先级只有在多个 DMA 通道同时使用时才有意义,如果是单个通道,优先级可以随便设置。

  11. DMA_M2M

    存储器到存储器模式,使用存储器到存储器时用到,设定 DMA_CCR 的位 14 MEN2MEN 即可启动存储器到存储器模式。

(2)DMA结构体初始化

c 复制代码
		DMA_InitTypeDef DMA_InitStructure;

		// 开启DMA时钟
		RCC_AHBPeriphClockCmd(DMA_CLOCK, ENABLE);
		// 源数据地址
    	DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)ShuZu_YouShu;	//数组有32个数,每个数都是一个字,且使用const定义(存储在FLASH)
		// 目标地址
		DMA_InitStructure.DMA_MemoryBaseAddr 		= (uint32_t)ShuZu_MeiShu;	//数组有32个数,每个数都是一个字
		// 方向:外设到存储器(这里的外设是内部的FLASH)	
		DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralSRC;
		// 传输大小	
		DMA_InitStructure.DMA_BufferSize 			= 32;						//因为数组有32个数,所以设32
		// 外设(内部的FLASH)地址递增	    
		DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Enable;
		// 内存地址递增
		DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable;
		// 外设数据单位	
		DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Word;	//因为每个数大小都是一个字,所以宽度设为Word
		// 内存数据单位
		DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Word;	 //因为每个数大小都是一个字,所以宽度设为Word
		// DMA模式,一次或者循环模式
		DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal ;			//一次传输模式
		//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  
		// 优先级:高	
		DMA_InitStructure.DMA_Priority 				= DMA_Priority_High;
		// 使能内存到内存的传输
		DMA_InitStructure.DMA_M2M 					= DMA_M2M_Enable;			//因为本次是FLASH传输到SRAM,所以是存储器到存储器
		// 配置DMA通道		   
		DMA_Init(DMA_CHANNEL, &DMA_InitStructure);
    	//清除DMA数据流传输完成标志位
    	DMA_ClearFlag(DMA_FLAG_TC);
    	// 使能DMA
		DMA_Cmd(DMA_CHANNEL,ENABLE);

其中,

  1. 需开启DMA时钟才能使用DMA功能:
c 复制代码
// 开启DMA时钟
RCC_AHBPeriphClockCmd(DMA_CLOCK, ENABLE);
  1. ShuZu_YouShu定义如下:
c 复制代码
uint32_t ShuZu_YouShu[32]= {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};
  1. 需清除 DMA 标志位后再使用DMA功能,防止不必要的干扰:
c 复制代码
//清除DMA数据流传输完成标志位
    DMA_ClearFlag(DMA_FLAG_TC);

(3)程序验证

DMA传输过后,在DEBUG界面可以看到,ShuZu_YouShu的数字传输到了ShuZu_MeiShu

2、DMA 存储器到外设模式实验

具体实验为 将数组中的数字 传输至 串口

(1)串口初始化

代码解释均放入注释中,正文不再复述

c 复制代码
/**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率 为 115200
	USART_InitStructure.USART_BaudRate = 115200;
	// 配置 数据字长 为 8
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置 停止位 为 1
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置 校验位 为 0
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

(2)DMA结构体初始化

因为数据是从存储器到串口,所以设置存储器为源地址,串口的数据寄存器为目标地址。

要发送的数据有很多且都先存储在存储器中,则存储器地址指针递增;串口数据寄存器只有一个,则外设地址地址不变。

两边数据单位设置成一致,传输模式可选一次或者循环传输,只有一个 DMA 请求,优先级随便设。

最后调用 DMA_Init 函数把这些参数写到 DMA 的寄存器中,然后使能 DMA开始传输。

c 复制代码
void USARTx_DMA_Config(void)
{
		DMA_InitTypeDef DMA_InitStructure;
	
		// 开启DMA时钟
		RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
		// 设置DMA源地址:串口数据寄存器地址*/
		DMA_InitStructure.DMA_PeripheralBaseAddr 	= USART_DR_ADDRESS;
		// 内存地址(要传输的变量的指针)
		DMA_InitStructure.DMA_MemoryBaseAddr 		= (u32)ShuZu_YouShu;
		// 方向:从内存到外设	
		DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralDST;
		// 传输数量
		DMA_InitStructure.DMA_BufferSize 			= 5000;		// 传输数量	设为 5000,这个数随便设,只要不超过数组大小就可
		// 外设地址不增	    
		DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable;
		// 内存地址自增
		DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable;
		// 外设数据单位	
		DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte;	//因为每次传输1bit,所以设为Byte
		// 内存数据单位
		DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;	 	//因为每次传输1bit,所以设为Byte
		// DMA模式,一次或者循环模式
		DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal ;				//一次传输模式
		//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;	
		// 优先级:中	
		DMA_InitStructure.DMA_Priority 				= DMA_Priority_Medium; 
		// 禁止内存到内存的传输
		DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable;
		// 配置DMA通道		   
		DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);		
		// 使能DMA
		DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
}

其中,

  1. 外设地址DMA_PeripheralBaseAddr为串口地址(需查阅开发手册),即:

c 复制代码
// 外设寄存器地址
#define 	PERIPH_BASE         ((uint32_t)0x40000000)
#define 	APB2PERIPH_BASE     (PERIPH_BASE + 0x10000)
#define  	USART1_BASE         (APB2PERIPH_BASE + 0x3800)
#define  	USART_DR_ADDRESS    (USART1_BASE+0x04)
  1. ShuZu_YouShu数组填充为 A-Z共26个字母,再加一个回车
c 复制代码
  /*填充将要发送的数据*/
  for(i=0; i < 5000; i++)
  {
    ShuZu_YouShu[i]	 = 'A'+ i%27;
    if(ShuZu_YouShu[i] == '[')
	{
		ShuZu_YouShu[i] = '\n';
	}
  }

(3)程序验证

使用USART_DMACmd函数打开USART 的DMA请求。

c 复制代码
/* USART1 向 DMA发出TX请求 */
  USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);

它接收三个参数:

  • 第一个参数用于设置串口外设,可以是 USART1/2/3UART4/5 这 5 个参数可选。
  • 第二个参数设置串口的具体 DMA 请求,有串口发送请求 USART_DMAReq_Tx 和接收请求 USART_DMAReq_Rx 可选。
  • 第三个参数用于设置启动请求 ENABLE 或者关闭请求 DISABLE

运行该函数后 USART 的DMA 发送传输就开始了,根据配置存储器的数据会发送到串口。

验证结果如下:

相关推荐
雪碧透心凉_30 分钟前
Win32汇编学习笔记09.SEH和反调试
汇编·笔记·学习
qq_459730031 小时前
STM32-ADC模数转换
stm32·单片机·嵌入式硬件
半个番茄2 小时前
STM32 : GPIO_TypeDef
stm32·单片机·嵌入式硬件
serenity宁静2 小时前
Center Loss 和 ArcFace Loss 笔记
笔记·深度学习·机器学习
14_112 小时前
Cherno C++学习笔记 P51 创建并使用库
c++·笔记·学习
小鱼小鱼.oO3 小时前
SpringcloudAlibaba黑马笔记(部分)
笔记
厉昱辰3 小时前
51单片机入门基础
单片机·嵌入式硬件·51单片机
JaneZJW3 小时前
江科大STM32入门——UART通信笔记总结
笔记·stm32·单片机·嵌入式
Heavydrink3 小时前
Spring学习笔记2
笔记·学习·spring
就叫飞六吧3 小时前
51 单片机和 STM32 引脚命名对照表与解析
c++·stm32·单片机·嵌入式硬件·51单片机