DMA直接内存访问,STM32实现高速数据传输使用配置

1、DMA运用场景

        随着智能化、信息化的不断推进,嵌入式设备的数据处理量也呈现指数级增加,因此对于巨大的数据量处理的情况时,必须采取其它的方式去替CPU减负,以保证嵌入式设备性能。例如SD卡存储器和音视频、网络高速通信等其它情景使用时,如果仅靠CPU去处理,将会消耗大量的系统资源,并且可能不能满足设备实时性的要求,对于嵌入式等一众资源受限的设备中,这是致命的。因此有必要采取一种特殊的方式,使得在执行大量数据处理过程中,CPU依然去执行正常的嵌入式系统任务。

        在嵌入式系统中,常用DMA去解决这一问题。DMA(Direct Memory Access),直接存储器访问)是一种特殊的硬件功能,用于数据传输而不需要CPU的干预DMA主要用于高速数据传输,可以提高系统性能和效率

        ①数据传输:DMA可以在外设和存储器之间进行高速数据传输,例如将数据从外设(如传感器、音频设备、网络接口等)直接传输到存储器中,或者从存储器中直接传输到外设中。这样可以减少CPU的负载,并提高数据传输的速度和效率。

        ②音视频处理:在多媒体应用中,DMA可以用于将音频、视频等数据从外设传输到存储器中进行处理,或者从存储器传输到外设进行播放。通过使用DMA,系统可以实现高质量的音视频数据传输和处理,同时降低对CPU的负担。

        ③存储器处理:DMA可以用于将存储器中的数据备份到外部存储设备(如硬盘、闪存等),或从外部存储设备中恢复数据到存储器中。这样可以提高数据备份和恢复的速度,并降低对CPU的负载。

        ④高速通信:DMA可以用于在嵌入式系统中实现高速通信,例如通过网络接口卡(如以太网)、串行接口(如UART、SPI)等传输数据。DMA可以在数据传输时,绕过CPU直接在外设和存储器之间进行传输,提高数据传输的速度和效率。

2、STM32 DMA基础

在STM32F4xx官方参考手册文档中,有对DMA控制器的讲解说明,内容十分的丰富,在这一部分中将其中较为核心基础的内容进行了梳理。

直接存储器访问 (DMA) 用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。这样节省的 CPU 资源可供其它操作使用。

DMA搬运的三种模式:

①、内存--->内存

②、内存--->外设

③、外设--->内存

DMA1:外设--->内存,内存--->外设

DMA2:外设--->内存,内存--->外设,内存--->内存

DMA2比DMA1多了一个内存到内存的处理功能,因此,如果需要实现内存到内存的DMA搬运模式,必须使用DMA2。

流:是数据传输的一条链路,每个DMA控制器有8条独立的数据流,每次传输的最大数据量为65535,如果数据单位为字的话,可以一次传输256KB。

通道:每个数据流有8个通道选择,每个通道对应不同的DMA请求。

同一个数据流只能使用一个通道,同一个DMA控制器可以使用多个数据流。

仲裁器:仲裁器为两个 AHB 主端口(存储器和外设端口)提供基于请求优先级的 8 个 DMA 数据流请求管理,并启动外设/存储器访问序列。

仲裁器优先级管理分为软件优先级管理和硬件优先级管理。多个数据流到来时,仲裁器会分为两个阶段进行仲裁,第一个阶段为软件优先级管理,在其编程时设置数据流的优先级第二个阶段为硬件阶段,由数据流的硬件编号决定。

FIFO:源和目标之间的一个数据中转站。FIFO模式下,可以将要传输的多个数据(或字节)累计存储在FIFO缓冲器中,然后在FIFO缓冲器中设置存储阈值,当到达阈值时,FIFO会自动把所有存储的数据一次性的发送到目标地址。

一个FIFO为4个字的大小,每个数据流有4字的FIFO,DMA配置为存储器---存储器模式时,FIFO由硬件开启,软件控制无效。且DMA配置为存储器到存储器模式时,不能设置为循环传输。

如图所示可知,DMA1、DMA2控制器挂载在AHB1总线下

