STM32_11:DMA

目录

1.DMA简介

2.DMA框图和映射

3.DMA仲裁

4.MTM配置

5.MTP配置

1.数据传输过程

2.数据传输触发机制

3.内存到物理引脚的完整电路


1.DMA简介

手册10章

  • DMA: Data memory Access, 直接存储器访问。
  • 可以把数据从一个地方传输到另一个地方,而且不占用CPU。
  • DMA1: 7个通道,M->M,P->M,M->P.
  • DMA2: 5个通道 , M->M,P->M,M->P.
  • (存在大容量、互联型(f105/f107)产品中)

2.DMA框图和映射

DM1请求映射

DMA2请求映射:ADC3/SDIO/TIM8的DMA请求只有大容量的单片机才有

3.DMA仲裁

多个DMA同时请求:

  1. 软件阶段,DMA_CCRx DMA通道配置寄存器PL--->设置通道优先级
  2. 硬件阶段,通道编号小的优先级大,DMA1优先级大于DMA2的优先级

4.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;
1.数据输入与输出

uint32_t DMA_PeripheralBaseAddr; //外设地址

uint32_t DMA_MemoryBaseAddr; //存储器地址

uint32_t DMA_DIR //传输方向

1.外设地址 :DMA_CPAR 外设地址寄存器

(DMA Channel x Peripheral Address Register)

2.存储器地址 : DMA_CMAR 存储器地址寄存器

3.传输方向 : DMA_CCRx:DIR DMA通道配置寄存器DIR位
2.数据大小以及传输单位

uint32_t DMA_BufferSize; //传输数目

uint32_t DMA_PeripheralInc; //外设地址增量模式

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

uint32_t DMA_PeripheralDataSize; //外设数据宽度

uint32_t DMA_MemoryDataSize; //存储器数据宽度

1.传输数目 : DMA_CNDTR DMA通道传输数量寄存器 2^16=65535

2.外设地址是否递增 : DMA_CCRx:PINC位 DMA通道配置寄存器PINC位

3.存储器地址是否递增 :DMA_CCRx:MINC位 DMA通道配置寄存器MINC位

4.外设数据宽度 :DMA_CCRx:PSIZE位 DMA通道配置寄存器PSIZE位

5.存储器数据宽度 :DMA_CCRx:MSIZE位 DMA通道配置寄存器MSIZE位

注:如果是固定的存储器,不需要增量。如实是数据数组,需要使能增量模式。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值,增量值取决与所选的数据宽度为1、2或4。
3.传输结束

uint32_t DMA_Mode; //模式选择

1.模式选择: DMA_CCRx:CIRC位

2.传输过半,传输完成,传输出错 DMA_ISR
M - > M: Flash to SRAM, 内部FLASH (CODE)的数据传输到内部的SRAM(变量)

M -> P : SRAM to 串口 + LED,延时DMA传输数据不需要占用CPU

4.MTM配置

  1. 在FLASH中定义好要传输的数据,在SRAM中定义好接收FLASH数据的变量
  2. 初始化DMA始终
  3. 初始化DMA结构体
  4. 清除通道标志位
  5. 使能DMA
  6. 编写比较函数
  7. main函数应用
cs 复制代码
#include "dma.h"
#include "stm32f10x.h"


const uint32_t SRC_Buffer[BUFFER_SIZE] = {
    0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F10,
    0x11121314, 0x15161718, 0x191A1B1C, 0x1D1E1F20,
    0x21222324, 0x25262728, 0x292A2B2C, 0x2D2E2F30,
    0x31323334, 0x35363738, 0x393A3B3C, 0x3D3E3F40
};

uint32_t DST_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 DMA_MTM_Init(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	DMA_InitStructure.DMA_PeripheralBaseAddr	= (uint32_t)SRC_Buffer;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	
	DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
	
	DMA_Init(DMA1_Channel6,&DMA_InitStructure);
	DMA_ClearFlag(DMA1_FLAG_TC6);
	DMA_Cmd(DMA1_Channel6,ENABLE);
	
}

