文章目录


DMA直接存储器存储


DMA,即直接存储器存储数据,可以直接访问内部存储器。这篇文章用于总结和复习前段时间学习过的DMA转运数据的知识点。

在STM32里DMA可以直接访问STM32的内部存储器,包括运行内存SARM、程序存储器FLASH以及寄存器等,DMA都有权进行访问。


一、STM32中的DMA

1. DMA的功能

DMA可以提供外设到存储器或者存储器和存储器之间的高速数据传输,无需CPU的干预,节约了CPU的资源。

这里的外设指的是STM32中各个外设的DR数据寄存器

存储器指的就是STM32中的运行内存SARM和程序存储器FLASH。存储变量数组和程序代码的地方。

  • 程序存储器FLASH,存储c语言编译后的程序代码。
  • 运行内存SARM,存储程序运行过程中的临时变量,在程序中定义变量,数组,结构体的地方。

2.DMA的特性

  1. STM32的DMA有12个独立可配置的通道。即数据转运的路径。
    DMA1有7个通道, DMA2有5个通道每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

  2. 每个通道都可以选择是软件触发还是硬件触发。
    每个通道的硬件触发源都不相同,当选择硬件触发时要根据手册,查询触发的是哪个通道,选择相应的通道。
    这里软件触发实际上就是存储器到存储器之间的数据转运。比如 FLASH->SRAM中的数据转运。
    硬件触发,指的就是外设到存储器之间的数据转运。比如 转运ADC的数据,把ADC的数据转运到SRAM中。
  3. 直接数据转运,不需要CPU的干预,配置好后硬件直接转运数据

下图为DMA的硬件触发请求来源:

3.DMA的结构

DMA主要是用来进行转运数据的,那么基本的结构就包括外设寄存器,转运的存储器,传输计数器,自动重装器,软件/硬件触发选择器等。

根据上图,是江科大视频里的ppt,总结了DMA的基本框架,我们在配置DMA时,根据这张图来配置就可以了。

来简述一下上图的各个分别的含义:

  1. 外设存储器和存储器
  • 起始地址,决定了数据从哪里来,到哪里去。
  • 数据宽度,被转运数据的位数,可以选择Byte(一个字节8位),HalfWord(半字16位),Word(字32位)。
  • 地址是否自增指定一次转运完成后,下一次转运,是不是要把地址移动到下一个位置去,相当于指针p++。即转运一次挪一个坑。

2. 传输计数器和自动重装器

  • 传输计数器指定我总共要转运几次。当传输计数器减到0之后,之前自增的地址,也会恢复到起始地址的位置。以方便之后DMA开始新一轮的转换。
  • 自动重装器,作用是决定了DMA转换的模式,不重装,就是正常的单次模式,重装,就是循环模式。当传输计数器减到0之后,是否要恢复最初的自动重装值。
  • 比如传输计数器设置为5,不设置自动重装。这时,DMA转运5次之后就结束了。设置自动重装的话,DMA转运5次之后,传输计数器又重新恢复为5。以此循环。

3. 软件/硬件触发选择器

这里的触发选择,主要是选择哪一种触发DMA进行转运。

  • 当M1M2为1时,DMA的模式为软件触发,这时DMA就会以最快的速度,连续不断的触发DMA,争取早日清零传输计数器。
  • 当M1M2为0时,DMA的模式为硬件触发,可以由外设作为触发源触发DMA转运外设。硬件触发源可以选择ADC,串口,定时器的各个通道等。

4.DMA数据转运的几个条件

DMA转运是需要满足以下三个条件

  1. DMA使能
    2. 传输计数器必须大于0
    3. 有相应的触发源

注意这里的触发源,必须有触发信号。触发源触发一次,传输计数器自减一次。当传输计数器等于0时,且设置了自动重装时,这时无论是否触发,DMA都不会再进行转运数据了,此时需要手动失能DMA,再向传输计数器写入一个大于0的数。再使能DMA。

二、配置DMA

这里以DMA转运存储器到存储器的数据为例。

1. 定义两个储存于SRAM的数组

uint8_t DataA[]="0123456789";
uint8_t DataB[]="ABCDEFGHIJ";

2.RCC开启DMA时钟控制

使用库函数开启DMA的时钟。

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);  //开启DMA1的时钟控制

3.配置DMA初始化

这一步是关键关键