由STM32F4xx官方参考手册可知,对于STM32F407系列,其嵌入式SRAM的起始映射的地址0x2000 0000开始。

由STM32F4xx官方参考手册可知,对于STM32F407系列,其嵌入式FLASH的起始映射的地址0x0800 0000开始。

3、STM32 编程实现DMA

在这一部分的讲解梳理中,将DMA配置为了内存--->内存模式,如果是要配置为内存--->外设外设--->内存,修改 DMA结构体的DMA_InitStructure.DMA_DIR参数即可其它参数的配置的思路大致相同。

//STM32F407中const修饰的全局存储到FLASH中
const uint32_t src_const_buf[32] = {
                                 0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                                 0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
                                 0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
                                 0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
                                 0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
                                 0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
                                 0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
                                 0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80
                              };
uint32_t dst_buf[32] = {0};

因为DMA配置结构体中,需要填入外设/内存的地址信息,所以通过打印数组的内存地址,查看数据的存储位置,以确定外设/内存的地址。由上述STM32F4xx的官方参考手册嵌入式FLASH和SRAM部分说明和打印出来的内存地址可知,const修饰的全局变量被存储于嵌入式FLASH中。

DMA数据传输测试,DMA内存到内存模式代码实现效果如下图所示,通过打印的数据也可确定,DMA高速存储成功且数据无误。

实现DMA存储器到存储器,高速数据传输模式的参考代码Demo如下:

#include "stm32f4xx.h"
#include "stm32f4xx_dma.h"
#include <stdio.h>

//const修饰的全局存储到FLASH中
const uint32_t src_const_buf[32] = {
                                 0xAAAAAAAA,0xBBBBBBBB,0xCCCCCCCC,0xDDDDDDDD,
                                 0xEEEEEEEE,0xFFFFFFFF,0x10000000,0x11111111,
                                 0x22222222,0x33333333,0x44444444,0x55555555,
                                 0x66666666,0x77777777,0x88888888,0x99999999,
                                 0x10000000,0x10000000,0x10000000,0x10000000,
                                 0x11111111,0x11111111,0x11111111,0x11111111,
                                 0x22222222,0x22222222,0x22222222,0x22222222,
                                 0x33333333,0x33333333,0x33333333,0x33333333
                              };
uint32_t dst_buf[32] = {0};

void DMA_Config(void);
int8_t buf_cmp(uint32_t *pbuf1, uint32_t *pbuf2, int len);

int main(void)
{
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
    USART1_Init(115200);
    printf("starting...\r\n");
    printf("const addr:%X\r\n", (uint32_t)src_const_buf);
    printf("addr:%X\r\n", (uint32_t)dst_buf);     
    DMA_Config();
    while(DMA_GetCmdStatus(DMA2_Stream0) != DISABLE);    //传输完成后,DMA会进行复位
    if(buf_cmp((uint32_t *)src_const_buf, dst_buf, 32)==0)
    {
        
        printf("DMA传输完成,且数据无误...\r\n");
        printf("src:\r\n");
        for(int i = 0; i<32; i++)
        {
            printf("%0X\t", src_const_buf[i]);
        }
        printf("\r\n");
        printf("dst:\r\n");
        for(int i = 0; i<32; i++)
        {
            printf("%0X\t", dst_buf[i]);
        }
        printf("\r\n");
        return 0;
    }else{
        printf("DMA数据传输故障...\r\n");    
        return -1;
    }
    
}

void DMA_Config(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    volatile uint32_t  Timeout = 10000;
        
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);//内存到内存的传送,使用DMA2
    DMA_DeInit(DMA2_Stream0);    //初始化DMA的寄存器到复位状态
        
    while(DMA_GetCmdStatus(DMA2_Stream0) != DISABLE);    //确保DMA复位完成

    
    //配置DMA流
    DMA_InitStructure.DMA_Channel = DMA_Channel_0;    //启用DMA通道0
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)src_const_buf; //FLASH中的数据地址
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dst_buf;
//SRAM的数据地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;    //存储器到存储器模式
    DMA_InitStructure.DMA_BufferSize = (uint32_t)32;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;    //FLASH地址自增使能
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;            //SRAM地址自增
            //SRAM地址自增使能
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    
    DMA_Init(DMA2_Stream0, &DMA_InitStructure);    //DMA初始化
    
    DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
    DMA_Cmd(DMA2_Stream0, ENABLE);        //DMA使能
        
    while(DMA_GetCmdStatus(DMA2_Stream0) != ENABLE && (Timeout-->0));
    
    if(Timeout == 0)
    {
        while(1);
    }
    
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}


