DMA传输原理与实现详解(超详细)

news2024/9/30 2:08:45

DMA(Direct Memory Access,直接内存访问)是一种计算机数据传输方式,允许外围设备直接访问系统内存,而无需CPU的干预。
在这里插入图片描述

文章目录

    • Part 1: DMA的工作原理
      • 配置阶段:
      • 数据传输阶段:
    • Part 2: DMA数据组成
    • Part 3: DMA传输过程的实现
    • Part 4: DMA中断处理和性能优化
      • DMA中断处理:
      • DMA性能优化:
    • Part 5: STM32实现DMA
      • 基于标准库
      • 基于HAL库

Part 1: DMA的工作原理

DMA(Direct Memory Access,直接内存访问)是一种计算机数据传输方式,允许外围设备直接访问系统内存,而无需CPU的干预。下面详细介绍DMA的工作原理:

配置阶段:

  1. 配置源地址(Source Address):通过指定源地址,DMA可以知道需要传输数据的起始位置。

  2. 配置目标地址(Destination Address):指定目标地址,将数据传输到系统内存中的相应位置。

  3. 配置数据长度(Data Length):DMA需要知道需要传输的数据长度,以便正确地读取和写入数据。

  4. 配置控制信息(Control Information):例如传输模式、中断使能等参数,用于指定传输的具体配置。

数据传输阶段:

在这里插入图片描述

  1. 外设发起传输请求:外围设备(如网络接口卡、硬盘控制器)向DMA控制器发起传输请求。

  2. DMA控制器响应请求:DMA控制器接收到传输请求后,暂停CPU的访问,并通过请求信号(如DMA请求信号)获取对系统总线的控制权。

  3. 读取数据:DMA控制器从外设读取数据,并存储在内部缓冲区中。

  4. 数据传输:DMA控制器将数据从内部缓冲区传输到系统内存中的目标地址。

  5. 传输完成通知:当数据传输完成后,DMA控制器会释放对系统总线的控制权,并发出传输完成的中断信号,通知CPU。

  6. CPU处理中断:CPU接收到传输完成的中断信号后,会执行相应的中断处理程序。

Part 2: DMA数据组成

DMA传输涉及的数据主要有以下几种组成:

  1. 源地址(Source Address):源地址表示数据传输的起始地址,即外设设备中数据缓冲区的地址。DMA将从这个地址开始读取数据。

  2. 目标地址(Destination Address):目标地址表示数据传输的目的地址,即系统内存中的指定地址。DMA将数据传输到这个地址。

  3. 数据长度(Data Length):数据长度表示需要传输的数据大小。它可以以字节、字或者其他单位进行表示。

  4. 控制信息(Control Information):控制信息包括传输模式、中断使能等参数。在传输过程中,DMA根据这些参数来控制数据的传输行为。

此外,还有一些额外的参数和寄存器与DMA相关,用于配置和控制DMA的操作,例如:

  1. DMA通道选择(DMA Channel Selection):在具有多个DMA通道的系统中,选择要使用的DMA通道。

  2. DMA传输模式(DMA Transfer Mode):指定DMA传输的模式,如单次传输模式、循环传输模式等。

  3. DMA中断使能(DMA Interrupt Enable):用于控制DMA传输完成时是否产生中断。

Part 3: DMA传输过程的实现

DMA的传输过程涉及多个步骤,包括启动DMA、请求传输、数据读取和写入等操作。
在这里插入图片描述

下面是DMA传输过程的一个简单实现示例:

  1. 配置DMA参数
    在开始DMA传输之前,需要先配置DMA相关的参数,如源地址、目标地址、数据长度和控制信息等。这些参数通常通过设置相应的寄存器来实现。
// 配置DMA
void configureDMA(uint32_t sourceAddr, uint32_t destAddr, uint32_t dataLength) {
    // 配置源地址和目标地址
    writeDMARegister(SOURCE_ADDRESS_REG, sourceAddr);
    writeDMARegister(DESTINATION_ADDRESS_REG, destAddr);
    
    // 配置数据长度
    writeDMARegister(DATA_LENGTH_REG, dataLength);
    
    // 配置控制信息,如传输模式、中断使能等
    writeDMARegister(CONTROL_INFO_REG, controlInfo);
}
  1. 启动DMA传输
    配置完成后,通过设置相应的使能寄存器,启动DMA传输。
