stm32精简笔记6——DMA

1 DMA简介

  • DMA:Data Memory Access,直接存储器访问,主要功能是可以将数据从一个地方搬运到另一个地方,且不占用CPU内存。

  • DMA1:有7个通道,可以实现P->M外设到内存(ADC)、M->P内存到外设、M->M内存到内存。

  • DMA2:只存在于大容量设备中,有5个通道,可以实现P->M外设到内存(ADC)、M->P内存到外设、M->M内存到内存。

2 DMA框图

在这里插入图片描述

  • 数据发送方给DMA控制器发送DMA请求,DMA收到请求后,启动DMA传输。
  • 在参考手册中有不同的DMA请求映像。
  • 其中ADC3和SDIO、TMI8只在大容量产品中。

在这里插入图片描述

  • DMA有12个管道,7个DMA1,5个DMA2,每个通道可以接收多个外设的请求,但一个时间只能接受一个。

  • 多个DMA通道请求时,需要仲裁器判断响应先后。

  • 仲裁器管理分为两个阶段:

    • 第一阶段属于软件阶段, 可以在DMA_CCRx 寄存器中PL位设置,有 4 个等级:非常高、高、中和低四个优先级。
    • 第二阶段属于硬件阶段,如果两个或以上的 DMA 通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高, 比如通道 0 高于通道 1。 在大容量产品和互联型产品中,DMA1 控制器拥有高于 DMA2 控制器的优先级。

3 初始化结构体

typedef struct
{
/*数据从哪里来到哪里去*/
  uint32_t DMA_PeripheralBaseAddr;   // 外设地址  DMA_CPARx   32位,将地址写在该寄存器,一般来自GPIO的IDR寄存器。
  uint32_t DMA_MemoryBaseAddr; 	// 存储器地址 DMA_CMARx   32位,数据从内存来则将地址写到该处。
  uint32_t DMA_DIR; // 传输方向  DMA_CCRx中的DIR第4位,0从外设读,1从内存读。 M->M由位14MEM2MEM来决定。
    
/*数据传多少,单位是什么*/
  uint32_t DMA_BufferSize;      // 传输数目     DMA_CNDTRx低16位有效,一次可以传65536个数据
  uint32_t DMA_PeripheralInc;    // 外设地址增量模式   DMA_CCRx 6位PINC   置1递增
  uint32_t DMA_MemoryInc;   // 存储器地址增量模式   DMA_CCRx 7位MINC  置1递增
  uint32_t DMA_PeripheralDataSize;   // 外设数据宽度   DMA_CCRx的10、11位PSIZE来决定 00 8位 、01 16位 、10 32位
  uint32_t DMA_MemoryDataSize;   // 存储器数据宽度  DMA_CCRx的8、9位MSIZE来决定 00 8位 、01 16位 、10 32位
// 备注:当传输宽度不一致时会发生错误,在参考手册的可编程的数据传输宽度、对齐方式和数据大小端表中。

/*什么时候传输结束*/
  uint32_t DMA_Mode;     // 模式选择   DMA_CCRx  5位CIRC决定 置1循环模式重复发送 需要DMA_IFCR来判断是否完成

  uint32_t DMA_Priority;       // 优先级设置 DMA_CCRx 寄存器中PL位设置,有 4 个等级:非常高、高、中和低四个优先级。

  uint32_t DMA_M2M;      // 传输方向  M->M由DMA_CCRx中的位14MEM2MEM来决定。
}DMA_InitTypeDef;


/*初始化DMA*/
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);
/*DMA使能,控制DMA_CCRx的第0位EN置1工作*/
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
/*查看传输是否完成,通常调用TC参数*/
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);

4 M to M代码

4.1 流程

1)配置DMA结构体。

2)使能DMA。