uint8_t BuffetCmp(const uint32_t* pBuffer1,uint32_t* pBuffer2,uint32_t BufferLength)
{
	while(BufferLength -- )
	{
		if(*pBuffer1 != *pBuffer2)
		{
			return 0;
		}
		pBuffer1++;
		pBuffer2++;
	}
	
	return 1;
}
cs 复制代码
#include "stm32f10x.h"
#include "main.h"
#include "led.h"
#include "buzzer.h"
#include "key.h"
#include "Relay.h"
#include "Shake.h"
#include "Exti.h"
#include "usart.h"
#include "stdio.h"
#include "Time.h"
#include "sg90.h"
#include "systick.h"
#include "I2C_soft.h"
#include "dht11.h"
#include "spi.h"
#include "dma.h"

extern const uint32_t SRC_Buffer[BUFFER_SIZE];
extern uint32_t DST_Buffer[BUFFER_SIZE];

int  main()
{
	uint8_t ret = 0;
	
	initSysTick();
	led_init();
	DMA_MTM_Init();
	
    /*
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);
	ms_delay(2000);
	GPIO_SetBits(GPIOA,GPIO_Pin_1);
	ms_delay(2000);
    */
	
	ret = BuffetCmp(SRC_Buffer,DST_Buffer,BUFFER_SIZE);
	
	if(ret == 0)
	{
		GPIO_ResetBits(GPIOA,GPIO_Pin_1);
	}
	else
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_1);
	}

	while(1)
	{
	} 
   
}

5.MTP配置

  1. 初始化串口
  2. 配置DMA初始化结构体
  3. 清除通道标志位
  4. 使能DMA
  5. 编写main函数应用
  6. 开启串口DMA通道
cs 复制代码
#include "dma.h"
#include "stm32f10x.h"


const uint32_t SRC_Buffer[BUFFER_SIZE] = {
    0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F10,
    0x11121314, 0x15161718, 0x191A1B1C, 0x1D1E1F20,
    0x21222324, 0x25262728, 0x292A2B2C, 0x2D2E2F30,
    0x31323334, 0x35363738, 0x393A3B3C, 0x3D3E3F40
};

uint32_t DST_Buffer[BUFFER_SIZE];
uint8_t Send_Buffer[SEND_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 DMA_MTM_Init(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	DMA_InitStructure.DMA_PeripheralBaseAddr	= (uint32_t)SRC_Buffer;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	
	DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
	
	DMA_Init(DMA1_Channel6,&DMA_InitStructure);
	DMA_ClearFlag(DMA1_FLAG_TC6);
	DMA_Cmd(DMA1_Channel6,ENABLE);
	
}

uint8_t BuffetCmp(const uint32_t* pBuffer1,uint32_t* pBuffer2,uint32_t BufferLength)
{
	while(BufferLength -- )
	{
		if(*pBuffer1 != *pBuffer2)
		{
			return 0;
		}
		pBuffer1++;
		pBuffer2++;
	}
	
	return 1;
}

void DMA_USART_Init(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Send_Buffer;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART1_DR_ADDR;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
	
	DMA_InitStructure.DMA_BufferSize = SEND_SIZE;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	
	DMA_Init(DMA1_Channel4,&DMA_InitStructure);
	DMA_ClearFlag(DMA1_FLAG_TC4);
	DMA_Cmd(DMA1_Channel4,ENABLE);
	
}
cs 复制代码
#include "stm32f10x.h"
#include "main.h"
#include "led.h"
#include "buzzer.h"
#include "key.h"
#include "Relay.h"
#include "Shake.h"
#include "Exti.h"
#include "usart.h"
#include "stdio.h"
#include "Time.h"
#include "sg90.h"
#include "systick.h"
#include "I2C_soft.h"
#include "dht11.h"
#include "spi.h"
#include "dma.h"

extern const uint32_t SRC_Buffer[BUFFER_SIZE];
extern uint32_t DST_Buffer[BUFFER_SIZE];
extern uint8_t Send_Buffer[SEND_SIZE];
int  main()
{
	
	uint16_t i  = 0;
	
	for(i = 0;i < SEND_SIZE;i++)
	{
		Send_Buffer[i] = '0';
	}
	
	DMA_USART_Init();
	my_usart1_init();
	USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
	
	while(1)
	{
	} 
   
}
cs 复制代码
#ifndef _DMA_H_
#define _DMA_H_

#include "stm32f10x.h"

#define BUFFER_SIZE 16
#define SEND_SIZE 500

#define USART1_DR_ADDR  (USART1_BASE + 0x04) 

void DMA_MTM_Init(void);
uint8_t BuffetCmp(const uint32_t* pBuffer1,uint32_t* pBuffer2,uint32_t BufferLength);
void DMA_USART_Init(void);

#endif

1.数据传输过程

时间线 | 事件 | 说明

----------|----------------------------------------------------------|----------------------

t0 | 初始化Send_Buffer为SENDSIZE个'0' | for循环赋值

t1 | DMA_USART_Init()执行 | DMA配置,包括DMA_Cmd(ENABLE)

t2 | my_usart1_init()执行 | USART配置

t3 | USART_Cmd(ENABLE)执行 | USART使能,TXE=1

t4 | USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE)

