目录
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同时请求:
- 软件阶段,DMA_CCRx DMA通道配置寄存器PL--->设置通道优先级
- 硬件阶段,通道编号小的优先级大,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配置
- 在FLASH中定义好要传输的数据,在SRAM中定义好接收FLASH数据的变量
- 初始化DMA始终
- 初始化DMA结构体
- 清除通道标志位
- 使能DMA
- 编写比较函数
- 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配置
- 初始化串口
- 配置DMA初始化结构体
- 清除通道标志位
- 使能DMA
- 编写main函数应用
- 开启串口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的触发信号。
关键触发条件:
- USART的TXE(发送数据寄存器空)标志为1(USART_Cmd)
- USART已使能DMA发送请求(
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE)) - 当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端口
↓ 操作系统驱动
串口助手软件显示