DMA传输将数据从一个地址空间复制到另一个地址空间,提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源,提高CPU效率
16个独立可配置的通道(数据流):DMA1(8个通道)、DMA2(8个通道)
每个通道都支持软件触发(存储器到存储器)和特定的硬件触发(存储器到外设,触发一次转运一次)
DMA 的数据传输方向方式共有四种:
1.外设到内存 2.内存到外设 3.内存到内存(只能DMA2实现) 4.外设到外设(保留模式,基本不用)
存储器映像
ROM只读存储器,非易失性、掉电不丢失的存储器
RAM随机存储器,易失性、掉电丢失的存储器 SRAM运行内存
终止地址取决于存储器的容量,存到哪里哪里就是终止地址
DMA框图
用于访问各个存储器的DMA总线
DMA内部的多个通道可以进行独立的数据转运
仲裁器:用于调度各个通道,防止产生冲突。优先级管理分为软件和硬件,软件部分由数据流的优先级(DMA_SxCR寄存器种)决定,硬件部分由流的编号决定,机制类似中断硬件编号。
AHB从设备:用于配置DMA参数
DMA请求:用于硬件触发DMA的数据转运。外设想要通过DMA来传输数据,必须先给 DMA 控制器发送 DMA请求, DMA 控制器根据通道优先级处理该请求。控制器会给外设一个应答信号,当外设应答后且 DMA 控制器收到应答信号之后,就会启动 DMA 的传输,直到传输完毕。
主Flash:是ROM只读存储器的一种,只能读不能写,DMA的目的地址不能是Flash。可以配置Flash接口控制器,对Flash进行写入,按页擦除再写入数据 eg:SPI串行通信
FIFO直接模式和阈值突发模式
1.直接模式(禁止FIFO):不使用FIFO 的阈值级别控制。每完成一次从外设到FIFO 的数据传输后,相应的数据立即就会移出并存储到目标中。
注:存储器到存储器传输时不得使用直接模式
2.FIFO阈值突发模式:在此模式下,每次产生外设请求.数据流都会启动数据源到FIFO的传输。达到FIFO的阈值级别时,FIFO的内容移出并存储到目标中。
FIFO用于在源数据传输到目标之前临时存储这些数据。每个数据流都有一个独立的(总容量16字节)FIFO,FIFO临时存储数据最多为16字节,FIFO的存储阈值级别可由软件配置为1/4(4字节)、1/2(8字节)、3/4(12字节)或满(16字节)。
DMA传输模式
DMA传输方向使用 DMA_SxCR 寄存器中的 DIR[1:0] 位进行配置,有三种可能的传输方向:存储器到外设、外设到存储器或存储器到存储器。
存储器到外设模式
-
在此模式下数据流会立即启动传输,从数据源完全填充FIFO。每次发生外设请求,FIFO 的内容都会移出并存储到目标中。当 FIFO 的级别小于或等于预定义的阈值级别时,将使用存储器中的数据完全重载 FIFO。
-
如果传输完成或外设请求停止传输或软件关闭通道,传输会立即停止。
-
直接模式下,使能了数据流,DMA 便会预装载第一个数据,将其传输到内部 FIFO。一旦外设请 求数据传输,DMA 便会将预装载的值传输到配置的目标。每次传输都会将数据先装载至内部FIFO。
-
预装载的数据大小为 DMA_SxCR 寄存器中 PSIZE 位字段的值。
-
数据流在经过仲裁后才可以访问源或目标端口
DMA基本结构
指针递增
(地址是否自增)寄存器 - 地址不用自增,存储器 - 地址要自增
外设和存储器指针在每次传输后可以自动向后递增或保持常量。
如果使能了递增模式,则根据在 DMA_SxCR 寄存器 PSIZE 或 MSIZE 位中编程的数据宽度,下一次传输的地址将是前一次传输的地址递增 1(对于字节)、2(对于半字)或 4(对于字)。
单个寄存器访问外设源或目标数据时,禁止递增模式
传输计数器
(自减计数器)转运过程中,每转运一次,计数器的数就会减1,到0 时停止转运,之前自增的地址也会恢复到起始地址的位置
自动重装器 - 传输计数器减到0之后是否要恢复到最初的值
不重装就是普通模式,重装就是循环模式
普通模式与循环模式
普通模式:DMA搬运了设定长度的数据后,CNDTR清0,会产生中断标志,然后DMA就停止工作了,如果再有数据也不接收了。需关断 DMA 使能后再重新配置后才能继续传输。eg:转运数组,转运一轮就可以
循环模式: DMA搬运了设定长度的数据后,CNDTR清0,会产生中断标志,如果再有数据,DMA会循环保存到内存中,覆盖前面的数据。在CNDTR=0时DMA会自动装载初始化时的配置,CNDTR重置为初始值。
eg:ADC扫描模式+连续转换,为了配合ADC,DMA要使用循环模式
M2M:Memory to Memory 存储器到存储器
软件触发:M2M位给1,应用在存储器到存储器转运的情况,不需要时机,并且想尽快完成任务让传输计数器减到0,不能和循环模式一起用,不然DMA就停不下来了
硬件触发:M2M位给0,与外设有关的转运,需要时机。eg:ADC转换完成,串口收到数据,定时时间到
数据宽度与对齐
相同的数据宽度就是一个一个正常转运
目标数据宽度比远端数据宽度大时:在目标数据前面多出来的空位补0
目标数据宽度比远端数据宽度小时:只写入低位数据,多出来的高位舍弃
代码
存储器 ->串口
dma.c
#include "public.h"
/*
内存——>外设模式1.确定引脚:USART1 TX 数据流7通道42.GPIO初始化复用为串口串口的代码和之前一样,这里不做解释3.DMA初始化a.结构体申明 DMA_InitTypeDefb.配置结构体15个c.结构体初始化4.DMA数据传输5.传输状态获取*/
uint8_t data[4] = {0x01,0x02,0x03,0x04};
u16 MyDMA_size;void dma_usart_init()
{DMA_InitTypeDef DMA_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//while(DMA_GetCmdStatus(DMA2_Stream7) == !DISABLE) //等待DMA处于空闲状态,没有其他使用者DMA_InitStructure.DMA_Channel = DMA_Channel_4; //串口对应的通道4DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; //串口地址DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)data; //内存地址DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //内存到外设 DMA_InitStructure.DMA_BufferSize = 4; //传输数据大小DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存增量模式DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度1字节DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//内存数据长度1字节DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //中等优先级DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; //不使用FIFODMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull; //满缓冲DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //内存单节拍存储DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设单节拍存储//DMA_DeInit(DMA2_Stream7);//复位,清除FIFO缓冲区里的数据DMA_Init(DMA2_Stream7, &DMA_InitStructure);
}void dma_usart_start()
{//1.去使能关闭传输DMA_Cmd(DMA2_Stream7, DISABLE);//2.确保可以正常工作while(DMA_GetCmdStatus(DMA2_Stream7) == ENABLE)//3.开始数据传输DMA_SetCurrDataCounter(DMA2_Stream7, 4);//4.使能DMA_Cmd(DMA2_Stream7, ENABLE);//判断传输是否完成if(DMA_GetFlagStatus(DMA2_Stream7, DMA_FLAG_TCIF7) == SET){DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);//清空标志位}
}
main.c
串口使用16进制显示
#include "public.h"int main(void)
{uint8_t data[8] = {1,2,3,4,5,6,7,8};uint8_t databuf[8];//2号分组:2bit给抢占 2bit给响应 0-3 0-3 //NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); SysTick_Init();USART_Init_Config();dma_usart_init();USART_DMACmd(USART1,USART_DMAReq_Tx, ENABLE);while(1){dma_usart_start();//printf("\r\n");Delay_ms(1000);}
}//void tmep_init()
//{//}//float get_temp_value()
//{
// //}