int8_t buf_cmp(uint32_t *pbuf1, uint32_t *pbuf2, int len)
{
    int cnt = 0;
    for(int i=0; i<len; i++)
    {
        if(pbuf1[i] != pbuf2[i])
        {
            return -1;
        }
    }
    return 0;
}

void DMA2_Stream0_IRQHandler(void)
{    
    //DMA2通道0数据流传输完成中断 
    if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) == SET)
    {
        //清除DMA传输完成中断标志位
        DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
        //在此可根据项目需求增加DMA处理完时的操作
    }

}

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

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

相关文章

探讨CSDN等级制度:博客等级、原力等级、创作者等级

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;Vir2021GKBS &#x1f43c;本文由…

数据结构:双向链表

文章目录 1. 双向带头循环链表的结构2. 相关操作2.1 创建节点2.2 尾插2.3 头插2.4 打印2.5 尾删2.6 头删2.7 查找2.8 指定位置前/后插入2.9 删除指定位置的节点2.10 删除指定位置后的节点2.11 销毁链表 3.顺序表与链表区别 1. 双向带头循环链表的结构 与单链表不同的是&#xf…

C#,奥西里斯数(Osiris Number)的算法与源代码

1 奥西里斯数(Osiris Number) 奥西里斯数(Osiris Number)是一个数字&#xff0c; 其值等于通过将其自身数字的所有排列相加而形成的所有数字的值之和。 计算结果&#xff1a; 2 源程序 using System; namespace Legalsoft.Truffer.Algorithm { /// <summary> /…

Django学习记录02

1.请求与响应 1.1get与post的区别 get 一般是从url输入地址&#xff0c;会调用get请求 post 一般是内部数据传输# get请求 def something(request):# req是一个对象&#xff0c;封装了用户发送过来的所有请求相关数据# 1.获取请求方式 http://localhost:8000/something# pri…

Ubuntu22.04 gnome-builder gnome C 应用程序习练笔记(二)

gnome-builder创建的程序&#xff0c;在工程树中有三个重要程序&#xff1a;main主程序、application应用程序和window主窗口程序。main整个程序的起始&#xff0c;它会操作application生产应用环境&#xff0c;application会操作window生成主窗口&#xff0c;于是就有了 appli…

【lesson47】进程通信之system V(共享内存)补充知识

文章目录 补充知识 补充知识 进行通信的key值问题&#xff0c;进程要通信的对方进程怎么能保证对方能看到&#xff0c;并且看到的就是该进程创建的共享内存的。 所以就通过key值来标识共享内存&#xff0c;key值是几不重要&#xff0c;只要在系统里是唯一的即可。 这样server和…

Java图形化界面编程——Container容器 笔记

2.3 Container容器 2.3.1 Container继承体系 Winow是可以独立存在的顶级窗口,默认使用BorderLayout管理其内部组件布局;Panel可以容纳其他组件&#xff0c;但不能独立存在&#xff0c;它必须内嵌其他容器中使用&#xff0c;默认使用FlowLayout管理其内部组件布局&#xff1b;S…

2月8日作业

1、现有文件test.c\test1.c\main.c,编写Makkefile 代码&#xff1a; CCgcc EXEa.out OBJS$(patsubst %.c,%.o,$(wildcard *.c)) CFLAGS-c -oall:$(EXE)$(EXE):$(OBJS)$(CC) $^ -o $%.o:%.c$(CC) $(CFLAGS) $ $^.PHONY:cleanclean:rm $(OBJS) $(EXE)运行结果&#xff1a; 2、…

【网络】:序列化和反序列化

序列化和反序列化 一.json库 二.简单使用json库 前面已经讲过TCP和UDP&#xff0c;也写过代码能够进行双方的通信了&#xff0c;那么有没有可能这种通信是不安全的呢&#xff1f;如果直接通信&#xff0c;可能会被底层捕捉&#xff1b;可能由于网络问题&#xff0c;一方只接收到…

云计算运营模式介绍

目录 一、云计算运营模式概述 1.1 概述 二、云计算服务角色 2.1 角色划分 2.1.1 云服务提供商 2.1.2 云服务消费者 2.1.3 云服务代理商 2.1.4 云计算审计员 2.1.5 云服务承运商 三、云计算责任模型 3.1 云计算服务模式与责任关系图 3.2 云计算服务模式与责任关系解析…

【LeetCode每日一题】525连续数组 303区域和检索(前缀和的基本概念和3个简单案例)

前缀和 // 构造prefix let prefix [0] arr.forEach(num > {prefix.push(prefix.at(-1) num); })如果想要计算某个区间 i 到 j 这个子数组的和时&#xff0c;可以根据 prefix[j1] - prefix[i] 获得。 例题1&#xff1a;303.区域和检索 - 数组不可变 给定一个整数数组 num…

深度神经网络中的BNN和DNN:基于存内计算的原理、实现与能量效率

前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;https://www.captainbed.cn/z ChatGPT体验地址 文章目录 前言引言内存计算体系结构深度神经网络&#xff08;DNN&#xff09;随机梯度的优…

C++进阶(十三)异常

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、C语言传统的处理错误的方式二、C异常概念三、异常的使用1、异常的抛出和捕获2、异常的重新…

【Cocos入门】场景切换(loadScene、preloadScene)

一、loadScene 加载场景 loadScene(sceneName: string, onLaunched: Director.OnSceneLaunched, onUnloaded: Director.OnUnload) : boolean 通过场景名称进行加载场景。返回值为布尔类型 参数&#xff1a; NameTypeDescriptionsceneNamestring场景名称onLaunchedDirector.O…

FPGA_工程_按键控制的基于Rom数码管显示

一 信号 框图&#xff1a; 其中 key_filter seg_595_dynamic均为已有模块&#xff0c;直接例化即可使用&#xff0c;rom_8*256模块&#xff0c;调用rom ip实现。Rom_ctrl模块需要重新编写。 波形图&#xff1a; 二 代码 module key_fliter #(parameter CNT_MAX 24d9_999_99…

基于微信小程序的新生报到系统的研究与实现,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

分布式springboot 3项目集成mybatis官方生成器开发记录

文章目录 说明实现思路实现步骤第一步&#xff1a;创建generator子模块第二步&#xff1a;引入相关maven插件和依赖第三步&#xff1a;编写生成器配置文件第四步&#xff1a;运行查看结果 说明 该文章为作者开发学习记录&#xff0c;方便以后复习和交流主要内容为&#xff1a;…

Zoho Mail企业邮箱商业扩展第3部分:计算财务状况

在Zoho Mail商业扩展系列的压轴篇章中&#xff0c;王雪琳利用Zoho Mail的集成功能成功地完成了各项工作&#xff0c;并顺利地建立了自己的营销代理机构。让我们快速回顾一下她的成功之路。 一、使用Zoho Mail成功方法概述 首先她通过Zoho Mail为其电子邮件地址设置了自定义域…

如何开始深度学习,从实践开始

将“如何开始深度学习”这个问题喂给ChatGPT和文心一言&#xff0c;会给出很有专业水准的答案&#xff0c;比如&#xff1a; 要开始深度学习&#xff0c;你可以遵循以下步骤&#xff1a; 学习Python编程语言的基础知识&#xff0c;因为它在深度学习框架中经常被使用。 熟悉线性…

2023年出版的新书中提到的《人月神话》(202402更新)(2)共8本

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 《人月神话》于1975年出版&#xff0c;1995年出二十周年版。自出版以来&#xff0c;该书被大量的书籍和文章引用&#xff0c;直到现在热潮不退。 2023年&#xff0c;清华大学出版社推…