// 启动DMA传输
void startDMA() {
    // 设置DMA使能位
    writeDMARegister(ENABLE_REG, 1);
    
    // 发送传输请求
    sendDMARequest();
}
  1. 请求传输
    外设设备发出DMA请求,请求DMA控制权,开始数据传输过程。DMA控制器收到传输请求后,暂停CPU的访问,并通过请求信号(如DMA请求信号)获取对系统总线的控制权。
// 发送DMA传输请求
void sendDMARequest() {
    // 发送DMA请求信号给DMA控制器
    setDMARequestSignal();
}
  1. 数据读取和写入
    DMA控制器根据配置的参数,从外设设备中读取数据,并将其写入系统内存中的目标地址。
// 读取数据并写入内存
void transferData() {
    // 从外设读取数据
    uint32_t data = readDataFromPeripheral();
    
    // 写入内存
    writeDataToMemory(data);
}
  1. 传输完成通知
    当数据传输完成后,DMA控制器会释放对系统总线的控制权,并发送传输完成的中断信号,通知CPU。
// DMA中断处理函数
void handleDMAInterrupt() {
    // 处理传输完成的中断信号
    // ...
}

Part 4: DMA中断处理和性能优化

DMA中断处理:

在DMA传输完成时,DMA控制器可以触发一个中断,通知CPU传输已完成。CPU可以相应地执行中断处理程序,进行必要的操作。

  1. 中断使能设置:
    在配置DMA参数时,通过设置相应的控制信息,可以选择是否使能DMA传输完成中断。如果使能了中断,DMA传输完成时会产生中断请求信号。否则,传输完成后不会触发中断。

  2. 中断处理程序:
    在CPU侧,需要编写中断处理程序来处理DMA传输完成中断。中断处理程序负责执行相应的操作,如处理传输完成的数据、清除中断标志等。

DMA性能优化:

为了提高DMA传输的效率和性能,可以采取以下优化技术:

  1. 数据对齐(Data Alignment):
    尽可能地对齐数据可以提高DMA的传输效率。许多硬件平台在DMA传输时对数据对齐有限制,所以确保数据在传输过程中的对齐是重要的。

  2. 数据块传输(Block Transfer):
    DMA支持以块为单位的数据传输,逐次传输多个数据块,并在传输完成后给出一个中断通知。这种方式比每次传输一个数据更高效,减少了中断的开销和系统总线访问的次数。

  3. 通道优先级(Channel Priority):
    在具有多个DMA通道的系统中,可以通过设置不同的通道优先级,来决定DMA通道之间的数据传输优先级。这样可以在多个外设设备同时请求传输时,对优先级较高的设备进行优先处理。

  4. 多重缓冲区(Double Buffering):
    使用多个缓冲区来存储数据可以提高DMA传输效率。当DMA从一个缓冲区传输数据时,CPU可以同时向另一个缓冲区写入新的数据,从而实现并行操作。

Part 5: STM32实现DMA

基于标准库

示例代码:

#include "stm32f10x.h"

#define BUFFER_SIZE 100

uint32_t sourceBuffer[BUFFER_SIZE];
uint32_t destinationBuffer[BUFFER_SIZE];

void DMA_Configuration(void) {
    DMA_InitTypeDef DMA_InitStructure;
    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    DMA_DeInit(DMA1_Channel1);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)destinationBuffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
    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);
}

int main() {
    // 初始化源缓冲区
    for (int i = 0; i < BUFFER_SIZE; i++) {
        sourceBuffer[i] = i;
    }
    
    // 配置DMA
    DMA_Configuration();
    
    while (1) {
        // 等待DMA传输完成
        while (!DMA_GetFlagStatus(DMA1_FLAG_TC1));
        
        // 处理传输完成的数据
        for (int i = 0; i < BUFFER_SIZE; i++) {
            // 处理destinationBuffer中的数据
            // ...
        }
        
        // 清除DMA传输完成标志位
        DMA_ClearFlag(DMA1_FLAG_TC1);
    }
}

在这个示例代码中,首先通过DMA_Configuration函数进行DMA的配置。然后在主循环中等待DMA传输完成的标志位,处理传输完成的数据,并清除传输完成标志位。

基于HAL库

示例代码:

#include "stm32f1xx_hal.h"

#define BUFFER_SIZE 100

DMA_HandleTypeDef hdma_adc1;

uint32_t sourceBuffer[BUFFER_SIZE];
uint32_t destinationBuffer[BUFFER_SIZE];

