您的位置 首页 新品

STM32之DMA

DMA,全称为:DirectMemoryAccess,即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场…

DMA,全称为:Direct Memory Access,即直接存储器拜访。DMA传输办法无需CPU 直接操控传输,也没有中止处理办法那样保存现场和康复现场的进程,经过硬件为RAM 与I/O设备拓荒一条直接传送数据的通路,能使CPU 的功率大为进步。

STM32中 DMA1有7个通道,DMA2有5个通道(DMA2 仅存在大容量产品中)。DMA挂载的时钟为AHB总线,其时钟为72Mhz,所以能够完结高速数据转移。
STM32F103RBT6 只要1 个DMA操控器,DMA1 ,下面咱们就针对DMA1 进行介绍。
从外设(TIMx、ADC、SPIx 、I2Cx 和USARTx )发生的DMA恳求,经过逻辑或输入到DMA操控器,这就意味着一起只能有一个恳求有用。外设的DMA恳求,能够经过设置相应的外设寄存器中的操控位,被独登时敞开或封闭。
DMA1各通道一览:
这儿咱们要运用的是串口 1 的 DMA 传送,也便是要用到通道 4。
DMA1通道4的装备办法如下:
dma.c首要代码:

[cpp]view plaincopy

  1. u16DMA1_MEM_LEN;//保存DMA每次数据传送的长度
  2. //DMA1的各通道装备
  3. //这儿的传输方法是固定的,这点要依据不同的状况来修正
  4. //从存储器->外设形式/8位数据宽度/存储器增量形式
  5. //DMA_CHx:DMA通道CHx
  6. //cpar:外设地址
  7. //cmar:存储器地址
  8. //cndtr:数据传输量
  9. voidMYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32cpar,u32cmar,u16cndtr)
  10. {
  11. RCC->AHBENR|=1<<0;//敞开DMA1时钟
  12. delay_ms(1);//等候DMA时钟安稳
  13. DMA_CHx->CPAR=cpar;//DMA1外设地址
  14. DMA_CHx->CMAR=(u32)cmar;//DMA1,存储器地址
  15. DMA1_MEM_LEN=cndtr;//保存DMA传输数据量
  16. DMA_CHx->CNDTR=cndtr;//DMA1,传输数据量
  17. DMA_CHx->CCR=0X00000000;//复位
  18. DMA_CHx->CCR|=1<<4;//从存储器读
  19. DMA_CHx->CCR|=0<<5;//一般形式
  20. DMA_CHx->CCR|=0<<6;//外设地址非增量形式
  21. DMA_CHx->CCR|=1<<7;//存储器增量形式
  22. DMA_CHx->CCR|=0<<8;//外设数据宽度为8位
  23. DMA_CHx->CCR|=0<<10;//存储器数据宽度8位
  24. DMA_CHx->CCR|=1<<12;//中等优先级
  25. DMA_CHx->CCR|=0<<14;//非存储器到存储器形式
  26. }
  27. //敞开一次DMA传输
  28. voidMYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
  29. {
  30. DMA_CHx->CCR&=~(1<<0);//封闭DMA传输
  31. DMA_CHx->CNDTR=DMA1_MEM_LEN;//DMA1,传输数据量
  32. DMA_CHx->CCR|=1<<0;//敞开DMA传输
  33. }
}
在主函数里首要有这几个句子完结DMA传输:
1.首要装备DMA1通道4相关参数
MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,5200);//DMA1通道4,外设为串口1,存储器为SendBuff,长度5200.
2.然后将待发送内容装入存储器
SendBuff[i]=TEXT_TO_SEND[t];
3.然后敞开一次DMA传输
MYDMA_Enable(DMA1_Channel4);//开端一次DMA传输!
4.监控传送进展。
pro=DMA1_Channel4->CNDTR;//得到当时还剩下多少个数据

DMA能够传递多少数据?

传统的DMA的概念是用于大批量数据的传输,可是我了解,在STM32中,它的概念被扩展了,或许更多的时分快速是其运用的要点。数据能够从1~65535个。

直接存储器存取(Direct Memory Access,DMA)是计算机科学中的一种内存拜访技能。它答应某些电脑内部的硬体子体系(电脑外设),能够独登时直接读写体系存储器,而不需绕道 CPU。在平等程度的CPU担负下,DMA是一种快速的数据传送办法。它答应不同速度的硬件设备来交流,而不需求依于 CPU的很多中止恳求。【摘自Wikipedia】

现在越来越多的单片机选用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的不同中止(传输完结、半传输、传输完结)经过“线或”办法衔接至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模块)。

一个简略的比如 transfera 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

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);

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;
//u16AD_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);

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);
//###### 内部温度传感器Channel 16 ###################
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_55Cycles5);

使能ADC1的DMA恳求映像
ADC_DMACmd(ADC1, ENABLE);

ADC_Cmd(ADC1, ENABLE);

//运用之前一定要校准
ADC_ResetCalibration(ADC1);

while(ADC_GetResetCalibrationStatus(ADC1));

ADC_StartCalibration(ADC1);

while(ADC_GetCalibrationStatus(ADC1));

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/xinpin/272410.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部