STM32学习笔记——DMA控制器(向原子哥学习)
一、DMA简介
DMA,全称为:Direct Memory Access,即直接存储器访问,DMA 用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当 CPU 初始化这个传输动作,传输动作本身是由DMA 控制器 来实行和完成。DMA 传输对于高效能嵌入式系统算法和网络是很重要的。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。STM32 最多有 2 个 DMA 控制器(DMA2 仅存在大容量产品中),DMA1 有 7 个通道,DMA2 有 5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个 DMA 请求的优先权。
从外设(TIMx、ADC、SPIx、I2Cx 和 USARTx)产生的 DMA 请求,通过逻辑或输入到DMA 控制器,这就意味着同时只能有一个请求有效。外设的 DMA 请求,可以通过设置相应的外设寄存器中的控制位,被独立地开启或关闭。
表1 DMA各通道一览表
逻辑或是指通道 1 的几个 DMA1 请求(ADC1、 TIM2_CH3、 TIM4_CH1),这几个是通过逻辑或到通道 1 的,这样我们在同一时间,就只能使用其中的一个。其他通道也是类似的。
二、DMA寄存器
注意: 在以下列举的所有寄存器中,所有与通道6和通道7相关的位,对DMA2都不适用,因为DMA2只有5个通道。
1、DMA 中断状态寄存器(DMA_ISR)
如果开启了 DMA_ISR 中这些中断,在达到条件后就会跳到中断服务函数里面去,如果没开启,我们也可以通过查询这些位来获得当前 DMA 传输的状态。这里常用的是TCIFx,即通道 DMA 传输完成与否的标志。注意此寄存器为只读寄存器,所以在这些位被置位之后,只能通过其他的操作来清除。
2、DMA 中断标志清除寄存器(DMA_IFCR)。
图2 DMA_IFCR 寄存器
DMA_IFCR 的各位就是用来清除 DMA_ISR 的对应位的,通过写 0 清除。在 DMA_ISR 被置位后,我们必须通过向该位寄存器对应的位写入 0 来清除。
三、库函数下 DMA1 通道 4 的配置步骤:
1、使能 DMA 时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟
2、初始化 DMA 通道 4 参数
DMA 通道配置参数种类比较繁多,包括内存地址,外设地址,传输数据长度,数据宽度,通道优先级等等。这些参数的配置在库函数中都是在函数 DMA_Init 中完成,下面我们看看函数定义:
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx,DMA_InitTypeDef* DMA_InitStruct)
函数的第一个参数是指定初始化的 DMA 通道号,主要看看第二个参数。跟其他外设一样,同样是通过初始化结构体成员变量值来达到初始化的目的,下面看看 DMA_InitTypeDef 结构体的定义:
typedef struct
{
uint32_t DMA_PeripheralBaseAddr;
uint32_t DMA_MemoryBaseAddr;
uint32_t DMA_DIR;
uint32_t DMA_BufferSize;
uint32_t DMA_PeripheralInc;
uint32_t DMA_MemoryInc;
uint32_t DMA_PeripheralDataSize;
uint32_t DMA_MemoryDataSize;
uint32_t DMA_Mode;
uint32_t DMA_Priority;
uint32_t DMA_M2M;
}DMA_InitTypeDef;
这个结构体的成员比较多,但是每个成员变量的意义在前面基本都已经提到过,再做个简要的介绍。
第一个参数 DMA_PeripheralBaseAddr 用来设置 DMA 传输的外设基地址,比如要进行串口DMA 传输,那么外设基地址为串口接受发送数据存储器 USART1->DR 的地址,表示方法为&USART1->DR。
第二个参数 DMA_MemoryBaseAddr 为内存基地址,也就是我们存放 DMA 传输数据的内存地址。
第三个参数 DMA_DIR 设置数据传输方向,决定是从外设读取数据到内存还送从内存读取数据发送到外设,也就是外设是源地还是目的地,这里我们设置为从内存读取数据发送到串口,所以外设自然就是目的地了,所以选择值为 DMA_DIR_PeripheralDST。
第四个参数 DMA_BufferSize 设置一次传输数据量的大小,这个很容易理解。
第五个参数 DMA_PeripheralInc 设置传输数据的时候外设地址是不变还是递增。如果设置为递增,那么下一次传输的时候地址加 1,这里因为我们是一直往固定外设地址&USART1->DR发送数据,所以地址不递增,值为 DMA_PeripheralInc_Disable;
第六个参数 DMA_MemoryInc 设置传输数据时候内存地址是否递增。 这个参数和DMA_PeripheralInc 意思接近,只不过针对的是内存。这里我们的场景是将内存中连续存储单元的数据发送到串口,毫无疑问内存地址是需要递增的,所以值为 DMA_MemoryInc_Enable。
第七个参数 DMA_PeripheralDataSize 用来设置外设的数据长度是为字节传输(8bits) ,半字传输 (16bits) 还是字传输 (32bits) ,这里我们是 8 位字节传输,所以 值设置为DMA_PeripheralDataSize_Byte。
第八个参数 DMA_MemoryDataSize 是用来设置内存的数据长度,和第七个参数意思接近,这里我们同样设置为字节传输 DMA_MemoryDataSize_Byte。
第九个参数 DMA_Mode 用来设置 DMA 模式是否循环采集,也就是说,比如我们要从内存中采集 64 个字节发送到串口,如果设置为重复采集,那么它会在 64 个字节采集完成之后继续从内存的第一个地址采集,如此循环。这里我们设置为一次连续采集完成之后不循环。所以设置值为 DMA_Mode_Normal。在我们下面的实验中,如果设置此参数为循环采集,那么你会看到串口不停的打印数据,不会中断,大家在实验中可以修改这个参数测试一下。
第十个参数是设置 DMA 通道的优先级,有低,中,高,超高三种模式,这个在前面讲解过,这里我们设置优先级别为中级,所以值为 DMA_Priority_Medium。如果要开启多个通道,那么这个值就非常有意义。
第十一个参数 DMA_M2M 设置是否是存储器到存储器模式传输,这里我们选择DMA_M2M_Disable。
这里给出上面场景的实例代码:
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = &USART1->DR; //DMA 外设 ADC 基地址
DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA 内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //从内存读取发送到外设
DMA_InitStructure.DMA_BufferSize = 64; //DMA 通道的 DMA 缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //8 位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8 位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA 通道 x 拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存传输
DMA_Init(DMA_CHx, &DMA_InitStructure); //根据指定的参数初始化
3、使能串口 DMA 发送
进行 DMA 配置之后,我们就要开启串口的 DMA 发送功能,使用的函数是:
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
如果是要使能串口 DMA 接受,那么第二个参数修改为 USART_DMAReq_Rx 即可。
4、使能 DMA1 通道 4,启动传输。
使能串口 DMA 发送之后,我们接着就要使能 DMA 传输通道:
DMA_Cmd(DMA_CHx, ENABLE);
通过以上 3 步设置,我们就可以启动一次 USART1 的 DMA 传输了。
5、查询 DMA 传输状态
在 DMA 传输过程中,我们要查询 DMA 传输通道的状态,使用的函数是:
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
比如我们要查询 DMA 通道 4 传输是否完成,方法是:
DMA_GetFlagStatus(DMA2_FLAG_TC4);
这里还有一个比较重要的函数就是获取当前剩余数据量大小的函数:
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)
比如我们要获取 DMA 通道 4 还有多少个数据没有传输,方法是:
DMA_GetCurrDataCounter(DMA1_Channel4);
DMA 相关的库函数更详细的资料可以查看固件库中文手册。
STM32学习笔记——DMA控制器(向原子哥学习)的更多相关文章
- Hadoop源码学习笔记(3) ——初览DataNode及学习线程
Hadoop源码学习笔记(3) ——初览DataNode及学习线程 进入了main函数,我们走出了第一步,接下来看看再怎么走: public class DataNode extends Config ...
- STM32F103X datasheet学习笔记---DMA
1.前言 直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输. 无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作. 两个DMA控制器 ...
- 学习笔记︱Nvidia DIGITS网页版深度学习框架——深度学习版SPSS
DIGITS: Deep Learning GPU Training System1,是由英伟达(NVIDIA)公司开发的第一个交互式深度学习GPU训练系统.目的在于整合现有的Deep Learnin ...
- STM32学习笔记——定时器中断(向原子哥学习)
定时器中断 STM32 的定时器功能十分强大,有 TIME1 和 TIME8 等高级定时器,也有 TIME2~TIME5 等通用定时器,还有 TIME6 和TIME7 等基本定时器.在本章中,我们将利 ...
- STM32学习笔记——SPI串行通讯(向原子哥学习)
一.SPI 简介 SPI是 Serial Peripheral interface 的缩写,就是串行外围设备接口.SPI 接口主要应用在 EEPROM, FLASH,实时时钟,AD 转换器,还有数 ...
- STM32学习笔记——新建工程模板步骤(向原子哥学习)
1. 在创建工程之前,先在电脑的某个目录下面建立一个文件夹,我们先把它命名为Template,后面建立的工程可以放在这个文件夹下.在 Template 工程目录下面,新建 3 个文件夹USER , ...
- Swoft2.x 小白学习笔记 (一) ---控制器
Swoft通过官方文档进行学习,这里不做介绍,直接上手. 涉及到Swoft方面:(配置.注意的坑) 1.控制器(路由.验证器.中间件) 2.mysql (Model使用).Redis配置及通用池 3 ...
- ThinkPHP 学习笔记 ( 二 ) 控制器 ( Controller )
/** * ThinkPHP version 3.1.3 * 部署方式:应用部署 * 文内的 http://localhost/ 由实际主机地址代替 */ 入口文件 index.php: <?p ...
- IOS 学习笔记 2015-04-15 控制器数据反向传值
// // FirstViewController.h // 控制器数据传递 // // Created by wangtouwang on 15/4/15. // Copyright (c) 201 ...
随机推荐
- Wall - POJ 1113(求凸包)
题目大意:给N个点,然后要修建一个围墙把所有的点都包裹起来,但是要求围墙距离所有的点的最小距离是L,求出来围墙的长度. 分析:如果没有最小距离这个条件那么很容易看出来是一个凸包,然后在加上一个最小距离 ...
- Node.js学习(10)----文件系统fs
fs 模块是文件操作的封装,它提供了文件的读取.写入.更名.删除.遍历目录.链接等 POSIX 文件系统操作.与其他模块不同的是,fs 模块中所有的操作都提供了异步的和同步的两个版本, 例如读取文件内 ...
- maven 私服 配置 转
3 . Nexus预置的仓库 点击左侧 Repositories 链接,查看 Nexus 内置的仓库: Nexus 的仓库分为这么几类: hosted 宿主仓库:主要用于部署无法从公共仓库获取的构件( ...
- 【设计模式 - 15】之解释器模式(Interpreter)
1 模式简介 解释器模式允许我们自定义一种语言,并定义一个这种语言的解释器,这个解释器用来解释语言中的句子.由于这种模式主要用于编译器的编写,因此在日常应用中不是很常用. 如果一种特定类型的 ...
- 圣诞节来了,雪花纷飞的CSS3动画
原文链接:http://www.html5think.com/article/index/id/80
- jQuery 实现上下,左右滑动
前几天的任务:http://t.sina.com.cn/ 的下滑效果. 渐变移动出足够的空白 -> 淡出最后一个 ->渐变移动出足够的空白 我们要做的是向左移动效果.这个效果用时需添加一 ...
- 如何将ER图转换成关系模式集
在ER图中,主要是实体类型和联系类型. 1.实体类型的转换 (“——”表示对应关系) 实体类型——关系模式 实体的属性——关系模式的属性 实体标识符——关系模式的键 2.联系的转换 一元联系较简单,三 ...
- The Boost C++ Libraries
" ...one of the most highly regarded and expertly designed C++ library projects in the world.&q ...
- objective-c 中的关联介绍
objective-c 中的关联介绍 转载请注明CSDN博客上的出处: http://blog.csdn.net/daiyibo123/article/details/46471993 如何设置关联 ...
- 在Linux下用netstat查看网络状态、端口状态
在Linux下用netstat查看网络状态.端口状态 在linux一般使用netstat 来查看系统端口使用情况步. netstat命令是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表.实 ...