012 - STM32学习笔记 - DMA_存储器到存储器

news2024/11/18 1:32:37

011 - STM32学习笔记 - DMA直接存储器

1、DMA简介

我们知道stm32在实际应用过程中具有很强大的功能,包含数据的采集、处理、逻辑功能的运算等,因此stm32一直在处理大量的事务,但是在实际使用过程中,我们知道有些事情实际上不需要CPU过多参与,比如:数据的复制和存储,当产生大量数据的时候,CPU如果转过来进行处理的话,那么对于CPU来说时额外的负载,会严重降低CPU的实际效率。

为此stm32提出了DMA(Data Memory Access)的概念,即直接存储器访问,DMA是用来将数据从一个地址空间转到另一个地址空间,并且提供在外设和存储器之间或者存储器和存储器之间的高速数据传输功能。这里外设指的是外设的数据寄存器,存储器指的是片内的SRAM或者FLASH。因此DMA主要功能就是不需要CPU参与,来实现数据的转移,从而避免CPU的过度资源消耗。

先看一下DMA的功能框图

在这里插入图片描述

系统中,将DMA分为6个部分:

①、通道+流

:是数据传输的链路,每个DMA控制器有8条独立的数据流(REQ_STREAM0 ~ REQ_STREAM7),每次传输的数据量最大为65535,假设单位数据单位为字,那么一次就可以传输256Kb。

通道:每个数据流有8个通道(REQ_STRx_CH0 ~ REQ_STRx_CH0),每个通道对应不同的DMA请求。

②、仲裁器

当出现多个数据流时,DMA需要决定哪个数据流先进行传输,哪个后传输,因此增加了仲裁器,对于数据流的优先级,与中断的优先级比较类似,也是从两个方面进行控制:

软件设置:通过DMA_SxCR:PL位,可以设置为:00 - 低;01 - 中;10 - 高;11 - 非常高。

硬件设置:当多个流设置的优先级相同时,根据硬件数据流的编号来决定,编号越小,优先级越高。

③、FIFO

FIFO(First In,First Out),FIFO类似于一个管道,作为源和目标的数据中转站,每个数据流有4个字的FIFO,其中阈值级别可以分为1/4,1/2,3/4或者满,这里的意思是根据FIFO管道的空间使用情况,可以选择一次使用多大容量作为中转站。

在这里插入图片描述

阈值容量选择可以通过FIFO控制器DMA_SxFCR:FTH[1:0]进行配置:

00:选择FIFO的1/4;

01:选择FIFO的1/2;

10:选择FIFO的3/4;

11:选择FIFO的完整容量;

:数据采用FIFO进行传输时,突发模式的配置一定要注意

在这里插入图片描述

MSIZE = 字节

当选择FIFO级别为1/4容量时,即管道容量选择为1个字,需要4个字节才能填充满,选择MBURST=INCR4(选择4个字节)时,会发生1次数据传递;

当选择FIFO级别为1/2容量时,即管道容量选择为2个字,需要8个字节才能填充满,选择MBURST=INCR4(选择4个字节)时,发生两次数据传递,或者选择MBURST=INCR8(选择8个字节)时发生1次数据传递;

当选择FIFO级别为3/4容量时,即管道容量选择为3个字,需要12个字节才能填充满,选择MBURST=INCR4(选择4个字节)时,会发生3次数据传递;

当选择FIFO级别为满容量时,即管道容量选择为4个字,需要16个字节才能填充满,选择MBURST=INCR4(选择4个字节)时,发生4次数据传递,选择MBURST=INCR8(选择8个字节)时,发生2次数据传递,选择MBURST=INCR16(选择16个字节)时,发生1次数据传递。

MSIZE = 半字

当选择FIFO级别为1/2容量时,即管道容量选择为2个字,需要4个半字才能填充满,选择MBURST=INCR4(选择4个字节)时,发生1次数据传递

当选择FIFO级别为满容量时,即管道容量选择为4个字,需要8个半字才能填充满,选择MBURST=INCR4(选择4个字节)时,发生2次数据传递,选择MBURST=INCR8(选择8个字节)时,发生1次数据传递。

MSIZE = 字

当选择FIFO级别为满容量时,即管道容量选择为4个字,需要4个才能填充满,选择MBURST=INCR4(选择4个字节)时,发生1次数据传递。

