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

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函数中,处理传输完成的数据。

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

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

相关文章

Jmeter+MySQL链接+JDBC Connection配置元件+使用

参考大大的博客学习&#xff1a;怎么用JMeter操作MySQL数据库&#xff1f;看完秒懂&#xff01;_jmeter mysql_程序员馨馨的博客-CSDN博客 注&#xff1a;里面所有没打码的都是假数据&#xff0c;麻烦大家自行修改正确的信息。 一、背景 需要取数据库中的值&#xff0c;作为…

分布式操作系统会不会是操作系统的终端形态?

昨天一位网友私信我&#xff0c;提出一个问题&#xff1a;“Laxcus分布式操作系统会不会是操作系统发展的终极形态&#xff1f;”。今天觉得有必要把这件事说一说&#xff0c;所以就忙里偷闲写下这篇文章。 咱们先说结论&#xff1a;是也不是&#xff0c;需要具体情况具…

C++--菱形继承

1.什么是菱形继承 单继承&#xff1a;一个子类只有一个直接父类时称这个继承关系为单继承 多继承&#xff1a;一个子类有两个或以上直接父类时称这个继承关系为多继承 菱形继承的问题&#xff1a;菱形继承有数据冗余和二义性的问题&#xff0c;数据冗余是由于创建多个相同类型的…

Gradle和Maven的区别

Gradle和Maven 当涉及到构建和管理项目时&#xff0c;Gradle和Maven是两个非常流行的选项。本文将讨论Gradle和Maven之间的区别以及它们的配置信息差异。 1. Gradle和Maven的区别 1.1 构建脚本语言 Maven使用XML作为构建脚本语言&#xff0c;而Gradle使用基于Groovy的DSL&…

❤️创意网页:创意动态画布~缤纷移动涂鸦~图片彩色打码

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

【Vue】在el-table的el-table-column中,如何控制单行、单列、以及根据内容单独设置样式。例如:修改文字颜色、背景颜色

用cell-style表属性来实现。在官网中是这样表述这个属性的。 在el-table中用v-bind绑定此属性。&#xff08;v-bind的简写是&#xff1a;&#xff09; <el-table:data"options":cell-style"cell"><el-table-column prop"id" label"…

设计模式-建造者模式

在前面几篇文章中&#xff0c;已经讲解了单例模式、工厂方法模式、抽象工厂模式&#xff0c;创建型还剩下一个比较重要的模式-建造者模式。在理解该模式之前&#xff0c;我还是希望重申设计模式的初衷&#xff0c;即为解决一些问题而提供的优良方案。学习设计模式遗忘其初衷&am…

图文教程:使用 Photoshop、3ds Max 和 After Effects 创建被风暴摧毁的小屋

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 在 Photoshop 中设置图像 步骤 1 打开 Photoshop。 打开 Photoshop 步骤 2 我已经将小屋的图像导入到Photoshop中以演示 影响。如果您愿意&#xff0c;可以使用其他图像。 图片导入 步骤 3 由于小…

css实现渐变边框动画

渐变边框动画 1、实现效果2、实现代码 1、实现效果 2、实现代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">&…

Jenkins搭建最简教程

纠结了一小会儿&#xff0c;到底要不要写这个&#xff0c;最终还是决定简单记录一下&#xff0c;因为Jenkins搭建实在是太简单了&#xff0c;虽然也有坑&#xff0c;但是坑主要在找稳定的版本上。 先学一个简称&#xff0c;LTS (Long Term Support) 属实是长见识了&#xff0c…

【高级数据结构】并查集

目录 修复公路&#xff08;带扩展域的并查集&#xff09; 食物链&#xff08;带边权的并查集&#xff09; 修复公路&#xff08;带扩展域的并查集&#xff09; 洛谷&#xff1a;修复公路https://www.luogu.com.cn/problem/P1111 题目背景 A 地区在地震过后&#xff0c;连接…

Qt —— Vs2017编译hiredis源码并测试调用(附调用hiredis库源码)

下载hiredis源码 编译hiredis源码 1、解压下载的hiredis源码包,如图使用Vs2017打开hiredis_win.sln 2、如下两图,Vs2017打开.sln后点击升级。 分别对两个工程的debug、release进行配置。Debug配置为多线程调试DLL(MDd)、Release配置为多线程DLL(/MD),这样做是为了配合被调用…

GAMES104里渲染等一些剩下的问题

渲染的一些剩下的问题 1. 如何理解渲染中的AO(环境光遮蔽) 环境光遮蔽 我们先从一个简单的效果开始—环境光遮蔽(Ambient Occlusion,以下简称AO)。大家可以看到&#xff0c;下图中的场景没有任何渲染效果&#xff0c;也没有任何着色效果&#xff0c;但场景呈现出了非常清晰的…

Flutter:滑动面板

前言 无意中发现了这个库&#xff0c;发现现在很多app中都有类似的功能。以手机b站为例&#xff0c;当你在看视频时&#xff0c;点击评论&#xff0c;视频会向上偏移&#xff0c;下方划出评论界面。 sliding_up_panel SlidingUpPanel是一个Flutter插件&#xff0c;用于创建滑…

【Python机器学习】实验04(2) 机器学习应用实践--手动调参

文章目录 机器学习应用实践1.1 准备数据此处进行的调整为&#xff1a;要所有数据进行拆分 1.2 定义假设函数Sigmoid 函数 1.3 定义代价函数1.4 定义梯度下降算法gradient descent(梯度下降) 此处进行的调整为&#xff1a;采用train_x, train_y进行训练 1.5 绘制决策边界1.6 计算…

CentOS 7安装PostgreSQL 15版本数据库

目录 一、何为PostgreSQL&#xff1f; 二、PostgreSQL安装 2.1安装依赖 2.2 执行安装 2.3 数据库初始化 2.4 配置环境变量 2.5 创建数据库 2.6 配置远程 2.7 测试远程 三、常用命令 四、用户创建和数据库权限 一、何为PostgreSQL&#xff1f; PostgreSQL是以加州大学…

Python接口自动化测试框架运行原理及流程

这篇文章主要介绍了Python接口自动化测试框架运行原理及流程,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 本文总结分享介绍接口测试框架开发&#xff0c;环境使用python3selenium3unittestddtrequests测试框…

【MyBatis-Plus 进阶学习笔记】

MyBatis-Plus 进阶学习笔记记录 一、 MyBatis Plus 七大功能0. 数据准备1. 逻辑删除2. 自动填充2.1 优化1 自动填充 有的类没有更新和创建时间字段2.2 优化2 自己设置时间时填充自己设置的&#xff0c;不设置时自动填充 3. 乐观锁插件 注&#xff1a;wrapper不能服用4. 性能分析…

Docker续集+Docker Compose

目录 Containerd与docker的关系 runCrunC与Containerd的关联 OCI协议Dockerfile多阶段构建&#xff08;解决&#xff1a;如何让一个镜像变得更小 &#xff09;多阶段构建Images瘦身实践.dockerignore Docker Compose快速开始Quick StartCompose 命令常用命令命令说明 Compose 模…

吴师傅教你几招极速清理C盘,高能操作绝不让你失望!

电脑使用久了&#xff0c;C盘堆积的垃圾过多&#xff1b;每天上网会给电脑带来很多临时文件&#xff0c;这些垃圾文件不清理掉时间久了就会影响到电脑的运行速度&#xff1b;也会导致C盘变红&#xff0c;空间不足。那么&#xff0c;电脑C盘满了如何清理呢&#xff1f;教你几招极…