STM32—DMA直接存储器访问详解

DMA——直接存储器访问

DMA:Data Memory Access, 直接存储器访问。

DMA和我们之前学过的串口、GPIO都是类似的,都是STM32中的一个外设。串口是用来发送通信数据的,而DMA则是用来把数据从一个地方搬到另一个地方,而且不占用CPU。

举个例子:

我们如果要把一串数据发送给串口 ,CPU先要把这一串数据先一个一个取回来暂存在CPU中的寄存器中,然后再一个一个发送给串口。

这样就会导致CPU不能做其他事情,CPU一直处于被占用的状态。

当DMA出现后,CPU只需要给DMA发送一条命令,如将数据发送给串口,然后DMA就来完成这个上述需要CPU完成的工作了。这就节省了CPU的资源来完成其他操作。


上面我们解释完DMA是什么后,我们接着来看DMA具体有几种方式。

1.P->M(外设到存储器)

后面的ADC数据采集使用的是这一类

2.M->P(存储器到外设)

之前的串口发送实验属于这一类

3.M->M(存储器到存储器)

存储器到存储器的实验一会儿在后面讲

我们先来看一下DMA的功能框图

DMA功能框图讲解

请添加图片描述

我们先把DMA的功能框图分为三大主要部分来讲解:1.DMA请求。2.通道。3.仲裁器

DMA请求

通过上面的DMA框图我们可以看到,DMA请求通常是由外设发起的,例如串口、GPIO、ADC等等。

那么具体的外设是如何发送DMA请求的呢?

这里就要先提一下通道这个概念,不同的外设通过具体的通道向DMA控制器发送请求,然后DMA控制器再根据通道的优先权来处理请求。

DMA有DMA1和DMA2两个控制器,DMA1有7个通道,DMA2有5个通道,不同的DMA控制器的通道对应这不同的外设请求。

请添加图片描述

从上图可以看出,芯片的参考手册已经帮我们把不同的外设分别对应的通道分配好了,我们使用的时候只需要去查询这个表就行了。

仲裁器

虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能接收多个。

当多个通道同时向DMA控制器发送DMA请求的时候,谁可以先使用DMA控制器呢?这时就需要仲裁器来判断通道的优先级了。

仲裁器管理DMA通道请求分为两个阶段。

第一阶段属于软件阶段,可以在DMA_CCRx寄存器中设置,有4个等级:非常高四个优先级。见下图

请添加图片描述

第二阶段属于硬件阶段, 如果两个或以上的DMA通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高,比如通道0高于通道1。

DMA数据配置

使用DMA,最核心的就是配置要传输的数据,包括数据从哪里来要到那里去传输数据的单位是什么要传多少数据是一次传输还是循环传输等等

从哪里来到那里去?

我们知道DMA传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储器到存储器。 具体的传输方向DMA_CCR位4 DIR配置:0表示从外设到存储器,1表示从存储器到外设。 这里面涉及到的外设地址由DMA_CPAR配置,存储器地址由DMA_CMAR配置。

我们在使用固件库编程的时候,通常不会直接配置这三个寄存器,都是通过初始化结构体来配置的,这三个寄存器对应的结构体变量如下图:

请添加图片描述

配置内容对应寄存器
外设地址DMA_CPAR
存储器地址DMA_CMAR
传输方向DMA_CCR:DIR
外设到存储器

当我们使用从外设到存储器传输时,以ADC采集为例。DMA外设寄存器的地址对应的就是ADC数据寄存器的地址, DMA存储器的地址就是我们自定义的变量(用来接收存储AD采集的数据)的地址。方向我们设置外设为源地址。

存储器到外设

当我们使用从存储器到外设传输时,以串口向电脑端发送数据为例。DMA外设寄存器的地址对应的就是串口数据寄存器的地址, DMA存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储通过串口发送到电脑的数据)的地址。方向我们设置外设为目标地址。

存储器到存储器

当我们使用从存储器到存储器传输时,以内部FLASH向内部SRAM复制数据为例。 DMA外设寄存器的地址对应的就是内部FLASH(我们这里把内部FALSH当作一个外设来看)的地址, DMA存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部FLASH的数据)的地址。 方向我们设置外设(即内部FLASH)为源地址。跟上面两个不一样的是,这里需要把DMA_CCR位14:MEM2MEM:存储器到存储器模式配置为1,启动M2M模式。

数据要传多少,传的单位是什么?

请添加图片描述

配置内容对应寄存器
传输数目DMA_CNDTR
外设地址是否递增DMA_CCRx:PINC
存储器地址是否递增DMA_CCRx:MINC
外设数据宽度DMA_CCRx:PSIZE
存储器数据宽度DMA_CCRx:MSIZE

当我们配置好数据要从哪里来到哪里去之后,我们还需要知道我们要传输的数据是多少,数据的单位是什么。

以串口向电脑发送数据为例,我们可以一次性给电脑发送很多数据,具体多少由DMA_CNDTR配置, 这是一个32位的寄存器,一次最多只能传输65535个数据。

要想数据传输正确,源和目标地址存储的数据宽度还必须一致

串口数据寄存器是8位的, 所以我们定义的要发送的数据也必须是8位。

外设的数据宽度由**DMA_CCR的PSIZE[1:0]**配置, 可以是8/16/32位,

存储器的数据宽度由**DMA_CCR的MSIZE[1:0]**配置,可以是8/16/32位。

那么当PSIZE和MSIZE不相同时,也能传输,但是可能传输结果与你的预期不同,我们可以参照下表来查询:

请添加图片描述

在DMA控制器的控制下,数据要想有条不紊的从一个地方搬到另外一个地方,还必须正确设置两边数据指针的增量模式。

外设的地址指针由DMA_CCRx的PINC配置,存储器的地址指针由MINC配置。以串口向电脑发送数据为例,要发送的数据很多, 每发送完一个,那么存储器的地址指针就应该加1,而串口数据寄存器只有一个, 那么外设的地址指针就固定不变。具体的数据指针的增量模式由实际情况决定。

什么时候传输完成?

请添加图片描述

配置内容对应寄存器
模式选择(一次传输、循环传输)DMA_CCRx:CIRC
传输过半、传输完成、传输出错标志位DMA_ISR

数据什么时候传输完成,我们可以通过查询标志位或者通过中断的方式来鉴别。

每个DMA通道在DMA传输过半传输完成传输错误时都会有相应的标志位,如果使能了该类型的中断后,则会产生中断。

有关各个标志位的详细描述请参考DMA中断状态寄存器DMA_ISR的详细描述。

传输完成还分两种模式,是一次传输还是循环传输

一次传输很好理解,即是传输一次之后就停止,

要想再传输的话, 必须关断DMA使能后再重新配置后才能继续传输。

循环传输则是一次传输完成之后又恢复第一次传输时的配置循环传输, 不断的重复。

具体的由DMA_CCR寄存器的CIRC 循环模式位控制。

以上就是所有关于DMA的理论部分,下面我来进行程序编写。我先设计一个程序将存储器中的数据通过DMA的方式发送给串口。

我依然按照我上面讲DMA数据配置的顺序来写程序。

DMA(存储器到外设)实现代码

首先创建一个数组作为数据源,一会儿发送给串口

//准备将存储器中的这个数组中的数据发送到串口
u32 SourceBuffer[500];

然后开始配置DMA,上面我们讲DMA数据配置的时候已经说过了,我们配置DMA的时候是通过向DMA初始化结构体中填数据来配置的,所以我们先定义一个DMA初始化结构体,然后打开DMA外设的时钟信号。

//定义DMA初始化结构体
DMA_InitTypeDef DMA_initStruct;

//首先查询DMA挂载在哪根总线上,然后打开DMA时钟信号
RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1EN,ENABLE);

数据从哪里来,到哪里去?

配置外设基地址

存储器基地址

数据传输方向

//通过上一篇文章对串口(USART)介绍的文章,可以找到串口数据发送的寄存器是放在USART_DR中的
DMA_initStruct.DMA_PeripheralBaseAddr = USART1_BASE + 0x04;

//配置存储器的基地址,也就是数组名
DMA_initStruct.DMA_MemoryBaseAddr = (u32)SourceBuffer;

//配置DMA数据传输方向,从存储器到外设,所以是外设Destination
DMA_initStruct.DMA_DIR = DMA_DIR_PeripheralDST;

数据要传多少,传的单位是什么?

我就不赘述了,请看代码中的注释

//配置发送的数据大小为500,因为数组大小为500
DMA_initStruct.DMA_BufferSize = 500;

//外设地址不用自增,因为串口的发送寄存器只有一个字节这么大
DMA_initStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

//配置外设的数据宽度为字节
DMA_initStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

//由于数组是按顺序发送,所以存储器的地址可以自增
DMA_initStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;

//由于数组中的数据类型为u32,所以存储器的数据宽度配置为字
DMA_initStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;


什么时候传输完成?

//发送模式配置为发送一次,建议不要配置成循环发送,因为我的查看串口发送信息的工具会卡死
DMA_initStruct.DMA_Mode = DMA_Mode_Normal;

//优先级随便配置,没人跟你抢
DMA_initStruct.DMA_Priority = DMA_Priority_High;

//存储器到存储器的单独配置位,这里我们是存储器到外设,所以这个位为disable
DMA_initStruct.DMA_M2M = DMA_M2M_Disable;

上面配置好后,才算完成DMA初始化结构体的配置,接下来调用初始化函数。并给通道使能信号

//调用初始化函数
DMA_Init(DMA1_Channel4,&DMA_initStruct);

//DMA通道使能信号
DMA_Cmd(DMA1_Channel4,ENABLE);

DMA配置好了,下面来写main函数,注意,串口也是需要配置的。但是由于我在上一篇文章里已经详细讲述过串口的配置方法,在这里我就直接调用串口的配置函数了。

int main(){
	u16 i;
    //先用一个for循环向数组中填数据,一会儿这个数据会通过串口发送出去
	for(i=0; i<500; i++){
		SourceBuffer[i]=14;
	}
    
    //调用串口配置函数
	USART_GPIO_Config();
	USART_Config();
    
	//调用DMA配置函数
	DMA_Config();
    
    //串口1向DMA发出TX请求
	USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
	
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/539349.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

windows本地运行dreamtalk踩坑总结

dreamtalk是一个语音图片转视频的一个工具&#xff0c;就是给一段语音加一个头像图片&#xff0c;然后生成一段头像跟语音对口型的视频&#xff0c;其实还是很有意思的&#xff0c;最近阿里发布了一个类似的模型&#xff0c;但是还没开源&#xff0c;从展示视频看&#xff0c;阿…

酷开科技OTT大屏营销:开启新时代的营销革命

随着互联网技术的不断发展和普及&#xff0c;大屏已经成为越来越多家庭选择的娱乐方式。在这个背景下&#xff0c;酷开科技凭借其强大的技术实力和敏锐的市场洞察力&#xff0c;成功地将大屏转化为一种新的营销渠道&#xff0c;为品牌和企业带来了前所未有的商业机会。 酷开科技…

WEB3.0:互联网的下一阶段

随着互联网的发展&#xff0c;WEB3.0时代正在逐步到来。本文将深入探讨WEB3.0的定义、特点、技术应用以及未来展望&#xff0c;为读者带来全新的思考。 一、什么是WEB3.0&#xff1f; WEB3.0可以被理解为互联网发展的下一阶段&#xff0c;是当前WEB2.0的升级版。相较于2.0时代…

CentOS 各个版本下载地址

https://mirror.nsc.liu.se/centos-store/7.6.1810/isos/x86_64/ CentOS-7-x86_64-DVD-1810.iso 2018-Nov-26 00:55:20 4.2G application/octet-stream 正常版 CentOS-7-x86_64-DVD-1810.torrent 2018-Dec-03 16:03:27 85.9K application/x-bittorrent CentOS-7-x86_64-Every…

用户系统加密--Java

一个基本的用户系统如下&#xff1a; # 定义用户类 class User:def __init__(self, name, password):self.name nameself.password password# 创建用户列表 users []# 添加新用户 def add_user(name, password):new_user User(name, password)users.append(new_user)print…

洛谷-P1596 [USACO10OCT] Lake Counting S

P1596 [USACO10OCT] Lake Counting S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include<bits/stdc.h> using namespace std; const int N110; int m,n; char g[N][N]; bool st[N][N]; //走/没走 int dx[] {-1,-1,-1,0,0,1,1,1}; //八联通 int dy[] {-1,0,1,1,-1,1…

LDF、DBC、BIN、HEX、S19、BLF、ARXML、slx等

文章目录 如题 如题 LDF是LIN报文格式文件&#xff0c;把这个直接拖到软件里面&#xff0c;可以发报文和接收报文 DBC是CAN报文格式文件&#xff0c;把这个直接拖到软件里面&#xff0c;可以发报文和接收报文 BIN文件烧录在BOOT里面&#xff08;stm32&#xff09;&#xff0c…

【前端】解决前端图表大数据配色难题:利用HSL动态生成颜色方案

解决前端图表大数据配色难题&#xff1a;利用HSL动态生成颜色方案 在数据可视化项目中&#xff0c;尤其是当需要绘制包含大量数据点的图表时&#xff0c;一个常见的挑战是如何为每个数据点分配一个独特而又视觉上容易区分的颜色。使用固定的颜色列表可能在数据点数量超过列表限…

CTF-SHOW SSRF

web351 存在一个flag.php页面&#xff0c;访问会返回不是本地用户的消息&#xff0c;那肯定是要让我们以本地用户去访问127.0.0.1/flag.php web352 代码中先判断是否为HTTP或https协议&#xff0c;之后判断我们传入的url中是否含有localhost和127.0.0&#xff0c;如果没有则…

课时93:流程控制_函数进阶_综合练习

1.1.3 综合练习 学习目标 这一节&#xff0c;我们从 案例解读、脚本实践、小结 三个方面来学习。 案例解读 案例需求 使用shell脚本绘制一个杨辉三角案例解读 1、每行数字左右对称&#xff0c;从1开始变大&#xff0c;然后变小为1。    2、第n行的数字个数为n个&#xf…

Golang | Leetcode Golang题解之第24题两两交换链表中的节点

题目&#xff1a; 题解&#xff1a; func swapPairs(head *ListNode) *ListNode {dummyHead : &ListNode{0, head}temp : dummyHeadfor temp.Next ! nil && temp.Next.Next ! nil {node1 : temp.Nextnode2 : temp.Next.Nexttemp.Next node2node1.Next node2.Nex…

项目实训2024.04.12日志:Self-QA生成问答对

1. Self-QA技术 1.1. 为什么要用Self-QA技术 关于为什么要搜集问答对&#xff0c;我在创新实训2024.04.07日志&#xff1a;提取QA对这篇文章中提到过&#xff1a;训练大模型需要从业务侧积累的问题、资料、文档中提取出一些指令-问答对作为输入的语料。 之前我们对于问答对的…

ChatGPT加持,需求分析再无难题

简介 在实际工作过程中&#xff0c;常常需要拿到产品的PRD文档或者原型图进行需求分析&#xff0c;为产品的功能设计和优化提供建议。 而使用ChatGPT可以很好的帮助分析和整理用户需求。 实践演练 接下来&#xff0c;需要使用ChatGPT 辅助我们完成需求分析的任务 注意&…

【编程】C++ 常用容器以及一些应用案例

介绍一些我常用的C容器和使用方法&#xff0c;以及使用案例。blog 1 概述 容器&#xff08;Container&#xff09;是一个存储其他对象集合的持有者对象。容器以类模板实现&#xff0c;对支持的元素类型有很大的灵活性。容器管理元素的存储并提供多个成员函数来访问和操作元素…

鸿蒙OS开发学习:【第三方库调用】

介绍 本篇Codelab主要向开发者展示了在Stage模型中&#xff0c;如何调用已经上架到[三方库中心]的社区库和项目内创建的本地库。效果图如下&#xff1a; 相关概念 [Navigation]&#xff1a;一般作为Page页面的根容器&#xff0c;通过属性设置来展示页面的标题、工具栏、菜单。…

OpenHarmony编译构建系统

这篇来聊聊OpenHarmony的编译构建&#xff0c;经过前面的实践&#xff0c;再来看编译构建。 编译构建概述 在官网中提到了&#xff0c;OpenHarmony编译子系统是以GN和Ninja构建为基座&#xff0c;对构建和配置粒度进行部件化抽象、对内建模块进行功能增强、对业务模块进行功能…

二叉树-数据结构

二叉树-数据结构 二叉树是属性结构的一个重要类型。 如下图二叉树形状 二叉树特征如下&#xff1a; 1.二叉树由 n(n > 0) 个节点组成 2.如果 n 为 0&#xff0c;则为空树 3.如果 n 1&#xff0c;则只有一个节点称为根节点(root) 4.每个节点最多有两个节点&#xff0c;节…

【题目】【信息安全管理与评估】2022年国赛高职组“信息安全管理与评估”赛项样题2

【题目】【信息安全管理与评估】2022年国赛高职组“信息安全管理与评估”赛项样题2 信息安全管理与评估 网络系统管理 网络搭建与应用 云计算 软件测试 移动应用开发 任务书&#xff0c;赛题&#xff0c;解析等资料&#xff0c;知识点培训服务 添加博主wx&#xff1a;liuliu548…

【b站李同学的Lee】3 Github【gitgithub】入门教程,必学!

课程地址&#xff1a;【【git&github】入门教程&#xff0c;必学&#xff01;】 https://www.bilibili.com/video/BV1cE411G7yc/?share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 目录 3 Github 3.1 注册 3.2 多人协作开发流程 3.2.1 远程仓库 …

MYSQL5.7详细安装步骤

MYSQL5.7详细安装步骤&#xff1a; 0、更换yum源 1、打开 mirrors.aliyun.com&#xff0c;选择centos的系统&#xff0c;点击帮助 2、执行命令&#xff1a;yum install wget -y 3、改变某些文件的名称 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base…