DMA(Direct Memory Access)常译为“存储器直接存取”。早在Intel的8086平台上就有了DMA应用了。
一个完好的微操控器一般由CPU、存储器和外设等组件构成。这些组件一般在结构和功用上都是独立的,而各个组件的协谐和交互就由CPU完结。如此一来,CPU作为整个芯片的中心,其处理的作业量是很大的。假如CPU先从A外设拿到一个数据送给B外设运用,一起C外设又需求D外设供给一个数据。。。这样的数据转移作业将使CPU的负荷显得适当深重。
严厉的说,转移数据仅仅CPU的比较不重要的一种作业。CPU最重要的作业室进行数据运算,从加减乘除到一些高档的运算,包含浮点、积分、微分、FFT等。CPU还需求担任杂乱的中止申请和呼应,以确保芯片的实时功用。
理论上常见的操控外设,比方Usart、I2C、SPI乃至是USB等通讯接口,单纯的运用CPU进行协议模仿也是能够完结的,比方51单片机常常运用I/O口模仿I2C协议通讯。但这样既浪费了CPU的资源,一起完结后的功用体现往往和运用专门的硬件模块完结的作用相差甚远。从这个视点来看,各个外设操控器的存在,无疑下降了CPU的担负,解放了CPU的资源。
数据转移这一作业占用了大部分的CPU资源,成为了下降CPU的作业功率的主要原因之一。所以需求一种硬件结构分管CPU这一功用 —— DMA。
从数据转移的视点看,假如要把存储地址A的数值赋给别的一个地址上B的变量,CPU完结进程为首先读出A地址上的数据存储在一个中心变量,然后再转送到B地址的变量上。运用DMA则不需求中心变量,直接将A地址的数值传送到B地址的变量里。无疑减轻了CPU的担负,也提高了数据转移的功率。
stm32中 DMA1有7个通道,DMA2有5个通道。DMA挂载的时钟为AHB总线,其时钟为72Mhz,所以能够完结高速数据转移。
stm32的DMA1通道一览表
本例完结运用CPU和DMA转移同一组数据,经过计时,比较两者的转移功率。
直接操作寄存器
DMA的中止状况寄存器(DMA_ISR):
TEIFx:通道x的传输过错标志(x = 1 … 7) (Channel x transfer error flag) 硬件设置这些位。在DMA_IFCR寄存器的相应位写入’1’能够铲除这儿对应的标志位。
0:在通道x没有传输过错(TE); 1:在通道x发生了传输过错(TE)。
HTIFx:通道x的半传输标志(x = 1 … 7) (Channel x half transfer flag) 硬件设置这些位。在DMA_IFCR寄存器的相应位写入’1’能够铲除这儿对应的标志位。
0:在通道x没有半传输事情(HT); 1:在通道x发生了半传输事情(HT)。
TCIFx:通道x的传输完结标志(x = 1 … 7) (Channel x transfer complete flag) 硬件设置这些位。在DMA_IFCR寄存器的相应位写入’1’能够铲除这儿对应的标志位。
0:在通道x没有传输完结事情(TC); 1:在通道x发生了传输完结事情(TC)。
DMA_IFCR中止标志铲除寄存器:
结构相似DMA_ISR。
CTEIFx:铲除通道x的传输过错标志(x = 1 … 7) (Channel x transfer error clear) 这些位由软件设置和铲除。 0:不起作用 1:铲除DMA_ISR寄存器中的对应TEIF标志。
CHTIFx:铲除通道x的半传输标志(x = 1 … 7) (Channel x half transfer clear) 这些位由软件设置和铲除。 0:不起作用 1:铲除DMA_ISR寄存器中的对应HTIF标志。
CTCIFx:铲除通道x的传输完结标志(x = 1 … 7) (Channel x transfer complete clear) 这些位由软件设置和铲除。 0:不起作用 1:铲除DMA_ISR寄存器中的对应TCIF标志。
CGIFx:铲除通道x的大局中止标志(x = 1 … 7) (Channel x global interrupt clear) 这些位由软件设置和铲除。 0:不起作用 1:铲除DMA_ISR寄存器中的对应的GIF、TEIF、HTIF和TCIF标志。
DMA通道装备寄存器(DMA_CCRx):
MEM2MEM:存储器到存储器形式 (Memory to memory mode) 该位由软件设置和铲除。 0:非存储器到存储器形式; 1:发动存储器到存储器形式。
PL:通道优先级 (Channel priority level) 这些位由软件设置和铲除。00:低 01:中 10:高 11:最高
MSIZE:存储器数据宽度 (Memory size) 这些位由软件设置和铲除。 00:8位 01:16位 10:32位 11:保存
PSIZE:外设数据宽度 (Peripheral size) 这些位由软件设置和铲除。 00:8位 01:16位 10:32位 11:保存
MINC:存储器地址增量形式 (Memory increment mode) 该位由软件设置和铲除。 0:不履行存储器地址增量操作 1:履行存储器地址增量操作
PINC:外设地址增量形式 (Peripheral increment mode) 该位由软件设置和铲除。 0:不履行外设地址增量操作 1:履行外设地址增量操作
CIRC:循环形式 (Circular mode) 该位由软件设置和铲除。 0:不履行循环操作 1:履行循环操作
DIR:数据传输方向 (Data transfer direction) 该位由软件设置和铲除。 0:从外设读 1:从存储器读
TEIE:答应传输过错中止 (Transfer error interrupt enable) 该位由软件设置和铲除。 0:制止TE中止 0:答应TE中止
HTIE:答应半传输中止 (Half transfer interrupt enable) 该位由软件设置和铲除。 0:制止HT中止 0:答应HT中止
TCIE:答应传输完结中止 (Transfer complete interrupt enable) 该位由软件设置和铲除。 0:制止TC中止 0:答应TC中止
EN:通道敞开 (Channel enable) 该位由软件设置和铲除。 0:通道不作业 1:通道敞开
DMA通道x传输数量寄存器(DMA_CNDTRx)(x = 1…7)
低16位有用。这个寄存器操控通道每次传输的数据量,数据传输数量为0至65535。该寄存器会跟着传输的进行而递减,为0表明现已发送完结。
DMA外设地址寄存器(DMA_CPARx)
32位寄存器。外设数据寄存器的基地址,作为数据传输的源或方针。
DMA存储地址寄存器(DMA_CMARx)
存储器地址[31:0],存储器地址作为数据传输的源或方针。
代码如下: (system.h 和stm32f10x_it.h等相关代码参照stm32 直接操作寄存器开发环境装备)
User/main.c
#include#include "system.h"#include "usart.h" #include "dma.h"#include "tim.h" #include "string.h"#define LED1 PAout(4)#define LED2 PAout(5)#define LED3 PAout(6)void Gpio_Init(void);//数据源uc32 SRC_Const_Buffer[32] ={ 0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,0x21314,0x15161718,0x191A1B1C,0x1D1E1F20,0x21324,0x25262728,0x292A2B2C,0x2D2E2F30,0x31324,0x35363738,0x393A3B3C,0x3D3E3F40,0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,0x51525354,0x65758,0x595A5B5C,0x5D5E5F60,0x61626364,0x65768,0x696A6B6C,0x6D6E6F70,0x71727374,0x75768,0x797A7B7C,0x7D7E7F80};//方针方位u32 DST_Buffer[32];int main(void){ u8 i=0;u16 StartTime=0,CPUSpendTime=0,DMASpendTime=0;;Rcc_Init(9); //体系时钟设置Usart1_Init(72,9600);Tim_Init(TIM_2,65535,71); //初始化TIM2定时器,设定重装值和分频值,计时时刻为1us/次Dma_Init(DMA1_Channel1,(u32)SRC_Const_Buffer,(u32)DST_Buffer); //初始化DMA,外设地址示例 &USART1->DRNvic_Init(1,0,DMA1_Channel1_IRQChannel,4); //设置抢占优先级为0,呼应优先级为0,中止分组为4Gpio_Init();StartTime = TIM2->CNT;while(i<32) //CPU转移{DST_Buffer[i]=SRC_Const_Buffer[i];i++;}CPUSpendTime = TIM2->CNT - StartTime;printf("\r\n the CPU spend : %dus! \r\n",CPUSpendTime);if(strncmp((const char *)SRC_Const_Buffer,(const char *)DST_Buffer,32) ==0) //验证传输作用,判别两数组是否相同{printf("\r\n CPU Transmit Success! \r\n");}else{printf("\r\n CPU Transmit Fail! \r\n");}i=0;while(i<32) //清空方针数组,预备DMA转移{DST_Buffer[i]=0;i++;} StartTime = TIM2->CNT;Dma_Enable(DMA1_Channel1,32);//DMA转移while( DMA1_Channel1 -> CNDTR != 0); //等候传输完结DMASpendTime= TIM2->CNT - StartTime;printf("\r\n the DMA spend : %dus! \r\n",DMASpendTime);if(strncmp((const char *)SRC_Const_Buffer,(const char *)DST_Buffer,32) ==0) //验证传输作用,判别两数组是否相同{printf("\r\n DMA Transmit Success! \r\n");}else{printf("\r\n DMA Transmit Fail! \r\n");} while(1); }void Gpio_Init(void){RCC->APB2ENR=1<<2; //使能PORTA时钟 GPIOA->CRL&=0x0FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出GPIOA->CRL=0x34; //USART1 串口I/O设置GPIOA -> CRH&=0xFFFFF00F; //设置USART1 的Tx(PA.9)为第二功用推挽,50MHz;Rx(PA.10)为浮空输入GPIOA -> CRH=0x008B0; }
User/stm32f10x_it.c
#include "stm32f10x_it.h"#include "system.h"#include "stdio.h"#define LED1 PAout(4)#define LED2 PAout(5)#define LED3 PAout(6)#define LED4 PAout(7)void DMAChannel1_IRQHandler(void) //和发动文件有关,STM32F10x.s中 和 STM32F10x_md.s DMA中止接口函数不同{if( DMA1 ->ISR & (1<<1)) //传输完结中止{LED1 = 1;DMA1->IFCR = 1<<1; //铲除传输完结中止}if( DMA1 ->ISR & (1<<2)) //半传输完结中止{DMA1 ->IFCR = 1<<2; //铲除半传输完结中止}if( DMA1 ->ISR & (1<<3)) //传输过错中止{LED4 =1 ;DMA1 ->IFCR = 1<<3; //铲除传输过错中止}DMA1 ->IFCR = 1<<0; //铲除此通道的中止}
Library/src/dma.c
#include#include "system.h"#include "dma.h"//DMA通道初始化函数//传输方向:存储器 -> 存储器形式 ,32位数据形式,存储器增量形式//参数阐明:// DMA_CHx :挑选DMA操控器通道,DMA1有1-7,DMA2有1-4// P_Adress :外设地址// M_Adress :存储器地址void Dma_Init(DMA_Channel_TypeDef * DMA_CHx,u32 P_Address ,u32 M_Address){RCC->AHBENR = 1<<0;DMA_CHx -> CCR &= 0xFFFF0; //复位 DMA_CHx -> CCR = 1<<1; //答应传输完结中止//DMA_CHx -> CCR = 1<<2; //答应半传输中止DMA_CHx -> CCR = 1<<3; //答应传输过错中止 读写一个保存的地址区域,将会发生DMA传输过错 //设定数据传输方向DMA_CHx -> CCR = 0<<4; //设定数据传输方向 0:从外设读 1:从存储器读DMA_CHx -> CCR = 0<<5; //0:不履行循环操作 1:履行循环操作 //设定地址增量DMA_CHx -> CCR = 1<<6; //0:不履行外设地址增量操作 1:履行外设地址增量操作DMA_CHx -> CCR = 1<<7; //0:不履行存储器地址增量操作 1:履行存储器地址增量操作 //设定外设数据宽度 SDMA_CHx -> CCR = 0<<8; //外设数据宽度,由[9:8]两位操控DMA_CHx -> CCR = 1<<9; //00:8位 01:16位 10:32位 11:保存 //设定存储数据宽度DMA_CHx -> CCR = 0<<10; //存储器数据宽度,由[11:10]两位操控DMA_CHx -> CCR = 1<<11; //00:8位 01:16位 10:32位 11:保存 //设定为中等优先级DMA_CHx -> CCR = 1<<12; //通道优先级,由[13:12]两位操控DMA_CHx -> CCR = 1<<13; //00:低 01:中 10:高 11:最高 DMA_CHx -> CCR = 1<<14; //0:非存储器到存储器形式; 1:发动存储器到存储器形式。 //有必要装备好通道后装备地址DMA_CHx -> CPAR = (u32)P_Address; //设定外设寄存器地址DMA_CHx -> CMAR = (u32)M_Address; //设定数据存储器地址}//DMA通道使能//参数阐明:// DMA_CHx :挑选DMA操控器通道,DMA1有1-7,DMA2有1-4// Number :数据传输量void Dma_Enable(DMA_Channel_TypeDef * DMA_CHx,u16 Number){DMA_CHx -> CCR &= ~(1<<0); //关闭上一次DMA传输DMA_CHx -> CNDTR = Number; //数据传输量DMA_CHx -> CCR = 1<<0; //开端DMA传输 }
Library/inc/dma.h
#includevoid Dma_Init(DMA_Channel_TypeDef * DMA_CHx,u32 P_Adress ,u32 M_Address);void Dma_Enable(DMA_Channel_TypeDef * DMA_CHx,u16 Number);
直接操作寄存器输出:
the CPU spend : 972us!
CPU Transmit Success!
the DMA spend : 5us!
DMA Transmit Success!
库函数操作
mian.c
#include "stm32f10x.h"#include "stdio.h"#include "string.h"#define PRINTF_ON 1#define BufferSize 32vu16 LeftDataCounter;vu32 Tick;uc32 SRC_Const_Buffer[BufferSize] = { 0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,0x21314,0x15161718,0x191A1B1C,0x1D1E1F20,0x21324,0x25262728,0x292A2B2C,0x2D2E2F30,0x31324,0x35363738,0x393A3B3C,0x3D3E3F40,0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,0x51525354,0x65758,0x595A5B5C,0x5D5E5F60,0x61626364,0x65768,0x696A6B6C,0x6D6E6F70,0x71727374,0x75768,0x797A7B7C,0x7D7E7F80};u32 DST_Buffer[BufferSize];u8 i=0,DMASpendTime=0,CPUSpendTime=0;void RCC_Configuration(void);void GPIO_Configuration(void);void NVIC_Configuration(void);void USART_Configuration(void);void DMA_Configuration(void);int main(void){RCC_Configuration();GPIO_Configuration();NVIC_Configuration();USART_Configuration();DMA_Configuration();SysTick_Config(72);Tick = 0;while(i
stm32f10x_it.c
#include "stm32f10x_it.h"#include "stdio.h"extern vu32 Tick;extern vu16 LeftDataCounter;void SysTick_Handler(void){Tick++;}void DMA1_Channel6_IRQHandler(void){LeftDataCounter = DMA_GetCurrDataCounter(DMA1_Channel6); //获取剩下待传输数据DMA_ClearITPendingBit(DMA1_IT_GL6);}
库函数输出:
Transmit Success!
the CPU spend : 68us!
the DMA spend : 7us!