综上所述:MBUSRT的选择,需要结合MSIZE的大小来确定。

在STM32中,DMA分为两个:DMA1和DMA2

④⑤⑥、存储器接口、外设接口、编程接口

在DMA框图中,剩余3个分别时存储器接口(M接口)、外设接口(P接口)、编程接口,如下图

在这里插入图片描述

图中可以看到,DMA1控制器可以通过总线矩阵访问Flash、SRAM、和外部存储器,而DMA1的P接口不经过总线矩阵可以直接访问APB1外设;

在DMA2控制其中,可以看到M接口可以通过总线矩阵访问Flash、SRAM、APB1/2、AHB1/2/3外部存储器),而P接口也可以通过总线矩阵访问Flash、SRAM、APB1/2、AHB1/2/3外部存储器)

从上可以总结出,在DMA控制器中,DMA1可以实现:P->M,M->P,而DMA2则可以实现P->M,M->P,M->M的数据传输。(这里还是有点糊涂,反复看了好几次火哥的视频,还不是很清楚,等后面学习清楚了在做补充)

下来先看一下关于DMA的初始化结构体

/* 位于stm432f4xx_dma.h */
typedef struct
{
  uint32_t DMA_Channel;            /* 通道选择 */
  uint32_t DMA_PeripheralBaseAddr; /* 外设地址 */
  uint32_t DMA_Memory0BaseAddr;    /* 存储器0地址 */
  uint32_t DMA_DIR;                /* 传输方向 */
  uint32_t DMA_BufferSize;         /* 数据数目 */
  uint32_t DMA_PeripheralInc;      /* 外设递增 */
  uint32_t DMA_MemoryInc;          /* 存储器递增 */
  uint32_t DMA_PeripheralDataSize; /* 外设数据宽度 */
  uint32_t DMA_MemoryDataSize;     /* 存储器数据宽度 */
  uint32_t DMA_Mode;               /* 模式选择 */
  uint32_t DMA_Priority;           /* 优先级 */
  uint32_t DMA_FIFOMode;           /* FIFO模式 */
  uint32_t DMA_FIFOThreshold;      /* FIFO阈值 */
  uint32_t DMA_MemoryBurst;        /* 存储器突发传输 */
  uint32_t DMA_PeripheralBurst;    /* 外设突发传输 */  
}DMA_InitTypeDef;

DMA_Channel,用来选择DMA请求通道,可选通道为0-7,每个外设对应固定的通道。寄存器为DMA_SxCR:CHSEL[2:0]

#define DMA_Channel_0                     ((uint32_t)0x00000000)
#define DMA_Channel_1                     ((uint32_t)0x02000000)
#define DMA_Channel_2                     ((uint32_t)0x04000000)
#define DMA_Channel_3                     ((uint32_t)0x06000000)
#define DMA_Channel_4                     ((uint32_t)0x08000000)
#define DMA_Channel_5                     ((uint32_t)0x0A000000)
#define DMA_Channel_6                     ((uint32_t)0x0C000000)
#define DMA_Channel_7                     ((uint32_t)0x0E000000)

具体哪个外设选择哪个通道可以参考下面两个表:

在这里插入图片描述
在这里插入图片描述
DMA_PeripheralBaseAddr,外设地址,寄存器为:DMA_SxPAR。

DMA_Memory0BaseAddr,储存器0地址,寄存器为:DMA_SxM0AR。除了存储器0以外,还有一个存储器1,用于双缓冲区模式,用于大容量数据处理。

DMA_DIR,传输方向选择,可选外设到存储器,存储器到外设以及存储器到存储器,寄存器为:DMA_SxCR:DIR[1:0]。

#define DMA_DIR_PeripheralToMemory        ((uint32_t)0x00000000)		/* 外设到存储器 */
#define DMA_DIR_MemoryToPeripheral        ((uint32_t)0x00000040) 		/* 存储器到外设 */
#define DMA_DIR_MemoryToMemory            ((uint32_t)0x00000080)		/* 存储器到存储器 */

编程时需要用到的固件库函数

/* 初始化DMA寄存器到复位状态函数 */
void DMA_DeInit(DMA_Stream_TypeDef* DMAy_Streamx);