void DMA_Configuration(void) {
    __HAL_RCC_DMA1_CLK_ENABLE();
    
    hdma_adc1.Instance = DMA1_Channel1;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
    
    HAL_DMA_Init(&hdma_adc1);
    
    __HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
    
    HAL_DMA_Start(&hdma_adc1, (uint32_t)&(ADC1->DR), (uint32_t)destinationBuffer, BUFFER_SIZE);
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
    // 处理传输完成的数据
    for (int i = 0; i < BUFFER_SIZE; i++) {
        // 处理destinationBuffer中的数据
        // ...
    }
}

int main() {
    // 初始化源缓冲区
    for (int i = 0; i < BUFFER_SIZE; i++) {
        sourceBuffer[i] = i;
    }
    
    // 配置DMA
    DMA_Configuration();
    
    // 启动ADC转换
    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)sourceBuffer, BUFFER_SIZE);
    
    while (1) {
        // 主循环中不需要额外的处理
        
        // 在需要使用CPU的其他任务中加入适当的延时或等待DMA传输完成的标志位
        // ...
    }
}

这个示例使用了STM32Cube HAL库提供的HAL库函数进行DMA的配置和控制。在DMA_Configuration函数中,使用HAL_DMA_Init函数进行DMA的初始化,并且通过__HAL_LINKDMA宏将DMA与ADC关联起来。在HAL_ADC_ConvCpltCallback函数中,处理传输完成的数据。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【弹力设计篇】聊聊灾备设计、异地多活设计

单机&集群架构 对于一个高可用系统来说&#xff0c;为了提升系统的稳定性&#xff0c;需要以下常用技术服务拆分、服务冗余、限流降级、高可用架构设计、高可用运维&#xff0c;而本篇主要详细介绍下&#xff0c;高可用架构设计。容灾备份以及同城多活&#xff0c;异地多活…

Python开发之手动实现一维线性插值

Python开发之手动实现一维线性插值 1.线性插值法介绍2.手动实现线性插值3.案例一手动实现线性插值4.使用pandas的插值方法实现要求(推荐) 前言&#xff1a;主要介绍手动实现一维线性插值以及pandas里面的interpolate方法实现线性插值。 1.线性插值法介绍 线性插值法是一种简单…

MySQL中锁的简介——行级锁之 间隙锁 和 临键锁

1.间隙锁演示 2.临键锁演示 间隙锁锁住的是间隙&#xff0c;不包含对应的数据记录&#xff0c;而临键锁既会包含当前这条数据记录&#xff0c;也会锁定该数据记录之前的间隙。间隙锁的目的是防止其他事务插入间隙造成幻读现象。间隙锁是可以共存的&#xff0c;一个事务采用的间…

maven引入本地jar包的简单方式【IDEA】【SpringBoot】

前言 想必点进来看这篇文章的各位&#xff0c;都是已经习惯了Maven从中央仓库或者阿里仓库直接拉取jar包进行使用。我也是&#x1f921;&#x1f921;。 前两天遇到一个工作场景&#xff0c;对接三方平台&#xff0c;结果对方就是提供的一个jar包下载链接&#xff0c;可给我整…

sqlSugar应用表值函数