使用DMA_Init,函数来对存储器和外设的各个有关DMA的参数进行配置。

1. 配置三个参数

  • 外设地址和存储器的地址
    由于是存储器到存储器之间数据的转运
    • 外设地址就是DataA的首地址
    • 存储器地址就是DataB的首地址
  • 转运数据的宽度
    这里的参数选择有Byte ,HalfWord ,Word
    我们这里选择转运的数据宽度为字节就好啦。
  • 地址是否自增
    由于我们是转运数组的数据,所以两个数组都要自增。

2. DMA转运的方向

转运的方向有以下两种,以自己当作源地址还是目的地址。
这里我们选择的为SRC,外设站点->存储器。

3. 传输计数器和是否需要自动重装

我们设置的转运数组里有10位宽度,所以我们配置传输计数器转运10次。
是否需要自动重装,我们暂时不要自动重装,那么这里就设置不自动重装就好啦。

4.触发源选择部分

选择软件触发。当使用软件触发后,不需要时机,DMA即可转运。

5. DMA使能

直接使用库函数,开启DMA,当DMA满足了上述的几个步骤配置完后,DMA就可立即开始转运数据,把DataA数组的数据立即转运到DataB中

下方即为配置DMA的相关代码

	void MyDMA_Init(uint32_t addrA,uint32_t addrB,uint16_t Size)
{
	MyDMA_Size=Size;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);  //开启DMA1的时钟控制
	
	DMA_InitTypeDef DMA_InitStruct;
	DMA_StructInit(&DMA_InitStruct);
	DMA_InitStruct.DMA_PeripheralBaseAddr=addrA;		//外设地址
	DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;  //字节 uint8_t
	DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Enable;		//地址自增
	DMA_InitStruct.DMA_Priority=DMA_Priority_Medium;  //优先级
	
	DMA_InitStruct.DMA_MemoryBaseAddr=addrB;  //存储器的地址
	DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;  //字节 uint8_t
	DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;
	DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;  //单次 不需要自动重装
	
	DMA_InitStruct.DMA_BufferSize=Size;  //传输计数器的大小
	DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC;  //?
	DMA_InitStruct.DMA_M2M=DMA_M2M_Enable;  //软件触发
	
	DMA_Init(DMA1_Channel7,&DMA_InitStruct);      //软件触发 存储器到存储器的转运 可以任意选择通道
	
	DMA_Cmd(DMA1_Channel7,ENABLE);  //使能DMA  立即触发
	
	//DMA转运的三个条件
	//1. 传输计数器不为0  2. 有触发条件 软件触发or硬件触发  3. DMA使能
}

4.程序现象以及测试

依旧选择串口进行调试,当DMA_Init函数调用之后,数据就立刻开始了转运。

测试程序:

uint8_t DataA[]="0123456789";
uint8_t DataB[]="ABCDEFGHIJ";

int main()
{
    LED_Init();
    Serial_Init();
    printf("\r\nMEM2MEN传输之前:\n");
    printf("\r\nDataA:%s\n",DataA);
    printf("\r\nDataB:%s\n",DataB);
	printf("\r\nDataA的地址:%8x\n",(uint32_t)DataA);
	printf("\r\nDataB的地址:%8x\n",(uint32_t)DataB);
	Delay_ms(1000);

	printf("\r\n---------------------------------------\n");
	MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,10);  //数据转运
	
    printf("\r\nMEM2MEN传输之后:\n");
    printf("\r\nDataA:%s\n",DataA);
    printf("\r\nDataB:%s\n",DataB);
	printf("\r\n---------------------------------------\n");
	
    while(1)
    {
        LED1_ON();
        Delay_ms(50);
        LED1_OFF();
        Delay_ms(50);
    }
}

程序现象:

  1. LED灯在不断的闪烁,说明DMA转运CPU没有干预,DMA直接转运数据,非常方便。

  2. 串口助手这里也显示了转运之后的数据,DataA和DataB的数据一样,说明DataA的数据已经转运到DataB中。


总结

以上就是关于DMA存储器到存储器转运的相关内容

主要介绍DMA以及DMA的配置以及使用

使用DMA可以很快的转运外设到存储器存储器到存储器之间的数据转运
不需要CPU的干预,节约了CPU的资源。

慢慢总结,慢慢学习!

行文仓促,如有错误,欢迎指出。

原文作者:CSDN  藕粉-