/* DMA初始化函数 */
void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);

/* DMA使能函数 */
void DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState);

以下是函数实现:

/* 相关宏定义,使用存储器到存储器传输必须使用DMA2 */
#define DMA_STREAM               DMA2_Stream0
#define DMA_CHANNEL              DMA_Channel_0
#define DMA_STREAM_CLK         RCC_AHB1Periph_DMA2 
#define DMA_FLAG_TCIF            DMA_FLAG_TCIF0

#define BUFFER_SIZE 32
#define TIMEOUT_MAX              10000 /* Maximum timeout value */
/* 定义aSRC_Const_Buffer数组作为DMA传输数据源,const关键字将aSRC_Const_Buffer数组变量定义为常量类型 */
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
                                    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};

/* 定义DMA传输目标寄存器 */
uint32_t aDST_Buffer[BUFFER_SIZE];
                                    
/* 简单的延时函数 */
static void Delay(__IO uint32_t nCount)
{
	for(; nCount != 0; nCount--);
}
static void M2M_DMA_Config(void)
{
    __IO uint32_t    Timeout = TIMEOUT_MAX;
    DMA_InitTypeDef DMA_InitStructure;
    //打开DMA2所在总线时钟
    RCC_AHB1PeriphClockCmd(DMA_STREAM_CLK,ENABLE);
    /* 复位初始化DMA数据流 */
    DMA_DeInit(DMA_STREAM);
    /* 确保DMA数据流复位完成 */
    while(DMA_GetCmdStatus(DMA_STREAM) != DISABLE);
    /* DMA数据流通道选择 */
    DMA_InitStructure.DMA_Channel = DMA_CHANNEL;
    /* 源数据地址 */
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;
    /* 目标地址 */
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)aDST_Buffer;
    /* 存储器到存储器模式 */
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;
    /* 数据数目 */
    DMA_InitStructure.DMA_BufferSize = (uint32_t)BUFFER_SIZE;
    /* 使能自动递增功能 */
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
    /* 使能自动递增功能 */
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    /* 源数据是字大小(32位) */
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    /* 一次传输模式,存储器到存储器模式不能使用循环传输 */
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    /* DMAD数据流优先级为高 */
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    /* 禁用FIFO模式 */
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    /* 单次模式 */
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    /* 单次模式 */
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    /* 完成DMA数据流参数配置 */
    DMA_Init(DMA_STREAM,&DMA_InitStructure);
    /* 使能DMA,开始DMA数据流传输 */
    DMA_Cmd(DMA_STREAM,ENABLE);
    /* 检测DMA数据流是否有效并带有超时检测功能 */
    Timeout = TIMEOUT_MAX;
    while ((DMA_GetCmdStatus(DMA_STREAM) != ENABLE) && (Timeout-- > 0));
    /* 判断是否超时 */
    if (Timeout == 0)
    {
        /* 超时就让程序运行下面循环:RGB彩色灯闪烁 */
        while (1)
        {      
            LED_RED;
            Delay(0xFFFFFF);
            LED_OFF;
            Delay(0xFFFFFF);
        }
    } 
}

编写数据校验函数,在传输完成后对数据进行校验。

/* 判断指定长度的两个数据源是否完全相等,如果完全相等返回1,只要其中一对数据不相等返回0 */
uint8_t Buffercmp(const uint32_t* pBuffer,uint32_t* pBuffer1, uint16_t BufferLength)
{
    /* 数据长度递减 */
    while(BufferLength--)
    {
        /* 判断两个数据源是否对应相等 */
        if(*pBuffer != *pBuffer1)
        {
            /* 对应数据源不相等马上退出函数,并返回0 */
            return 0;
        }
        /* 递增两个数据源的地址指针 */
        pBuffer++;
        pBuffer1++;
    }
    /* 完成判断并且对应数据相对 */
    return 1;  
}

在main函数中调用