| 关键!使能USART的DMA请求

t5 | USART检测到TXE=1且DMA请求使能,向DMA发出传输请求

| 自动触发

t6 | DMA收到请求,开始传输第1个字节 | 读取Send_Buffer[0]='0'

t7 | DMA写入USART1->DR寄存器 | 地址0x40013804

t8 | USART硬件检测到DR写入,将'0'存入TDR

t9 | TXE标志变0(TDR非空)

t10 | USART将TDR中的'0'复制到TSR | 发送移位寄存器

t11 | TXE标志变1(TDR变空) | TDR数据已转给TSR

t12 | USART再次检测到TXE=1,向DMA发出下一个请求

t13 | DMA传输第2个字节:Send_Buffer[1]='0'

t14 | ...重复t7-t13的过程

tN | 传输完第SENDSIZE个字节后停止 | DMA自动停止

2.数据传输触发机制

当DMA_Cmd(DMA1_Channel4,ENABLE)执行后,DMA不会立即开始传输,因为它需要等待USART的触发信号。

关键触发条件:

  1. USART的TXE(发送数据寄存器空)标志为1(USART_Cmd)
  2. USART已使能DMA发送请求(USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE)
  3. 当TXE=1时,USART硬件会自动向DMA发出传输请求

my_usart1_init()执行USART_Cmd(USART1,ENABLE)时:

  • USART硬件被使能
  • USART的TDR(发送数据寄存器)初始为空
  • TXE标志自动置1(因为TDR为空)
  • 之后已使能USART_DMAReq_Tx,USART立即向DMA发出传输请求
  • DMA响应请求,开始第一次传输

3.内存到物理引脚的完整电路

Send_Buffer数组(内存RAM)

↓ DMA控制器(自动搬运)

USART1->DR寄存器(硬件地址0x40013804)

↓ USART发送逻辑(硬件自动)

TDR寄存器 → TSR寄存器(双缓冲)

↓ 波特率发生器(硬件定时)

发送移位寄存器(逐位移出)

↓ TX引脚驱动器

PA9引脚(物理电平变化)

↓ PCB走线

USB转串口芯片RX引脚

↓ 电平转换和USB协议

电脑USB端口

↓ 操作系统驱动

串口助手软件显示

相关推荐
Shirley~~2 小时前
Web Audio API
前端
鹏北海2 小时前
qiankun微前端通信与路由方案总结
前端·微服务·架构
韩曙亮2 小时前
【Web APIs】浏览器本地存储 ② ( window.sessionStorage 本地存储常用 API 简介 | 代码示例 )
开发语言·前端·javascript·localstorage·sessionstorage·web apis·浏览器本地存储
郑州光合科技余经理2 小时前
私有化B2B订货系统实战:核心模块设计与代码实现
java·大数据·开发语言·后端·架构·前端框架·php
time_rg2 小时前
深入理解react——2. Concurrent Mode
前端·react.js
0_12 小时前
封装了一个vue版本 Pag组件
前端·javascript·vue.js
五阿哥永琪2 小时前
基于 Spring AOP 的角色权限校验实现指南&&注解类型避坑指南
java·后端·spring
yugi9878382 小时前
STM32通过ESP8266发送数据实现方案
stm32·嵌入式硬件
Stirner2 小时前
A2UI : 以动态 UI 代替 LLM 文本输出的方案
前端·llm·agent