一、DMA简介
可参考STM32中文参考手册第10章DMA控制器 。
直接存储器存取(Direct Memory Access,DMA) 用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路, 能使 CPU 的效率大为提高。
DMA 是干什么用的呢?一般来说,如果没有DMA ,CPU如果想从外设“搬运”数据到存储器(Peripheral to Memory,P2M),或者从一个存储器到另一个存储器(Memory To Memory,M2M),数据必须要经过CPU,这就需要CPU 去亲力亲为。在运行过程中,CPU经常要为这种琐碎的事情被打断,很影响效率。所以,就有了DMA。现在,CPU 就不用那么麻烦了,CPU 只需要“叫”一声DMA,说你帮我去搬一下数据,从这搬到那,DMA 就会代替 CPU 完成这份工作,再也不用 CPU 自己去做了。
在刚学习 STM32 的时候,我们就接触过 STM32 的系统架构,如下图所示。可以看到,STM32 一共有两个DMA控制器:DMA1和DMA2(其中DMA2控制器只存在于大容量和互联型产品中),通过总线矩阵和DMA总线与各个不同的外设连接。这些外设可以向DMA发出DMA请求,由仲裁器根据请求优先级确定响应的顺序(SPI/I2S3、UART4、TIM5、TIM6、TIM7和DAC的DMA请求仅存在于大容量产品和互联型产品)。
二、DMA功能框图
下面图片来自STM32中文参考手册10.2节DMA主要特性 。实际上该功能框图是将上面的系统架构图详细化了。为了方便讲解,我们(火哥)将该图分为三个部分讲解。
1. DMA请求
本节参考STM32中文参考手册10.3.7节DMA请求映像 。
在 STM32 最多有2个DMA控制器(DMA2仅存在大容量产品中),12个独立的可配置的通道(请求),DMA1有7个通道。DMA2有5个通道 。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。
对于DMA1控制器,通过逻辑或 输入到DMA1控制器,这意味着同时只能有一个请求有效。同样,对于DMA2控制器,通过逻辑或 输入到DMA1控制器,也只能有一个请求有效。至于当多个请求同时到来的情况,是我们下面要讲的仲裁器 。
下面图是DMA1和DMA2的请求映射表:
DMA1:
DMA2:
2. 通道
本节参考STM32中文参考手册10.3.3节DMA通道 。
每个通道都可以在有固定地址的外设寄存器和存储器地址之间执行DMA传输。DMA传输的数据量是可编程的,最大达到65535。包含要传输的数据项数量的寄存器,在每次传输后递减。
每个通道都有3个事件标志,这3个事件标志逻辑或 成为一个单独的中断请求(位于DMA 中断状态寄存器(DMA_ISR)):
通过设置DMA_CCRx寄存器中的PINC(外设地址)和MINC(寄存器地址)标志位,外设和存储器的指针在每次传输后可以有选择地完成自动增量。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值,增量值取决与所选的数据宽度为1、2或4。递减模式则是每次传输后地址递减。
3. 仲裁器
本节参考STM32中文参考手册10.3.2节仲裁器 。
当多个DMA请求来临时,需要仲裁器来分配请求的响应优先级。软件优先级 分为4个,可在DMA_CCRx寄存器中设置:
最高优先级(DMA_Priority_VeryHigh)
高优先级(DMA_Priority_High)
中等优先级(DMA_Priority_Medium)
低优先级(DMA_Priority_Low)
如果2个请求有相同的软件优先级,则比较硬件优先级 :较低编号的通道比较高编号的通道有较高的优先权。举个例子,通道2优先于通道4,则通道2的DMA请求先响应,通道4的DMA请求则被挂起。在寄存器DMA_CCRx:PL[1:0]可以配置。
二、DMA的结构体定义和库函数定义
1. DMA初始化结构体
在头文件stm32f10x_dma.h 中定义了DMA初始化结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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;
2. DMA库函数
在源文件stm32f10x_dma.c 中定义了DMA库函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void DMA_DeInit (DMA_Channel_TypeDef* DMAy_Channelx) ;void DMA_Init (DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct) ;void DMA_StructInit (DMA_InitTypeDef* DMA_InitStruct) ;void DMA_Cmd (DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState) ;void DMA_ITConfig (DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState) ;void DMA_SetCurrDataCounter (DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber) ; uint16_t DMA_GetCurrDataCounter (DMA_Channel_TypeDef* DMAy_Channelx) ;FlagStatus DMA_GetFlagStatus (uint32_t DMAy_FLAG) ; void DMA_ClearFlag (uint32_t DMAy_FLAG) ;ITStatus DMA_GetITStatus (uint32_t DMAy_IT) ; void DMA_ClearITPendingBit (uint32_t DMAy_IT) ;
3. DMA配置流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t )aSRC_Const_Buffer; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t )aDST_Buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; 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);
三、DMA应用简单实例
1. 存储器到存储器(M2M)
在FLASH中定义好要传输的数据,在SRAM中定义好用来接收FLASH数据的变量。
初始化DMA,主要是配置DMA初始化结构体。
编写比较函数。
编写main函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 #ifndef __DMA_H #define __DMA_H #include "stm32f10x_dma.h" #define BUFFER_SIZE 32 void M2M_DMA_Config (void ) ;uint8_t Buffercmp (const uint32_t * pBuffer, uint32_t * pBuffer1, uint16_t BufferLength) ;#endif #include "dma.h" #include "usart.h" const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= { 0x01020304 ,0x05060708 ,0x090A0B0C ,0x0D0E0F10 , 0x11121314 ,0x15161718 ,0x191A1B1C ,0x1D1E1F20 , 0x21222324 ,0x25262728 ,0x292A2B2C ,0x2D2E2F30 , 0x31323334 ,0x35363738 ,0x393A3B3C ,0x3D3E3F40 , 0x41424344 ,0x45464748 ,0x494A4B4C ,0x4D4E4F50 , 0x51525354 ,0x55565758 ,0x595A5B5C ,0x5D5E5F60 , 0x61626364 ,0x65666768 ,0x696A6B6C ,0x6D6E6F70 , 0x71727374 ,0x75767778 ,0x797A7B7C ,0x7D7E7F80 }; uint32_t aDST_Buffer[BUFFER_SIZE];void M2M_DMA_Config (void ) { DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t )aSRC_Const_Buffer; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t )aDST_Buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; 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 Buffercmp (const uint32_t * pBuffer, uint32_t * pBuffer1, uint16_t BufferLength) { while (BufferLength--) { if (*pBuffer != *pBuffer1) { return 0 ; } pBuffer++; pBuffer1++; } return 1 ; } #include "stm32f10x.h" #include "dma.h" #include "led.h" #include "delay.h" extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];extern uint32_t aDST_Buffer[BUFFER_SIZE];int main (void ) { uint8_t status = 0 ; delay_init(); M2M_DMA_Config(); LED_Init(); LED0_ON; LED1_OFF; delay_ms(500 ); while ( DMA_GetFlagStatus(DMA1_FLAG_TC6) == RESET ); status = Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE); if (status == 0 ) { LED0_ON; LED1_OFF; } else { LED0_OFF; LED1_ON; } }
2. 存储器到USART串口(M2P)
初始化串口(从现有的例程移植过来)
配置DMA初始化结构体。
编写主函数(开启串口发送DMA请求)。
usart.c 和 usart.h 略,这两个文件的具体内容可见上一次笔记。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #ifndef __DMA_H #define __DMA_H #include "stm32f10x_dma.h" #define SENDBUFFER_SIZE 500 void USART_DMA_Config (void ) ;#endif #include "dma.h" #include "usart.h" uint8_t SendBuffer[SENDBUFFER_SIZE];void USART_DMA_Config (void ) { DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t )USART1_DR_ADDRESS; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t )SendBuffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = SENDBUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; 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); } #include "stm32f10x.h" #include "dma.h" #include "usart.h" #include "led.h" #include "delay.h" extern uint8_t SendBuffer[SENDBUFFER_SIZE];int main (void ) { uint16_t i = 0 ; delay_init(); LED_Init(); for (i = 0 ; i < SENDBUFFER_SIZE; i++) { SendBuffer[i] = 'p' ; } USART_Config(); USART_DMA_Config(); USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); while (1 ) { LED1 = !LED1; delay_ms(500 ); } }