4.2 bsp_mtm.h
#ifndef BSP_MTM_H__
#define BSP_MTM_H__
#include "stm32f10x.h"
#define BUFFER_SIZE 		32					 // 数据尺寸宏
#define MTM_DMA_CLK  		RCC_AHBPeriph_DMA1   // DAM时钟宏
#define MTM_DMA_Channel 	DMA1_Channel6		 // DAM的通道
#define MTM_DMA_Flag		DMA1_FLAG_TC6		 // 通道传输完成标志位
extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];
void MTM_DMA_Config(void);
uint8_t Buffer_Cmp(const uint32_t *sBuff, uint32_t *dBuff, uint16_t Len);
#endif
4.3 bsp_mtm.c
#include "bsp_mtm.h"



/* 定义aSRC_Const_Buffer数组作为DMA传输数据源
 * const关键字将aSRC_Const_Buffer数组变量定义为常量类型
 * 表示数据存储在内部的FLASH中,若有的数据未初始化则全为0
 */
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};
/* 定义DMA传输目标存储器
 * 存储在内部的SRAM中																		
 */
uint32_t aDST_Buffer[BUFFER_SIZE];
																		
//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;
									
void MTM_DMA_Config(void){
	// 声明初始化结构体
	DMA_InitTypeDef DMA_InitStruct;
	// 打开DMA时钟
	RCC_AHBPeriphClockCmd(MTM_DMA_CLK,ENABLE);
	// 初始化结构体
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer; // 外设地址数组名转32位
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)aDST_Buffer;           // 存储器地址
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC ; 					 // 传输方向
	
	DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE;              			 // 传输多少个
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable; 		 // 外设地址递增
	DMA_InitStruct.DMA_MemoryInc  = DMA_MemoryInc_Enable; 				 // 内存地址递增
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // 外设数据尺寸
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; 		 // 内存数据尺寸
	
	DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;						 // 循环发送模式
	DMA_InitStruct.DMA_Priority = DMA_Priority_High;					 // 优先级高
	DMA_InitStruct.DMA_M2M = DMA_M2M_Enable;							 // 打开MTM
	// 初始化
	DMA_Init(MTM_DMA_Channel,&DMA_InitStruct);							 // MTM可以任何通道
	// 清楚对应标志位
	DMA_ClearFlag(DMA1_FLAG_TC6);
	// DMA使能
	DMA_Cmd(MTM_DMA_Channel,ENABLE);									 // 使能DMA
}

// 比较传输结过

uint8_t Buffer_Cmp(const uint32_t *sBuff, uint32_t *dBuff, uint16_t Len){
	while(Len --){
		if(*sBuff != *dBuff){
			// 数据不相等
			return 0;
		}
		// 地址递增
		sBuff ++;
		dBuff ++;
	}
	return 1; // 当比较结束时,返回1
}
4.3 main.c
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_mtm.h"


int main(void){
	MTM_DMA_Config();
	LED_Config();
	// 检测传输是否完成
	while(DMA_GetFlagStatus(MTM_DMA_Flag) == RESET);
	if(Buffer_Cmp(aSRC_Const_Buffer, aDST_Buffer,BUFFER_SIZE)){
		LED_B(OFF);
		LED_R(OFF);
		LED_G(ON);
	}else{
		LED_B(OFF);
		LED_G(OFF);
		LED_R(ON);
	}
	while(1){

	}
}

5 M to P代码

5.1 流程

1)初始化UART。

2)初始化DMA。

3)清除DMA的TC标志位。

4)在主函数中通过USART_DMACmd来发送,DMA_GetFlagStatus判断是否结束。

5)如果不判断是否结束,马上调用USART则会覆盖内容。

6)MTP的USART的地址不能递增,且DIR的外设为dest。

5.2 bsp_mtp.h
#ifndef BSP_MTP_H__
#define BSP_MTP_H__
#include "stm32f10x.h"
#include <stdio.h>