一、新建表值函数 TableIntSplit 二、新建类 var employees _sqlSugarClient.Queryable<Employees>().InnerJoin(_sqlSugarClient.SqlQueryable<TableID>("select * from dbo.TableIntSplit(ids,split)").AddParameters(new { ids "1,2", s…

无涯教程-jQuery - Slide方法函数

幻灯片效果可以与show/hide/toggle一起使用。这会将元素滑出视口。 Slide - 语法 selector.hide|show|toggle( "slide", {arguments}, speed ); 这是所有参数的描述- direction - 效果的方向。可以是"左(left)"&#xff0c;"右(right)"&…

C语言第十一课--------操作符的使用与分类-------基本操作

作者前言 作者介绍&#xff1a; 作者id&#xff1a;老秦包你会&#xff0c; 简单介绍&#xff1a; 喜欢学习C语言和python等编程语言&#xff0c;是一位爱分享的博主&#xff0c;有兴趣的小可爱可以来互讨 个人主页::小小页面 gitee页面:秦大大 一个爱分享的小博主 欢迎小可爱们…

解决 tensorflow 出现的 ImportError: Could not find the DLL(s) ‘msvcp140_1.dll‘. 问题

在安装完tensorflow库后出现 问题详述&#xff1a; ImportError: Could not find the DLL(s) msvcp140_1.dll. TensorFlow requires that these DLLs be installed in a directory that is named in your %PATH% environment variable. You may install these DLLs by downlo…

JSP 文件上传

JSP 可以与 HTML form 标签一起使用&#xff0c;来允许用户上传文件到服务器。上传的文件可以是文本文件或图像文件或任何文档。 本章节我们使用 Servlet 来处理文件上传&#xff0c;使用到的文件有&#xff1a; upload.jsp : 文件上传表单。 message.jsp : 上传成功后跳转页…

超聚变和厦门大学助力兴业银行构建智慧金融隐私计算平台,助力信用卡业务精准营销...

兴业银行与超聚变数字技术有限公司、厦门大学携手&#xff0c;发挥产学研用一体化整体优势联合建设&#xff0c;厦门大学提供先进的算法模型及科研能力&#xff0c;超聚变提供产品解决方案及工程能力&#xff0c;兴业银行提供金融实践能力&#xff0c;三方发挥各自领域优势&…

RTPS规范v2.3(中文版)

实时发布订阅协议 DDS互操作性有线协议 &#xff08;DDSI-RTPS&#xff09; 技术规范 V2.3 &#xff08;2019-04-03正式发布&#xff09; https://www.omg.org/spec/DDSI-RTPS/2.3/PDF   目 录 0. 序言&#xff08;关于对象管理组&#xff09; 4 0.1 OMG 4 0.2 OMG规…

C++之浅拷贝和深拷贝总结(一百七十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

【SLAM】A-LOAM 算法部署与测试(Win10 + VMWare + Ubuntu18.04)

基础环境 ubuntu及ROS安装&#xff0c;略&#xff1b;安装完ROS以后&#xff0c;默认已经安装好了PCL和Eigen库&#xff1b; 安装Ceres 下载Ceres源文件&#xff1a;&#xff08; Vmware没有网络&#xff0c;到下面的网址手动下载安装包 &#xff09; - https://github.com…

MyBatis-Plus自定义sql注入器

文章目录 一、前言二、MyBatis-Plus自定义sql注入器功能实现2.1、编写自定义sql类2.2、将自定义sql添加到BaseMapper中2.3、编写自己的sql注入器 一、前言 在日常开发过程中&#xff0c;我们可能会发现 MyBatis-Plus 提供的那些自带的 sql 语句无法满足我们的开发需求&#xf…

Java中注解应用场景

1.Parameter注解 Parameter(names "-browser", description "browser name, supported scope [chrome]", required true) Param注解的用法解析_parameter_fFee-ops的博客-CSDN博客 Public User selectUser(param(“userName”) String name, param(“…

C# Solidworks二次开发:向量相关的数学函数API的使用介绍

今天要讲的是Solidworks二次开发时候&#xff0c;如何使用一些与数学相关的API方法的介绍&#xff0c;在Solidworks中本身提供了一个函数用于对数学对象的访问&#xff0c;函数名为MathUtility。借助这个函数&#xff0c;我们来引出今天要介绍的几个API。 &#xff08;1&#…

剑指 Offer. 二叉树的镜像

请完成一个函数&#xff0c;输入一个二叉树&#xff0c;该函数输出它的镜像。 例如输入&#xff1a; 镜像输出&#xff1a; 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 代码如下&#xff1a; class Solution { public:Tre…

android下的app性能测试应主要针对那些方面,如何开展?

如何开展安卓手机下的App性能测试&#xff0c;对于优秀的测试人员而言&#xff0c;除了要懂得性能测试的步骤流程外&#xff0c;还应该懂的性能测试的一些其他知识&#xff0c;比如性能测试指标、各指标的意义&#xff0c;常用的性能测试工具、如何查看结果分析等等知识。所以本…

P1535 [USACO08MAR] Cow Travelling S(dfs+剪枝 or 记忆化搜索)

1:本题暴力做法简单&#xff0c;重点在于我们如何剪枝&#xff1a; :《曼哈顿距离》我们每走一个点就判断&#xff0c;当前点到终点的最短步数是不是小于当前剩余的步数&#xff0c; 如果大于就肯定不符合直接return,或者当步数为0时&#xff0c;当还没到达终点&#xff0c;那…

web流程自动化详解

今天给大家带来Selenium的相关解释操作 一、Selenium Selenium是一个用于自动化Web浏览器操作的开源工具和框架。它提供了一组API&#xff08;应用程序接口&#xff09;&#xff0c;可以让开发人员使用多种编程语言&#xff08;如Java、Python、C#等&#xff09;编写测试脚本&…