DMA,全称为:Direct Memory Access,即直接存储器拜访。DMA传输办法无需CPU 直接操控传输,也没有中止处理办法那样保存现场和康复现场的进程,经过硬件为RAM 与I/O设备拓荒一条直接传送数据的通路,能使CPU 的功率大为进步。
STM32F103RBT6 只要1 个DMA操控器,DMA1 ,下面咱们就针对DMA1 进行介绍。
从外设(TIMx、ADC、SPIx 、I2Cx 和USARTx )发生的DMA恳求,经过逻辑或输入到DMA操控器,这就意味着一起只能有一个恳求有用。外设的DMA恳求,能够经过设置相应的外设寄存器中的操控位,被独登时敞开或封闭。




- u16DMA1_MEM_LEN;//保存DMA每次数据传送的长度
- //DMA1的各通道装备
- //这儿的传输方法是固定的,这点要依据不同的状况来修正
- //从存储器->外设形式/8位数据宽度/存储器增量形式
- //DMA_CHx:DMA通道CHx
- //cpar:外设地址
- //cmar:存储器地址
- //cndtr:数据传输量
- voidMYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32cpar,u32cmar,u16cndtr)
- {
- RCC->AHBENR|=1<<0;//敞开DMA1时钟
- delay_ms(1);//等候DMA时钟安稳
- DMA_CHx->CPAR=cpar;//DMA1外设地址
- DMA_CHx->CMAR=(u32)cmar;//DMA1,存储器地址
- DMA1_MEM_LEN=cndtr;//保存DMA传输数据量
- DMA_CHx->CNDTR=cndtr;//DMA1,传输数据量
- DMA_CHx->CCR=0X00000000;//复位
- DMA_CHx->CCR|=1<<4;//从存储器读
- DMA_CHx->CCR|=0<<5;//一般形式
- DMA_CHx->CCR|=0<<6;//外设地址非增量形式
- DMA_CHx->CCR|=1<<7;//存储器增量形式
- DMA_CHx->CCR|=0<<8;//外设数据宽度为8位
- DMA_CHx->CCR|=0<<10;//存储器数据宽度8位
- DMA_CHx->CCR|=1<<12;//中等优先级
- DMA_CHx->CCR|=0<<14;//非存储器到存储器形式
- }
- //敞开一次DMA传输
- voidMYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
- {
- DMA_CHx->CCR&=~(1<<0);//封闭DMA传输
- DMA_CHx->CNDTR=DMA1_MEM_LEN;//DMA1,传输数据量
- DMA_CHx->CCR|=1<<0;//敞开DMA传输
- }

DMA能够传递多少数据?
直接存储器存取(Direct Memory Access,DMA)是计算机科学中的一种内存拜访技能。它答应某些电脑内部的硬体子体系(电脑外设),能够独登时直接读写体系存储器,而不需绕道 CPU。在平等程度的CPU担负下,DMA是一种快速的数据传送办法。它答应不同速度的硬件设备来交流,而不需求依于 CPU的很多中止恳求。【摘自Wikipedia】
现在越来越多的单片机选用DMA技能,供给外设和存储器之间或许存储器之间的高速数据传输。当 CPU 初始化这个传输动作,传输动作本身是由
DMA 操控器和Cortex-M3核同享体系数据总线履行直接存储器数据传输。当CPU和DMA一起拜访相同的方针(RAM或外设)时,DMA恳求可能会中止 CPU拜访体系总线达若干个周期,总线裁定器履行循环调度,以确保CPU至少能够得到一半的体系总线(存储器或外设)带宽。
在发生一个工作后,外设发送一个恳求信号到DMA操控器。DMA操控器依据通道的优先权处理恳求。当DMA操控器开端拜访外设的时分,DMA操控器当即发送给外设一个应对信号。当从DMA操控器得到应对信号时,外设当即开释它的恳求。一旦外设开释了这个恳求,DMA操控器一起吊销应对信号。假如发生更多的恳求时,外设能够发动下次处理。
总归,每个DMA传送由3个操作组成:
1. 从外设数据寄存器或许从DMA_CMARx寄存器指定地址的存储器单元履行加载操作。
2. 存数据到外设数据寄存器或许存数据到DMA_CMARx寄存器指定地址的存储器单元。
3. 履行一次DMA_CNDTRx寄存器的递减操作。该寄存器包括未完结的操作数目。
裁定器依据通道恳求的优先级来发动外设/存储器的拜访。优先级分为两个等级:软件(4个等级:最高、高、中等、低)、硬件(有较低编号的通道比具有较高编号的通道有较高的优先权)。
能够在DMA传输过半、传输完结和传输错误时发生中止。
STM32中DMA的不同中止(传输完结、半传输、传输完结)经过“线或”办法衔接至NV%&&&&&%,需求在中止例程中进行判别。
进行DMA装备前,不要忘了在RCC设置中使能DMA时钟。STM32的DMA操控器挂在AHB总线上。
DMA总共有7个通道,各个通道的DMA映射联络如下:
外设的工作衔接至相应DMA通道,每个通道均能够经过软件触发完结存储器内部的DMA数据传输(M2M形式)
Tips:库2.0中函数RCC_AHBPeriphClockCmd的参数由“RCC_AHBPeriph_DMA”改成“RCC_AHBPeriph_DMA1”(假如是DMA1操控器的话)。
DMA的传输标志位(CHTIFx、CTCIFx、CGIFx)由硬件设置为“1”,但需求软件清零,在中止服务程序中铲除。当CGIFx(大局中止标志位)清零后,CHTIFx 和 CTCIFx均清零。
进程:怎样启用DMA?首要,众所周知的是初始化,任何设备启用前都要对其进行初始化,要对模块初始化,还要先了解该模块相应的结构及其函数,以便正确的设置;因为DMA较为杂乱,我就只谈谈DMA的根本结构和和常用函数,这些都是ST公司供给在库函数中的。
1、 下面代码是一个规范DMA设置,当然实践运用中可依据实践状况进行削减:
DMA_DeInit(DMA_Channel1);
上面这句是给DMA装备通道,依据ST供给的材料,STM3210Fx中DMA包括7个通道(CH1~CH7),也便是说能够为外设或memory供给7座“桥梁”(请答应我运用桥梁一词,我觉得更简略了解,哈哈,别“拍砖”呀!);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
上面句子中的DMA_InitStructure是一个DMA结构体,在库中有声明晰,当然运用时就要先界说 了;DMA_PeripheralBaseAddr是该结构体中一个数据成员,给DMA一个开始地址,好比是一个buffer开始地址,数据流程是:外设 寄存器à DMA_PeripheralBaseAddàmemory中变量空间(或flash中数据空间等),ADC1_DR_Address是我界说的一个地址 变量;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue;
上面这句很显然是DMA要衔接在Memory中变量的地址,ADC_ConvertedValue是我自己在memory中界说的一个变量;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
上面的这句是设置DMA的传输方向,就如前面我所说的,DMA能够双向传输,也能够单向传输,这儿设置的是单向传输,假如需求双向传输:把DMA_DIR_PeripheralSRC改成DMA_DIR_PeripheralDST即可。
DMA_InitStructure.DMA_BufferSize = 2;
上面的这句是设置DMA在传输时缓冲区的长度,前面有界说过了buffer的开始地址:ADC1_DR_Address ,为了安全性和可靠性,一般需求给buffer界说一个贮存片区,这个参数的单位有三种类型:Byte、HalfWord、word,我设置的2个 half-word(见下面的设置);32位的MCU中1个half-word占16 bits。
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
上面的这句是设置DMA的外设递加形式,假如DMA选用的通道(CHx)有多个外设衔接,需求运用外设递加形式:DMA_PeripheralInc_Enable;我的比如里DMA只与ADC1树立了联络,所以选用DMA_PeripheralInc_Disable
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
上面的这句是设置DMA的内存递加形式,DMA拜访多个内存参数时,需求运用DMA_MemoryInc_Enable,当DMA只拜访一个内存参数时,可设置成:DMA_MemoryInc_Disable。
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
上面的这句是设置DMA在拜访时每次操作的数据长度。有三种数据长度类型,前面现已讲过了,这儿不在叙说。
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
与上面相同。在此不再阐明。
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
上面的这句是设置DMA的传输形式:接二连三的循环形式,若只想拜访一次后就不要拜访了(或按指令操作来反诘,也便是想要它拜访的时分就拜访,不要它拜访的时分就中止),能够设置成通用形式:DMA_Mode_Normal
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
上面的这句是设置DMA的优先等级:能够分为4级:VeryHigh,High,Medium,Low.
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
上面的这句是设置DMA的2个memory中的变量相互拜访的
DMA_Init(DMA_Channel1,&DMA_InitStructure);
前面那些都是对DMA结构体成员的设置,在次再共同对DMA整个模块做一次初始化,使得DMA各成员与上面的参数共同。
DMA_Cmd(DMA_Channel1,ENABLE);
哈哈哈!这一句我想我就不罗嗦了,我们一看就理解。
至此,整个DMA总算设置好了,可是,DMA通道又是怎样与外设联络在一起的呢?哈哈,这也是我最初最想知道的一个工作,别急!容我想喝口茶~~~~~~哈哈哈!
要使DMA与外设树立有用衔接,这不是DMA本身的工作,是各个外设的工作,每个外设都有 一个xxx_DMACmd(XXXx,Enable )函数,假如使DMA与ADC树立有用联络,就运用ADC_DMACmd(ADC1,Enable); (这儿我启用了ADC中的ADC1模块)。
一个简略的比如 transfer
在V3.1.2库的方位
STM32F10x_StdPeriph_Lib_V3.1.2\Project\STM32F10x_StdPeriph_Examples\DMA\FLASH_RAM
DMA_DeInit(DMA1_Channel6);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Const_Buffer;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
//缓冲区巨细 Number of data to be transferred (0 up to 65535).数据传输数目
DMA_InitStructure.DMA_BufferSize = BufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//the Peripheral data width
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;
//the DMAy Channelx will be used in memory-to-memory transfer
//DMA通道的操作能够在没有外设恳求的状况下进行,这种操作便是存储器到存储器形式。
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA1_Channel6, ENABLE);
=======================================================================
外设的DMA恳求映像
要使DMA与外设树立有用衔接,这不是DMA本身的工作,是各个外设的工作,每个外设都有 一个
xxx_DMACmd(XXXx,Enable )函数,假如使DMA与ADC树立有用联络,就运用 ADC_DMACmd
(ADC1,Enable); (这儿我启用了ADC中的ADC1模块)。
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&AD_Value;
//u16
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 2;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 2;
ADC_Init(ADC1, &ADC_InitStructure);
//内部温度传感器
ADC_TempSensorVrefintCmd(ENABLE);
//##############
//################ Channel 10(电位器)
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_13Cycles5);
//###### 内部温度传感器
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_55Cycles5);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatu
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));