×
单片机 > 单片机程序设计 > 详情

STM32系统学习——DMA(直接储存器访问)

发布时间:2020-06-09 发布时间:
|

DMA主要功能是传输数据,但是不需要占用CPU,即在传输数据时,CPU可以做别的事,像多线程。数据传输从外设到存储器或者从存储器到存储器。DMA控制器包含了DMA1和DMA2,其中DMA1有7个通道,DMA2有5个通道,可以理解为传输数据的一种管道。要注意的是,DMA2只存在于大容量单片机中。 
一、DMA框图解析 
DMA控制器独立于内核,属于一个单独外设,结构结合下图来看 
 
1.DMA请求 
如果外设想通过DMA传输数据,必须先向DMA控制器发送DMA请求,DMA收到请求信号后,控制器会给外设一个应答信号,当外设应答且DMA控制器收到应答信号后,就会启动DMA传输,直到传输完毕。 
DMA有DMA1和DMA2两个控制器,DMA1有两个控制器,DMA1有7个通道,DMA2有5个通道,不同DMA控制器的通道有不同的外设请求。 
2、通道 
DMA有12个独立可编程的通道,DMA1有7个通道,DMA2有5个通道,每个通道对应不同外设的DMA请求。虽然每个通道可以接收多个外设请求,但是同一时间只能接收一个,不能同时接收多个。 
3、仲裁器 
当同时有多个DMA请求时,就意味着有先后响应的问题,这个就由仲裁器管理。仲裁器管理DMA请求分为2个阶段:第一阶段属于软件阶段,可以在MDA_CCRx寄存器中设置,有 4 个等级:非常高、高、中和低四个优先级。第二阶段属于硬件阶段,如果两个或以上的 DMA 通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高,比如通道 0 高于通道 1。在大容量产品和互联型产品中,DMA1 控制器拥有高于 DMA2 控制器的优先级。

二、DMA数据配置 
使用DMA,最核心的就是配置要传输的数据。 
1、从哪儿来,到哪儿去 
DMA传输数据 的方向有3个:外设到存储器,存储器到外设,存储器到存储器。具体方向由DMA_CCR中第四位DIR配置:0表示外设到存储器,1表示存储器到外设。涉及的地址由DMA_CPAR配置,存储器地址由DMA_CMAR配置。 
1)从外设到存储器 
以ADC采集为例,DMA外部寄存器地址对应ADC数据寄存器地址,DMA存储器地址是我们自定义的变量的地址。方向设置为源地址。 
2)存储器到外设 
存储器到外设传输以串口向电脑端发送为例,DMA 外设寄存器的地址对应的就是串口数据寄存器的地址,DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储通过串口发送到电脑的数据)的地址。方向我们设置外设为目标地址。 
3)存储器到存储器 
当我们使用从存储器到存储器传输时,以内部 FLASH 向内部 SRAM 复制数据为例。 
DMA外设寄存器的地址对应的就是内部 FLASH(我们这里把内部 FALSH 当作一个外设来看)的地址,DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部 FLASH 的数据)的地址。方向我们设置外设(即内部 FLASH)为源地址。跟上面两个不一样的是,这里需要把 DMA_CCR 位 14:MEM2MEM:存储器到存储器模式配置为 1,启动 M2M 模式。 
2、要传什么,单位是多少 
以串口向电脑发送数据为例,我们可以一次性给电脑发送很多数据,具体多少由DMA_CNDTR 配置,这是一个 32位的寄存器,一次最多只能传输 65535 个数据。 
要想数据传输正确,源和目标地址存储的数据宽度还必须一致,串口数据寄存器是 8位的,所以我们定义的要发送的数据也必须是 8 位。外设的数据宽度由 DMA_CCR 的PSIZE[1:0]配置,可以是 8/16/32位,存储器的数据宽度由 DMA_CCR 的 MSIZE[1:0]配置,可以是 8/16/32 位。 
在 DMA 控制器的控制下,数据要想有条不紊的从一个地方搬到另外一个地方,还必须正确设置两边数据指针的增量模式。外设的地址指针由 DMA_CCRx 的 PINC 配置,存储器的地址指针由 MINC 配置。以串口向电脑发送数据为例,要发送的数据很多,每发送完一个,那么存储器的地址指针就应该加 1,而串口数据寄存器只有一个,那么外设的地址指针就固定不变。具体的数据指针的增量模式由实际情况决定。 
3、什么时候传输完成 
数据什么时候传输完成,我们可以通过查询标志位或者通过中断的方式来鉴别。每个DMA 通道在 DMA 传输过半、传输完成和传输错误时都会有相应的标志位,如果使能了该类型的中断后,则会产生中断。有关各个标志位的详细描述请参考 DMA 中断状态寄存器DMA_ISR的详细描述。 
传输完成还分两种模式,是一次传输还是循环传输,一次传输很好理解,即是传输一次之后就停止,要想再传输的话,必须关断 DMA 使能后再重新配置后才能继续传输。循环传输则是一次传输完成之后又恢复第一次传输时的配置循环传输,不断的重复。具体的由 DMA_CCR寄存器的 CIRC 循环模式位控制。