// 内存数据
#define SENDBUFF_SIZE 5000
extern uint8_t SendBuff[SENDBUFF_SIZE];
// DMA宏定义
#define MTP_DMA_CLK  		RCC_AHBPeriph_DMA1   // DAM时钟宏
#define MTP_DMA_Channel 	DMA1_Channel4	 // TX对应通道
#define MTP_DMA_Flag		DMA1_FLAG_TC4		 // 通道传输完成标志位

// USART宏 外设地址
#define  USART_DR_ADDRESS        (USART1_BASE+0x04)

// 移植串口
#define  DEBUG_USARTx        			USART1
#define  DEBUG_USART_CLK     			RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd			RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE   		115200

#define  DEBUG_USART_GPIO_CLK 			RCC_APB2Periph_GPIOA
#define  DEBUG_USART_GPIO_APBxClkCmd 	RCC_APB2PeriphClockCmd

#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler

/*初始化USART*/
void USART_Config(void);
void USART_SendByte(uint8_t Data);
/*DMA初始化*/
void MTP_DMA_Config(void);
#endif
5.3 bsp_mtp.c
#include "bsp_mtp.h"

/*串口移植:不要中断*/

/*初始化USART*/
void USART_Config(void){
	// 两个结构体
	USART_InitTypeDef USART_InitStruct;
	GPIO_InitTypeDef GPIO_InitStruct;
	
	// 打开GPIO时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK,ENABLE);
	// 打开串口时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK,ENABLE);
	// 配置TX的GPIO结构体
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT,&GPIO_InitStruct);
	// 配置RX的GPIO结构体
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT,&GPIO_InitStruct);
	// 配置串口初始化结构体
	USART_InitStruct.USART_BaudRate = DEBUG_USART_BAUDRATE;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_Init(DEBUG_USARTx,&USART_InitStruct);

	USART_Cmd(DEBUG_USARTx,ENABLE);

}
void USART_SendByte(uint8_t Data){
	USART_SendData(DEBUG_USARTx,Data);
	while(USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_TXE) == RESET);
}
int fputc(int ch, FILE *f){
	USART_SendByte((uint8_t)ch);
	while(USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_TXE) == RESET);
	return ch;
}

// DMA配置                                   
void MTP_DMA_Config(void){
	// 声明初始化结构体
	DMA_InitTypeDef DMA_InitStruct;
	// 打开DMA时钟
	RCC_AHBPeriphClockCmd(MTP_DMA_CLK,ENABLE);
	// 初始化结构体
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDRESS;  // 外设地址
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)SendBuff;           	 // 存储器地址
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST ; 					 // 传输方向 
	
	DMA_InitStruct.DMA_BufferSize = SENDBUFF_SIZE;              		 // 传输多少个
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 		 // 外设地址不递增
	DMA_InitStruct.DMA_MemoryInc  = DMA_MemoryInc_Enable; 				 // 内存地址递增
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据尺寸
	DMA_InitStruct.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; 	 // 内存数据尺寸
	
	DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;						 	 // 不循环发送模式
	DMA_InitStruct.DMA_Priority = DMA_Priority_High;					 // 优先级高
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;							 // 关闭MTM
	// 初始化
	DMA_Init(MTP_DMA_Channel,&DMA_InitStruct);							 // 传递到外设只能用专门通道
	// 清楚对应标志位
	DMA_ClearFlag(MTP_DMA_Flag);
	// DMA使能
	DMA_Cmd(MTP_DMA_Channel,ENABLE);									 // 使能DMA
}
5.4 main.c
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_mtp.h"

uint8_t SendBuff[SENDBUFF_SIZE];

int main(void){
	uint16_t i = 0;
	for(i = 0;i < SENDBUFF_SIZE;i++){
		SendBuff[i] = 'a';
	}
	USART_Config();
	MTP_DMA_Config();
	// 打开DMA与UART时钟 STM32F10X_HD_VL才开用UART5
	USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);  
	while(DMA_GetFlagStatus(MTP_DMA_Flag) == RESET);
       /*立马调用printf会覆盖*/
	printf("\n传输完成\n");
	while(1){
	
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值