int main(void)
{
    /* 定义存放比较结果变量 */
    uint8_t TransferStatus;
    LED_Config();
    DEBUG_USART1_Config();
    M2M_DMA_Config();  
    /* 等待DMA传输完成 */
    while(DMA_GetFlagStatus(DMA_STREAM,DMA_FLAG_TCIF)==DISABLE);
    /* 比较源数据与传输后数据 */
    TransferStatus=Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE);
    /* 判断源数据与传输后数据比较结果*/
    if(TransferStatus==0)  
    {
        /* 源数据与传输后数据不相等时RGB彩色灯显示红色 */
        LED_RED;
        printf("数据传输完成,但数据不相等!\n");
    }
    else
    { 
        /* 源数据与传输后数据相等时RGB彩色灯显示蓝色 */
        LED_BLUE;
        printf("数据传输完成,且数据相等!\n");
    }
    while(1)
    {
    }
}

在本节内容中,对于DMA的使用总结如下:

  1. 使能 DMA 数据流时钟并复位初始化 DMA 数据流;(只要是操作外设,第一步一定是要打开时钟!!!)
  2. 配置 DMA 数据流参数;
  3. 使能 DMA 数据流,进行传输;
  4. 等待传输完成,并对源数据和目标地址数据进行比较

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

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

相关文章

探索智慧文旅:沉浸式VR导览与个性化数字人带你畅游景区

导语: 随着科技的不断进步和智能化的兴起,智慧文旅已经成为旅游业的新趋势。在这个数字化时代,旅游体验已经不再局限于传统的观光和游玩,而是通过创新科技为游客提供了全新的旅行方式和更加丰富的体验。 在智慧文旅中&#xff0c…

Pycharm配置远程调试

原文链接 在搞深度学习的时候,我们在本地开发,但是需要在服务器去运行工程,所以需要使用Pycharm进行远程配置,可以实现本地代码自动同步到服务器,并在本地使用服务器的解释器。 条件:需要使用专业版Pycha…

【webrtc】SDP: rtcp-mux

https://w3c.github.io/webrtc-pc/ RTCRtcpMuxPolicy 是关于传输rtcp包的ice地址收集的 默认是必须开启的。 rtcpMuxPolicy of type RTCRtcpMuxPolicy, defaulting to “require”. Indicates which rtcp-mux policy to use when gathering ICE candidates.

ESXi 7.0 U3m Cisco (思科) 定制版 OEM Custom Installer CD

VMware ESXi 7.0 Update 3m - 领先的裸机 Hypervisor (All OEM Customized Installer CDs) ESXi 7.0 U3m Standard (标准版) ESXi 7.0 U3m Dell (戴尔) 定制版 OEM Custom Installer CD ESXi 7.0 U3m HPE (慧与) 定制版 OEM Custom Installer CD ESXi 7.0 U3m Lenovo (联想) 定…

centos7安装 Miniconda

Miniconda是什么 Miniconda是一个轻量级的Anaconda发行版,它是一个Python环境管理器和软件包管理器。Anaconda是一个科学计算和数据科学的Python发行版,而Miniconda是Anaconda的精简版本,只包含了基本的组件。 按照以下步骤下载和安装Minicon…

软测量技术论文

软测量技术论文篇一 软测量技术在污水处理中的应用 摘要:近年来软测量技术的研究主要是基于人工神经网络,它对非线性问题有较好的处理能力,对求解结果有较好的泛化能力,由于实际生活中严格意义上的线性系统并不多见&#xff0c…

【实现微信红包效果】前端CSS实现微信红包打开效果(附源码下载)

【写在前面】上次领了一分钱微信红包后就在想如何实现红包打开翻转效果,微信带来最大的一个里程碑就是红包功能的开发,以至于出现这个现象,一块钱掉地上都不一定有人捡,但是微信群里抢到1分钱还得说一声谢谢老板,更有甚…

Win11 RTX 4090显卡深度学习环境配置(Nvidia显卡驱动、CUDA11.7.0)

Win11 RTX 4090显卡深度学习环境配置(Nvidia显卡驱动、CUDA11.7.0) 1. 简介2. 安装Anaconda3. 安装Pycharm4. 安装CUDA11.7.04.1 安装4.2 测试4.3 CUDA卸载 5. PyTorch安装5.1 PyTorch安装5.2 安装CUDA相关软件包5.3 测试 1. 简介 PyTorch分为CPU版本和…

并查集详解及应用