三、DMA初始化结构体 
结构体 xxx_InitTypeDef 定义在stm32f10x_xxx.h(后面xxx为外设名称)文件中,库函数xxx_Init定义在stm32f10x_xxx.c文件中。

DMA_ InitTypeDef 初始化结构体


1 typedef struct

2 {

3 uint32_t DMA_PeripheralBaseAddr; // 外设地址

4 uint32_t DMA_MemoryBaseAddr; // 存储器地址

5 uint32_t DMA_DIR; // 传输方向

6 uint32_t DMA_BufferSize; // 传输数目

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

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

9 uint32_t DMA_PeripheralDataSize; // 外设数据宽度

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

11 uint32_t DMA_Mode; // 模式选择

12 uint32_t DMA_Priority; // 通道优先级

13 uint32_t DMA_M2M; // 存储器到存储器模式

14 } DMA_InitTypeDef;



1) DMA_PeripheralBaseAddr:外设地址,设定 DMA_CPAR 寄存器的值;一般设置为外设的数据寄存器地址,如果是存储器到存储器模式则设置为其中一个存储器地址。 
2) DMA_Memory0BaseAddr:存储器地址,设定 DMA_CMAR 寄存器值;一般设置为我们自定义存储区的首地址。 
3) DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设。它设定DMA_CCR 寄存器的 DIR[1:0]位的值。这里并没有存储器到存储器的方向选择,当使用存储器到存储器时,只需要把其中一个存储器当作外设使用即可。 
4) DMA_BufferSize:设定待传输数据数目,初始化设定 DMA_CNDTR 寄存器的值。 
5) DMA_PeripheralInc:如果配置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定 DMA_CCR 寄存器的 PINC 位的值;一般外设都是只有一个数据寄存器,所以一般不会使能该位。 
6) DMA_MemoryInc:如果配置为DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定 DMA_CCR 寄存器的 MINC 位的值;我们自定义的存储区一般都是存放多个数据的,所以要使能存储器地址自动递增功能。 
7) DMA_PeripheralDataSize:外设数据宽度,可选字节(8位)、半字(16位)和字(32位),它设定 DMA_CCR寄存器的 PSIZE[1:0]位的值。 
8) DMA_MemoryDataSize:存储器数据宽度,可选字节(8 位)、半字(16 位)和字(32位),它设定 DMA_CCR 寄存器的 MSIZE[1:0]位的值。当外设和存储器之间传数据时,两边的数据宽度应该设置为一致大小。 
9) DMA_Mode:DMA 传输模式选择,可选一次传输或者循环传输,它设定DMA_CCR 寄存器的 CIRC 位的值。例程我们的 ADC 采集是持续循环进行的,所以使用循环传输模式。 
10) DMA_Priority:软件设置通道的优先级,有 4 个可选优先级分别为非常高、高、中和低,它设定 DMA_CCR 寄存器的 PL[1:0]位的值。DMA 通道优先级只有在多个 DMA 通道同时使用时才有意义,如果是单个通道,优先级可以随便设置。 
11) DMA_M2M:存储器到存储器模式,使用存储器到存储器时用到,设定DMA_CCR 的位 14MEN2MEN 即可启动存储器到存储器模式。


四、存储器到存储器的实验 
先定义一个静态的源数据,存放在内部Flash存储器中,使用DMA传输,把源数据拷贝到目标地址上(内部SRAM),最后对比源数据和目标地址的数据,看看是否准确传输。 
1、思路要点 
1)使能DMA时钟 
2)配置DMA数据参数 
3)使能DMA,进行传输 
4)等待传输完成,并对源数据和目标地址数据进行比较。 
2、DMA宏定义以及变量定义


1 // 当使用存储器到存储器模式时候,通道可以随便选,没有硬性的规定

2 #define DMA_CHANNEL DMA1_Channel6

3 #define DMA_CLOCK RCC_AHBPeriph_DMA1

5 // 传输完成标志

6 #define DMA_FLAG_TC DMA1_FLAG_TC6

8 // 要发送的数据大小

9 #define BUFFER_SIZE 32

10 

11 /* 定义 aSRC_Const_Buffer 数组作为 DMA 传输数据源

12 * const 关键字将 aSRC_Const_Buffer 数组变量定义为常量类型

13 * 表示数据存储在内部的 FLASH 中

14 */

15 const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]=

16 {

17 0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,

18 0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,

19 0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,

20 0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,

21 0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,

22 0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,

23 0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,

24 0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80

25 };

26 /* 定义 DMA 传输目标存储器

27 * 存储在内部的 SRAM 中

28 */

29 uint32_t aDST_Buffer[BUFFER_SIZE];

aSRC_Const_Buffer[BUFFER_SIZE]定义用来存放源数据,并且使用


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
C51 特殊功能寄存器SFR的名称和地址