DMA部分我用到的相对简略,当然,或许这是新东西,我暂时还用不到它的杂乱功用吧。下面用问答的方法表达我的思路。
DMA有什么用?
直接存储器存取用来供给在外设和存储器之间或许存储器和存储器之间的高速数据传输。无须CPU的干涉,经过DMA数据能够快速地移动。这就节省了CPU的资源来做其他操作。
有多少个DMA资源?
有两个DMA操控器,DMA1有7个通道,DMA2有5个通道。
数据从什么地方送到什么地方?
外设到SRAM(I2C/UART等获取数据并送入SRAM);
SRAM的两个区域之间;
外设到外设(ADC读取数据后送到TIM1操控其发生不同的PWM占空比);
SRAM到外设(SRAM中预先保存的数据送入DAC发生各种波形);
……还有一些现在还搞不清楚的。
DMA能够传递多少数据?
传统的DMA的概念是用于大批量数据的传输,可是我了解,在STM32中,它的概念被扩展了,或许更多的时分快速是其运用的要点。数据能够从1~65535个。
直接存储器存取(Direct Memory Access,DMA)是计算机科学中的一种内存拜访技能。它答应某些电脑内部的硬体子体系(电脑外设),能够独立地直接读写体系存储器,而不需绕道 CPU。在平等程度的CPU担负下,DMA是一种快速的数据传送方法。它答应不同速度的硬件设备来交流,而不需求依于 CPU的很多中止恳求。
现在越来越多的单片机选用DMA技能,供给外设和存储器之间或许存储器之间的高速数据传输。当 CPU 初始化这个传输动作,传输动作本身是由DMA 操控器来实施和完结。STM32就有一个DMA操控器,它有7个通道,每个通道专门用来办理一个或多个外设对存储器拜访的恳求,还有一个裁定器来和谐各个DMA恳求的优先权。
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的不同中止(传输完结、半传输、传输完结)经过“线或”方法衔接至NVIC,需求在中止例程中进行判别。
进行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 Enable*/
DMA_Cmd(DMA_Channel1,ENABLE);
哈哈哈!这一句我想我就不罗嗦了,我们一看就理解。
至此,整个DMA总算设置好了,可是,DMA通道又是怎样与外设联络在一起的呢?哈哈,这也是我最初最想知道的一个工作,别急!容我想喝口茶~~~~~~哈哈哈!
要使DMA与外设树立有用衔接,这不是DMA本身的工作,是各个外设的工作,每个外设都有 一个xxx_DMACmd(XXXx,Enable )函数,假如使DMA与ADC树立有用联络,就运用ADC_DMACmd(ADC1,Enable); (这儿我启用了ADC中的ADC1模块)。
一个简略的比如 transfer a word data buffer from FLASH memory to embedded SRAM memory.
在V3.1.2库的方位
STM32F10x_StdPeriph_Lib_V3.1.2\Project\STM32F10x_StdPeriph_Examples\DMA\FLASH_RAM
/* DMA1 channel6 configuraTIon */
DMA_DeInit(DMA1_Channel6);
//peripheral base address
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Const_Buffer;
//memory base address
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
//数据传输方向 Peripheral is source
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
//缓冲区巨细 Number of data to be transferred (0 up to 65535)。数据传输数目
DMA_InitStructure.DMA_BufferSize = BufferSize;
// the Peripheral address register is incremented
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
//the memory address register is incremented
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);
/* Enable DMA1 Channel6 Transfer Complete interrupt */
DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);
/* Enable DMA1 Channel6 transfer */
DMA_Cmd(DMA1_Channel6, ENABLE);
=======================================================================
外设的DMA恳求映像
要使DMA与外设树立有用衔接,这不是DMA本身的工作,是各个外设的工作,每个外设都有 一个
xxx_DMACmd(XXXx,Enable )函数,假如使DMA与ADC树立有用联络,就运用 ADC_DMACmd
(ADC1,Enable); (这儿我启用了ADC中的ADC1模块)。
/* DMA1 channel1 configuration ———————————————-*/
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&AD_Value;
//u16 AD_Value[2]; 不加&应该也能够 数组名 代表地址
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);
/* Enable DMA1 channel 1 */
DMA_Cmd(DMA1_Channel1, ENABLE);
/* ADC1 configuration——————————————————*/
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);
//内部温度传感器 增加这一句
/* Enable the temperature sensor and vref internal channel */
ADC_TempSensorVrefintCmd(ENABLE);
//############## 改了
//################ Channel 10(电位器)
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_13Cycles5);
//###### 内部温度传感器 Channel 16 ###################
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_55Cycles5);
/* Enable ADC1 DMA */使能ADC1的DMA恳求映像
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC1 reset calibaration register */ //运用之前一定要校准
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1));
/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1));
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConvCmd(ADC1, ENABLE);