文章和代码已经归档至【Github仓库:https://github.com/timerring/algorithms-notes 】或者公众号【AIShareLab】回复 算法笔记 也可获取。 文章目录 并查集优化方法 例题:合并集合code 例题:连通块中点的数量code 模板总结例题:食…

(链表) 143. 重排链表 ——【Leetcode每日一题】

❓143. 重排链表 难度:中等 给定一个单链表 L 的头节点 head ,单链表 L 表示为: L 0 L_0 L0​ → L 1 L_1 L1​ → … → L n − 1 L_{n-1} Ln−1​ → L n L_n Ln​ 请将其重新排列后变为: L 0 L_0 L0​ → L n L_n Ln​ →…

hudi系列-文件系统视图(FileSystemView)

hudi表的数据一直在演变过程中,存储在文件系统中的数据文件也在不断增加和版本迭代,hudi提供了表级别的文件系统视图(filesystem view)来简单、直观地了解表中的数据分布情况、数据文件的状态和变化,以及数据的版本控制信息。文件系统视图提供了以下一些功能: 获取最新的ba…

如何看待分层测试?

分层测试分了个寂寞? 分层测试这个风吹了好多年,不分层都不好意思说自己是专业测试。各互联网公司更是对此乐此不疲,测试架构、测试平台,搞了一套又一套,然而。。。 理想总是丰满,现实总是骨干&#xff0…

【探索 Kubernetes|集群搭建篇 系列 5】简化 Kubernetes 的部署,深入解析其工作流程

前言 大家好,我是秋意零。 在前面 4 个章节中,我们充分了解了容器技术和 Kubernes 原生时代引擎的架构和设计思想,今天分享的主要内容是,探索 Kubernetes 部署,深入解析其工作流程 👿 简介 &#x1f3e0…

OpenCV 项目开发实战--对图像进行对齐 (ECC)-附带 ( C++ / Python )代码实现

文末附基于Python和C++两种方式实现的测试代码下载链接 图 1. 左图:来自 Prokudin-Gorskii 收藏的图像。右:相同的图像,通道对齐。 左边的图片是名为Prokudin-Gorskii收藏的历史照片集的一部分。这张照片是一位俄罗斯摄影师在 1900 年代早期使用早期彩色相机拍摄的。由于相…

升级丨AMAA汽车社媒营销归因升级,提升ROI还用愁吗?

“如其抱怨,不如享受卷的过程。昨天的手机行业,就是今天的汽车行业。” “以后国内可能只剩下五个 ‘东西南北中’,打麻将一样。” 在刚结束不久的2023年中国汽车重庆论坛(CACS)上,来自车企大佬们的“声音…

【JVM】JVM 判断对象存活算法(引用计数算法与根可达性分析算法)

文章目录 引用计数算法介绍问题 根可达性分析算法介绍GC Root 在 JVM 中,需要检查出还有哪些存活对象(就是哪些对象还在使用),哪些未存活对象,未存活对象又被称之为垃圾对象,只有知道了哪种是垃圾对象&…

实战必看!工程项目中简单高效的示教编程

大家好,我是华山自控编程朱老师 示教编程是一种简单高效的方法,通过可视化界面和图像采集,可以指导编程操作,提高生产效率。 在本视频中,我们将分享一个项目,重点介绍如何进行简单高效的示教编程。 实战必…

端午节到了,用Python画一个粽子吧!

文章目录 粽子曲面真粽子曲面正弦曲面 粽子曲面 之前通过matplotlib绘制了圆锥曲面,但matplotlib绘制曲面图有几个问题,其中plot_surface需要有规范的xOy坐标,然后根据其坐标绘制z轴参数;plot_trisurf则必须有明确的三角面的顶点…

【AI人工智能】你们都是用它来干嘛?

🚀 个人主页 极客小俊 ✍🏻 作者简介:web开发者、设计师、技术分享博主 🐋 希望大家多多支持一下, 我们一起进步!😄 🏅 如果文章对你有帮助的话,欢迎评论 💬点赞&#x1…

基于Python的接口自动化测试框架

目录 前言: 项目背景 工具选择 框架思路 第三方库介绍 代码实现 不足之处 前言: Python是一种流行的编程语言,Python的易学性和易用性使得它成为编写接口自动化测试框架的理想选择。在Python中,有许多库可以帮助我们执行HTTP请求…