一、简介
*DMA(Direct Memory Access)直接存储器存取
*DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。
*DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了*CPU的资源
*12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道) 每个通道都支持软件触发和特定的硬件触发
*STM32F103C8T6 DMA资源:DMA1(7个通道)
*DMA请求映射
PS:DMA的工作不需要经过CPU,进行数据传输是在DMA通道中执行,这样内部工作犹如有两个CPU。它的作用就是解决大量数据转移过度消耗CPU资源的问题。
二、DMA传输方式
- 外设到内存
- 内存到外设
- 内存到内存
- 外设到外设
内存到内存:
#include "dma.h"
#include "stm32f10x.h"
//常量或者代码存储在FLASH
const uint32_t SRC_Buffer[BUFFER_SIZE] = //源
{
0X01020304,0X05060708,0x090a0b0c,0x0d0e0f10,
0X11121314,0X15161718,0x191a1b1c,0x1d1e1f20,
0X21222324,0X25262728,0x292a2b2c,0x2d2e2f30,
0X31323334,0X35363738,0x393a3b3c,0x3d3e3f40
};
//定义DMA传输的目标存储器,变量存储在SARM
uint32_t DST_Buffer[BUFFER_SIZE]; //目标
uint32_t SEND_Buffer[SEND_SIZE]; //定义M->P存储器的地址
//M->M
void DMA_MTM_Init(void)
{
DMA_InitTypeDef DMA_StructInit;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //打开时钟
DMA_StructInit.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer; //存储器地址
DMA_StructInit.DMA_PeripheralBaseAddr = (uint32_t)SRC_Buffer; //外设地址
DMA_StructInit.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向(外设作为源,flash作为外设)
DMA_StructInit.DMA_BufferSize = BUFFER_SIZE; //一次的传输数目(所有需要传输内容的传输一遍的数目)
DMA_StructInit.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; //外设数据宽度
DMA_StructInit.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; //存储器数据宽度
DMA_StructInit.DMA_PeripheralInc = DMA_PeripheralInc_Enable; //外设地址增量模式
DMA_StructInit.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器地址增量模式
DMA_StructInit.DMA_Mode = DMA_Mode_Normal; //模式选择(发送一次还是循环发送)
DMA_StructInit.DMA_Priority = DMA_Priority_High; //通道优先级
DMA_StructInit.DMA_M2M = DMA_M2M_Enable; //存储器到存储器模式
DMA_Init( DMA1_Channel6, &DMA_StructInit);
DMA_ClearFlag(DMA1_FLAG_TC6); //清除标志
DMA_Cmd(DMA1_Channel6, ENABLE);
}
//判断flash到sram 的内容是否一致
uint8_t BufferCmp(const uint32_t *pBuffer1,uint32_t *pBuffer2,uint32_t bufferLenght)
{
while(bufferLenght--)
{
if(*pBuffer1 != *pBuffer2)
return 0;
pBuffer1++;
pBuffer2++;
}
return 1;
}
int main()
{
extern const uint32_t SRC_Buffer[BUFFER_SIZE];
extern uint32_t DST_Buffer[BUFFER_SIZE];
uint8_t status = 0;
initSysTick();
usart_init();
DMA_MTM_Init();
ms_delay(1000);
while(DMA_GetFlagStatus(DMA1_FLAG_TC6) == RESET); //检测DMA是否传输完成
status = BufferCmp(SRC_Buffer,DST_Buffer,BUFFER_SIZE);
if(status == 0)
printf("DMA M-M 传输有误\r\n");
else
printf("DMA M-M 传输正确\r\n");
// DMA_USART1_Init();
// USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);//打开串口DMA使能
while(1)
{
}
内存到外设:
//M->P
void DMA_USART1_Init(void)
{
DMA_InitTypeDef DMA_StructInit;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //打开时钟
DMA_StructInit.DMA_MemoryBaseAddr = (uint32_t)SEND_Buffer; //存储器地址
DMA_StructInit.DMA_PeripheralBaseAddr = (uint32_t)USART1_DR_ADDR; //外设地址
DMA_StructInit.DMA_DIR = DMA_DIR_PeripheralDST; //传输方向(外设作为目的)
DMA_StructInit.DMA_BufferSize = SEND_SIZE; //传输数目
DMA_StructInit.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据宽度
DMA_StructInit.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据宽度(DR寄存器是8位)
DMA_StructInit.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址增量模式(外设不需要增量,串口的地址是固定)
DMA_StructInit.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器地址增量模式
DMA_StructInit.DMA_Mode = DMA_Mode_Normal; //模式选择(将所有内容只发一次)
DMA_StructInit.DMA_Priority = DMA_Priority_High; //通道优先级
DMA_StructInit.DMA_M2M = DMA_M2M_Disable; //存储器到存储器模式
DMA_Init( DMA1_Channel4, &DMA_StructInit);
DMA_ClearFlag(DMA1_FLAG_TC4); //清除标志(发送完成)
DMA_Cmd(DMA1_Channel4, ENABLE);
}
外设到内存:去看ADC DMA传输
三、中断
四、仲裁
多个DMA的时候 就需要仲裁 来判断哪个DMA先触发,当软件阶段的DMA的优先级一样时,就需要比较硬件阶段的通道编号,编号小的优先级大,要是硬件阶段也是一样,则比较DMA1和DMA2(DMA1优先级大于DMA2的优先级)
五、数据传输宽度、对齐方式
*外设的数据宽度如果大于源的数据宽度,外设每一个数据宽度接收源的一个数据宽度。外设没有存满的空间直接抛弃。
*如果源大于目标的数据宽度,源一次性发送过给外设的数据宽度,外设只能接收自己设定的数据宽度,至于源发送超出的数据宽度,直接抛弃(例如:源有四个数据宽度,外设只有一个,那么外设只接收一个 超出的数据宽度不要)
PS:
*外设是串口(目标),内存是源。这时外设地址不需要设置递增,因为串口的地址是固定的,内存的数据如果是多个那要设置递增。
*需不需要循环发送 就是内存所有数据都发过一次之后,还需不需要再次发送。
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; //存储器到存储器模式