STM32之DMA的认识和使用

发布者:心满愿望最新更新时间:2019-01-04 来源: eefocus关键字:STM32  DMA 手机看文章 扫描二维码
随时随地手机看文章

DMA是什么? 

DMA(Direct Memory Access,直接内存存取) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。(百度百科) DMA可以看作是数据搬运工

DMA有什么用? 

直接存储器存取用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。

stm32有多少资源? 

有两个DMA控制器,DMA1有7个通道,DMA2有5个通道。


数据从什么地方到什么地方? 

外设到SRAM(I2C/UART等获取数据并送入SRAM); 

SRAM的两个区域之间; 

外设到外设(ADC读取数据后送到TIM1控制其产生不同的PWM占空比); 

SRAM到外设(SRAM中预先保存的数据送入DAC产生各种波形); 等等等..


DMA的使用: 


怎样启用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模块)。


一个简单的例子:


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


关键字:STM32  DMA 引用地址:STM32之DMA的认识和使用

上一篇:STM32F10x DMA介绍以及 dma usart数据收发
下一篇:通用定时器——输入捕获实验

推荐阅读最新更新时间:2024-03-16 16:21

STM32的I2C的原理与使用(24C02附代码)
一、IIC的定义 I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS开发用于连接微控制器及其外围设备。IIC是一种多向控制总线,就是说多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实施数据传输的控制源。这种方式简化信号传输总线。 它由数据线SDA和时钟SCL构成的串行总线,接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上,可发送接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。 IIC是半双工通信方式。 二、多主机I2C总线系统结构 2)多设备的使用举例
[单片机]
<font color='red'>STM32</font>的I2C的原理与使用(24C02附代码)
更改STM32定时器周期后的输出疑问分析
他先将STM32某定时器计数周期设为0xff,单向递增计数模式,OC比较值设为0x7F。 在某时刻将新的计数周期0x7F与比较值0x3F加载到影子寄存器。 当正在进行的一个周期结束后,经示波器测量确实可以看到其下一个周期发生变化,但其周期明显与预设值对应不上! 再次经过一个周期,定时器才会按照预设值稳定输出。 以上是咨询者不算很清晰的描述【其实咨询TIMER问题最好配上时序图】,但可以了解到他要做的事情就是在ARR=0xff,ccr=0x7F的PWM输出状态下,于某时刻赋予ARR和CCR新值以改变输出波形。 事情不算复杂,疑点就是为什么需要2个周期延时后才能有基于新配置的稳定输出。 【他这里说的2个周期显然不清楚到底指的前
[单片机]
更改<font color='red'>STM32</font>定时器周期后的输出疑问分析
最简单的方式 创建STM32的工程(使用标准库)-1
1:看到网上好多资料有关怎么创建STM32工程文件的,比较麻烦,都是建啥文件夹,然后拷贝文件,挪来挪去。其实在STM32标准库中本来就有工程文件的样板,如下图,有5中IDE的工程文件样板。我这里选用的标准库是F1系列的。 2:我电脑装的IDE是MDK uVision V4.00。打开上面MDK-ARM文件夹,双击直接打开Project.uvproj 工程文件。 3:直接编译-build,没问题! 这里介绍下工程目录: STM32100E-EVAL 是当前工程的名字,100E应该是一款MCU,EVAL尾缀是evalution的简写,评估的意思。 User: 里面放main.c 和 stm32f10x_it.c ,我
[单片机]
最简单的方式 创建<font color='red'>STM32</font>的工程(使用标准库)-1
STM32_RTC笔记
RTC这东西晕晕的,因为一个模块涉及到了RTC,BKP,RCC多个模块,之间的关系让人有点模糊 入门的知识请大家看手册,我来总结: 总之,RTC只是个能靠电池维持运行的32位定时器over! 所以,使用时要注意以下问题: 1. 上电后要检查备份电池有没有断过电。如何检查? 恩,RTC的示例代码中已经明示: 往备份域寄存器中写一个特殊的字符,备份域寄存器是和RTC一起在断电下能保存数据的。 上电后检查下这个特殊字符是否还存在,如果存在,ok,RTC的数据应该也没丢,不需要重新配置它 如果那个特殊字符丢了,那RTC的定时器数据一定也丢了,那我们要重新来配置RTC了 这个过程包括时钟使能、RTC时钟源切换、设置分频系数等等,这个可以参考
[单片机]
stm32之电源管理(实现低功耗)
前言:STM32F10xxx系列产品都有电源管理模块,芯片功耗会影响到一个产品的续航能力;比如在一些终端传感器场合里,为了减轻后期的维护投入,要求长期工作时间较长,更需要合理的芯片功耗管理。芯片自带几种运行模式,包括正常模式、睡眠模式、停止模式、待机模式。越往后,芯片的功耗越低,但能执行功能就越少。低功耗的电源管理策略就是在芯片不需要对外界响应的时候进入低功耗模式,而当外界条件满足的时候,退出低功耗模式(唤醒),正常执行处理工作。下面对其模式之间的转换和各个模式下的芯片内部的运转情况等一探究竟。 写代码前要先了解芯片的特性及工作原理,难免会先阅读一些长长的文档。下面核心讲解一些要点。 1.硬件原理 下图是芯片的电源框架:
[单片机]
<font color='red'>stm32</font>之电源管理(实现低功耗)
关于STM32的滴答滴答
void SysTick_Configuration(void) { if (SysTick_Config((SystemCoreClock) / 10)) // 1/10s=100ms { while (1); } NVIC_SetPriority(SysTick_IRQn, 0x0); } SysTick_Config(SystemFrequency / 10) 函数的形参就是systick重装定时器的值
[单片机]
STM32学习笔记之ADC转换
1. ADC简介 stm32f103最少有2个AD模数转换器,每个ADC都有18个通道,可以测量16个外部和2个内部模拟量。最大转换频率为1Mhz,也就是转换时间为1us(在 ADCCLK = 14Mhz,采样周期为1.5个时钟周期时)。最大时钟超过14Mhz,将导致ADC转换准确度降低。stm32的ADC是12位精度的。 stm32的ADC转换有两种通道,规则通道和注入通道,注入通道可以抢占式地打断规则通道的采样,执行注入通道采样后,再执行之前的规则通道采样,和中断类似。本例只使用规则通道实现独立模式的中断采样,这里不再赘述两种通道区别。 stm32的ADC可以由外部事件触发(例如定时器捕获,EXTI线)和软件触发(即在配置相关
[单片机]
<font color='red'>STM32</font>学习笔记之ADC转换
关于STM32存储的堆栈地址
由c/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。-程序结束后有系统释放 4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放 5、程序代码区—存放函数体的二进制代码。 STM32的地址
[单片机]
关于<font color='red'>STM32</font>存储的堆栈地址
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved