DMA的原理

一、介绍

DMA(Direct Memory Access)是一种允许设备直接与内存进行数据交换的技术,无需‌CPU干预。DMA的主要功能是提供在‌外设和存储器之间或者存储器和存储器之间的高速数据传输。比如使用ADC进行数据采集,可以直接将数据存入存储器中,而无须CPU干预,节省了CPU的资源

二、基本结构与原理

工作原理

  1. 初始化阶段‌:当系统启动时,DMA控制器会初始化并配置好相关寄存器,这些寄存器用于定义DMA传输的数据量、传输方向、传输速率等参数。
  2. 请求传输‌:外设准备好数据后,会向DMA控制器发送一个传输请求。
  3. CPU响应请求‌:CPU收到DMA请求信号后,会让出总线控制权,允许DMA控制器接管总线控制权。
  4. 数据传输‌:DMA控制器从外设读取数据,并将其存储在内部缓冲区中,然后传输到系统内存中的目标地址。
  5. 传输完成‌:数据传输完成后,DMA控制器会释放总线控制权,并通知CPU。

下图为stm32中DMA的结构,两个DMA控制器有12个通道(DMA1有7个通道, DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

上方的框图,我们可以看到STM32内核,存储器,外设及DMA的连接,这些硬件最终通过各种各样的线连接到总线矩阵中,硬件结构之间的数据转移都经过总线矩阵的协调,使各个外设和谐的使用总线来传输数据,

三、DMA传输方式

DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:

  • 外设到内存
  • 内存到外设
  • 内存到内存
  • 外设到外设

数据传输阶段:

  • 源地址(Source Address)‌:指定数据传输的起始位置。
  • 目标地址(Destination Address)‌:指定数据传输的目的位置。
  • 数据长度(Data Length)‌:定义需要传输的数据大小。
  • 控制信息(Control Information)‌:包括传输模式、中断使能等参数。

数据传输阶段‌:

  • 外设发起传输请求。
  • DMA控制器暂停CPU访问,并通过请求信号获取总线控制权。
  • DMA控制器从外设读取数据,并存储在内部缓冲区。
  • 数据从内部缓冲区传输到系统内存中的目标地址。
  • 传输完成后,DMA控制器释放总线控制权,并通知CPU。

当用户将参数设置好,主要涉及源地址、目标地址、传输数据量这三个,DMA控制器就会启动数据传输,当剩余传输数据量为0时, 达到传输终点,结束DMA传输 。当然,DMA 还有循环传输模式,当到达传输终点时会重新启动DMA传输。 也就是说只要剩余传输数据量不是0,而且DMA是启动状态,那么就会发生数据传输。

当CPU和DMA同时访问相同的目标(RAM或外设)时, DMA请求会暂停CPU访问系统总线达若干个周期,总线仲裁器执行循环调度,以保证CPU至少可以得到一半的系统总线(存储器或外设)带宽
 

四、源码实例

实现从外设的数据到存储器的转运,并显示在LED显示屏上

#include "stm32f10x.h"                  // Device headeruint16_t MyDMA_Size;					//定义全局变量,用于记住Init函数的Size,供Transfer函数使用/*** 函    数:DMA初始化* 参    数:AddrA 原数组的首地址* 参    数:AddrB 目的数组的首地址* 参    数:Size 转运的数据大小(转运次数)* 返 回 值:无*/
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{MyDMA_Size = Size;					//将Size写入到全局变量,记住参数Size/*开启时钟*/RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);						//开启DMA的时钟/*DMA初始化*/DMA_InitTypeDef DMA_InitStructure;										//定义结构体变量DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;						//外设基地址,给定形参AddrADMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据宽度,选择字节DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;			//外设地址自增,选择使能DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;							//存储器基地址,给定形参AddrBDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据宽度,选择字节DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器地址自增,选择使能DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;						//数据传输方向,选择由外设到存储器DMA_InitStructure.DMA_BufferSize = Size;								//转运的数据大小(转运次数)DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//模式,选择正常模式DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;								//存储器到存储器,选择使能DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//优先级,选择中等DMA_Init(DMA1_Channel1, &DMA_InitStructure);							//将结构体变量交给DMA_Init,配置DMA1的通道1/*DMA使能*/DMA_Cmd(DMA1_Channel1, DISABLE);	//这里先不给使能,初始化后不会立刻工作,等后续调用Transfer后,再开始
}/*** 函    数:启动DMA数据转运* 参    数:无* 返 回 值:无*/
void MyDMA_Transfer(void)
{DMA_Cmd(DMA1_Channel1, DISABLE);					//DMA失能,在写入传输计数器之前,需要DMA暂停工作DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);	//写入传输计数器,指定将要转运的次数DMA_Cmd(DMA1_Channel1, ENABLE);						//DMA使能,开始工作while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);	//等待DMA工作完成DMA_ClearFlag(DMA1_FLAG_TC1);						//清除工作完成标志位
}
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};				//定义测试数组DataA,为数据源
uint8_t DataB[] = {0, 0, 0, 0};							//定义测试数组DataB,为数据目的地int main(void)
{/*模块初始化*/OLED_Init();				//OLED初始化MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);	//DMA初始化,把源数组和目的数组的地址传入/*显示静态字符串*/OLED_ShowString(1, 1, "DataA");OLED_ShowString(3, 1, "DataB");/*显示数组的首地址*/OLED_ShowHexNum(1, 8, (uint32_t)DataA, 8);OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);while (1){DataA[0] ++;		//变换测试数据DataA[1] ++;DataA[2] ++;DataA[3] ++;OLED_ShowHexNum(2, 1, DataA[0], 2);		//显示数组DataAOLED_ShowHexNum(2, 4, DataA[1], 2);OLED_ShowHexNum(2, 7, DataA[2], 2);OLED_ShowHexNum(2, 10, DataA[3], 2);OLED_ShowHexNum(4, 1, DataB[0], 2);		//显示数组DataBOLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2);Delay_ms(1000);		//延时1s,观察转运前的现象MyDMA_Transfer();	//使用DMA转运数组,从DataA转运到DataBOLED_ShowHexNum(2, 1, DataA[0], 2);		//显示数组DataAOLED_ShowHexNum(2, 4, DataA[1], 2);OLED_ShowHexNum(2, 7, DataA[2], 2);OLED_ShowHexNum(2, 10, DataA[3], 2);OLED_ShowHexNum(4, 1, DataB[0], 2);		//显示数组DataBOLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2);Delay_ms(1000);		//延时1s,观察转运后的现象}
}

五、总结

DMA传输方式虽然高效,但是,只是减轻了CPU的工作负担;系统总线仍然被占用。特别是在传输大容量文件时,CPU的占用率可能不到10%,但是用户会觉得运行部分程序时系统变得相当的缓慢。主要原因就是在运行这些应用程序(特别是一些大型软件),操作系统也需要从系统总线传输大量数据;故造成过长的等待时间。

参考:

关于DMA的介绍和在不同设备上的使用和介绍_qq64f6f10b54d8e的技术博客_51CTO博客

百度安全验证

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

干货 | 2024制造业数字化现状调查白皮书(免费下载)

导读:在这本白皮书中,我们询问了制造商有关数字化转型的工作情况、2024 年的优先事项和可持续性。研究结果清楚地表明,在数字化方面处于领先地位的制造商转型项目比那些没有规划或刚刚起步的项目实现的价值要大得多。 加入知识星球或关注下方…

运维转型大模型:全面指南与实战总结

运维心里苦谁做谁知道,有时候感觉自己像一个杂工,在公司都快变成一个修电脑的了,不装了我转行了,给大家分享一点经验,希望能帮助到你们。 运维工程师转行至大模型领域需要学习一系列新的技能和知识。以下是一个详细的…

【算法】JAVA刷算法必备数据结构

文章目录 数组List队列和栈栈的应用:表达式求值 数组List ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。 ArrayList 继承了 AbstractList ,并实现了 List 接口。 …

Nest.js实现一个简单的聊天室

本文将介绍如何使用 Nest.js 和 Uni-app 实现一个简单的实时聊天应用。后端使用 nestjs/websockets 和 socket.io,前端使用 uni-app 并集成 socket.io-client。这个项目允许多个用户同时加入聊天并实时交换消息。 效果图: 一、准备工作 安装 Node.js 和…

DAF-Net:一种基于域自适应的双分支特征分解融合网络用于红外和可见光图像融合

论文 DAF-Net: A Dual-Branch Feature Decomposition Fusion Network with Domain Adaptive for Infrared and Visible Image Fusion 提出了一种新的红外和可见光图像融合方法。该方法旨在结合红外图像和可见光图像的互补信息,以提供更全面的场景理解。红外图像在低…

学习C++的第七天!

1.虚函数是在基类中用 virtual 关键字声明的函数,可以在派生类中被重写。纯虚函数是在虚函数的基础上,在基类中被初始化为 0 的函数,含有纯虚函数的类是抽象类,不能被实例化。 2.如果基类的析构函数不是虚函数,当通过…

现代cpp多线程与并发初探

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 在现代c(c20)中,有了jthread和协程的概念,使得我们编写并发程序更加方便. 这里作简单学习. 前言知识 多线程编程 std::thread 用于创建一个执行的线程实例,所以它是一切并发编程的基础,使用时需要包含 <thread…

Android个性名片界面的设计——约束布局的应用

节选自《Android应用开发项目式教程》&#xff0c;机械工业出版社&#xff0c;2024年7月出版 做最简单的安卓入门教程&#xff0c;手把手视频、代码、答疑全配齐 【任务目标】 使用约束布局、TextView控件实现一个个性名片界面的设计&#xff0c;界面如图1所示。 图1 个性名片…

Transformer 算法模型详解

核心点&#xff1a;完整讲解Transformer模型&#xff01; 让我们用简单的语言来解释&#xff1a;想象一下&#xff0c;你正在阅读一本书&#xff0c;书中的每个字都很重要。但如果你每次只能关注一个字&#xff0c;理解整本书就会变得很慢。而Transformer模型就像是赋予你超能…

从密码学看盲拍合约:智能合约的隐私与安全新革命!

文章目录 前言一、什么是盲拍合约&#xff1f;二、盲拍合约的优势1.时间压力的缓解2.绑定与秘密的挑战 三、盲拍合约的工作原理1.提交盲出价2.披露出价3.结束拍卖4.退款机制 四、代码示例总结 前言 随着区块链技术的发展&#xff0c;智能合约在各种场景中的应用越来越广泛。盲…

基于Hive和Hadoop的病例分析系统

本项目是一个基于大数据技术的医疗病历分析系统&#xff0c;旨在为用户提供全面的病历信息和深入的医疗数据分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以 Spark…

Linux入门2——初识Linux权限

目录 0. Linux下的用户 1.文件访问者的分类 2.文件类型和访问权限 3. 文件权限值的表示方法 4.文件访问权限的相关设置方法 4.1 修改文件的访问权限 4.2修改文件的拥有者和所属组 0. Linux下的用户 在学习Linux权限之前&#xff0c;我们要先来了解Linux下的用户&#x…

vue+UEditor附件上传问题

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

端口隔离配置的实验

端口隔离配置是一种网络安全技术&#xff0c;用于在网络设备中实现不同端口之间的流量隔离和控制。以下是对端口隔离配置的详细解析&#xff1a; 基本概念&#xff1a;端口隔离技术允许用户将不同的端口加入到隔离组中&#xff0c;从而实现这些端口之间的二层数据隔离。这种技…

算法记录——链表

2.链表 2.1判断是否是回文链表 1.方法一&#xff1a;利用栈反转链表 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode…

Invalid Executable The executable contains bitcode

Invalid Executable The executable contains bitcode 升级xcode16后&#xff0c;打包上传testflight时三方库报错&#xff1a;Invalid Executable - The executable ***.app/Frameworks/xxx.framework/xxx contains bitcode. 解决方案&#xff1a; 执行一下指令删除该framew…

软件测试学习路线图

软件测试工程师是专门从事软件、系统或产品测试和评估的技术专业人士&#xff0c;确保它们符合既定标准并无任何缺陷。通过精心设计和执行测试计划&#xff0c;软件测试工程师发现 Bug、故障和需要改进的领域&#xff0c;从而提高最终产品的可靠性和性能。 软件测试工程师在软…

Awcing 799. 最长连续不重复子序列

Awcing 799. 最长连续不重复子序列 解题思路: 让我们找到一个数组中&#xff0c;最长的 不包含重复的数 的连续区间的长度。 最优解是双指针算法&#xff1a; 我们用 c n t [ i ] cnt[i] cnt[i]记录 i i i 这个整数在区间内出现的次数。(因为每个数的大小为 1 0 5 10^5 105, …

状态模式原理剖析

《状态模式原理剖析》 状态模式&#xff08;State Pattern&#xff09; 是一种行为设计模式&#xff0c;它允许对象在其内部状态改变时改变其行为。换句话说&#xff0c;当对象状态发生变化时&#xff0c;它的行为也会随之变化。 通过状态模式&#xff0c;可以消除通过 if-else…

从“可用”到“好用”,百度智能云如何做大模型的“超级工厂”?

如果说&#xff0c;过去两三年大模型处于造锤子阶段&#xff0c;那么今年&#xff0c;更多的则是考验钉钉子的能力&#xff0c;面对各类业务场景大模型是否能够有的放矢、一击必中&#xff0c;为千行百业深度赋能。 当前市场上&#xff0c;已经有200多把这样的锤子在疯狂找钉子…