13 直接存储器访问DMA(基于STM32HAL库)

news2024/11/26 20:38:10

目录

DMA-直接存储器访问控制器

DMA概览

DMA的作用

DMA框图

DMA外设要点概括

DMA功能对比

STMF10x DMA具体内容

DMA主要特性

DMA中断

DMA请求映像

DMA的使用步骤

HAL库中的DMA功能实例

句柄结构体介绍(以DMA为例)

 外设初始化结构体介绍

具体配置流程

CubeMX配置DMA过程

MEMTOMEM

P TO M

M TO P

生成工程讲解

DMA具体实现功能讲解

存储器到存储器

实验现象

存储器到外设

实验现象


DMA-直接存储器访问控制器

DMA概览

DMA的作用

  • 直接存储器访问 (DMA) 用于在外设与存储器之间以及存储器与存储器之间,提供高速数据传输。可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。这样节省的 CPU 资源可供其它操作使用。
  • 外设到存储器
    • 比如我们将串口接收到的数据,由串口外设的数据寄存器直接将数据通过DMA搬运到FLASH中
  • 外设到存储器
    • 比如我们将Flash中存储的数据通过DMA搬运到串口外设的数据寄存器,如何直接发送个上位机
  • 存储器到存储器,比如将FLASH的数据通过DMA搬运到SRAM

-总结:DMA的就是CPU的助手、数据搬运工。

DMA框图

 

DMA外设要点概括

对于DMA这个器件,它的功能就是建立起一个数据传输通道。

DMA功能对比

STMF10x DMA具体内容

DMA主要特性

● 12个独立的可配置的通道(请求): DMA1有7个通道, DMA2有5个通道

● 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。

● 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。

● 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。

● 支持循环的缓冲器管理

● 每个通道都有3个事件标志(DMA半传输、 DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。

● 存储器和存储器间的传输

● 外设和存储器、存储器和外设之间的传输

● 闪存、 SRAM、外设的SRAM、 APB1、 APB2和AHB外设均可作为访问的源和目标。

● 可编程的数据传输数目:最大为65535

DMA中断

每个DMA通道都可以在DMA传输过半、传输完成和传输错误时产生中断。为应用的灵活性考 虑,通过设置寄存器的不同位来打开这些中断。

DMA请求映像

DMA1控制器

从外设(TIMx[x=1、 2、 3、 4]、 ADC1、 SPI1、 SPI/I2S2、 I2Cx[x=1、 2]和USARTx[x=1、 2、 3]) 产生的7个请求,通过逻辑或输入到DMA1控制器,这意味着同时只能有一个请求有效。参见下 图的DMA1请求映像。

外设的DMA请求,可以通过设置相应外设寄存器中的控制位,被独立地开启或关闭。

DMA控制器(DMA)

DMA2控制器

从外设(TIMx[5、 6、 7、 8]、 ADC3、 SPI/I2S3、 UART4、 DAC通道1、 2和SDIO)产生的5个请 求,经逻辑或输入到DMA2控制器,这意味着同时只能有一个请求有效。参见下图的DMA2请求 映像。

外设的DMA请求,可以通过设置相应外设寄存器中的DMA控制位,被独立地开启或关闭。

注意: DMA2控制器及相关请求仅存在于大容量产品和互联型产品中。

DMA控制器(DMA)

DMA的使用步骤

1、建立(选取)传输通道。

        存储器->存储器

        外设->存储器

        存储器->外设

2、确定传输对象。

     具体的功能,比如:

        UART(源)->内存(目标)

        内存数据(源)->UART(目标)

3、敲定传输细节。

        确定由谁来产生DMA请求,外设的DMA请求对应通道

        通道优先级

        确定传输数据双方的数据格式

        确定数据是否需要一直采集(循环模式是否使能)

        是否需要传输标志\中断

HAL库中的DMA功能实例

  • DMA句柄结构体:

        -DMA_HandleTypeDef

  • DMA初始化结构体:

        -DMA_InitTypeDef

HAL库中外设驱动的实现(任意外设通用):

1、句柄结构体(DMA_HandleTypeDef):

Instance:它指向了外设内,一个具体的外设成员。如:ADC里的ADC1、ADC2,UART里的UART1、UART2,DMA的Channel1、Channel2,实际上它用指针指向一个外设基地址。

Init:指向了一个具体外设的初始化结构体,用来配置外设的工作参数。

-大家可以理解为句柄结构体就是HAL库的一种封装形式,也是通过句柄结构体实现对外设的初始化。

2、初始化结构体(xx_InitTypeDef):

根据外设的各种配置寄存器,组织起来的外设参数配置结构体,内附在xx_HandleTypeDef结构体中。

句柄结构体介绍(以DMA为例)

 

DMA_HandleTypeDef结构体里有两个最主要的成员

第一个是Instance,它指向了一个具体的外设基地址,当我们使用句柄结构体来进行具体初始化的时候,它就可以通过这个来指向一个具体外设的寄存器来进行我们的外设初始化。

第二个是Init(初始化结构体),它是用于配置特定外设的参数配置的,如串口的波特率、数据位数等等

第三个成员Lock(锁)和第四个成员(State)一般很少用,这里不做讲解。

而后面的几个函数都是外设的回调函数(一般在中断中配合外设标志位来使用):

第一个函数用于传输完成。由于是函数指针,因此需要我们自己定义函数,然后进行传参。

第二个函数用于传输过半。

第三个参数用于传输出错。

第四个参数用于传输中止。

回调函数可以这样理解:比如我们调用了hal库的一个中断处理函数,这个中断处理函数是在中断中进行的,然后这个中断处理函数就会接受一个参数(句柄结构体),通过结构体里的函数指针就可以回调我们在这个结构体里给它定义的回调函数,其实这个回调就是执行的意思。

最后的三个成员ErrorCode、DmaBaseAddress、ChannelIndex的具体一成员信息都是和外设相关的,也一般很少使用,具体我们可以查看参考手册来进行了解。

 外设初始化结构体介绍

DMA_InitTypeDef,它是根据DMA外设的各种配置寄存器,组织起来的外设参数配置结构体,是内附在DMA_HandleTypeDef句柄结构体里的

具体配置流程

首先初始化句柄结构体,将Instance成员指向一个具体的外设,它是DMA_Channel_TypeDef类型的,通过搜索可以发现,它对应着DMA1_Channel1到DMA1_Channel7,和DMA2_Channel1到DMA2_Channel5。通过指向一个具体的外设通道,从而建立起对应的外设请求。

然后配置DMA的工作模式,通过配置Init(初始化结构体)

配置第一个成员Direction,可以通过ctrl+f搜索到具体配置的传输方向

第二个成员PeriphInc和第三个成员MemInc

配置是否使能递增模式(分别对应外设和内存),如果我们要搬运的数据存储在一块连续字节的内存内,我们就需要采用递增才能将数据完整搬运到另一块内存中,它会自动发送完第一个内存地址的数据,然后进行内存地址的偏移,继续进行下一个地址内容的发送。对于寄存器而言我们也可以理解为一块内存。

第五、第六个成员PeriphDataAlignment和MemDataAlignment分别对应配置外设和内存的传输的数据格式

我们以串口为例,串口的数据寄存器只有8bit,我们要选择DMA_PDATAALIGN_BYTE。而对于内存,如果我们使用了一个int型的话,那么就对应着32bit,我们就要选择DMA_MDATAALIGN_WORD

第七个成员Mode,我们可以选择循环模式还是单次传输模式。

最后一个成员Priority,我们可以配置DMA的优先级

具体我们可以看DMA的仲裁器可以知道

总结起来就是hal库正是通过这种句柄结构体+初始化结构体的方式将外设很好地封装起来并且组织好了

配置完所有参数我们要使用HAL_DMA_Init传入句柄结构体,从而完成DMA外设的初始化

CubeMX配置DMA过程

MEMTOMEM

其中有一点需要补充注意的是,是否递增的选择:要看源数据存储是否在一块连续地址上,目标数据是否也是连续的地址字节,即是否大于一个字节。

数据的位数宽度可参考

P TO M

以串口1为例,USART1_RX用于从外面读取数据,因此DMA请求方向是外设到内存,通道固定是DMA1 Channel5,模式选择Normal,循环模式一般在DAC才选用,优先级选择低,数据位数宽度最好根据串口数据寄存器位数选择为Byte,一般CubeMX会自动根据外设寄存器选择好

M TO P

USART1_TX用于向外发送数据,因此DMA请求方向是内存到外设,通道固定是DMA1 Channel4,模式选择Normal,循环模式一般在DAC才选用,优先级选择低,数据位数宽度最好根据串口数据寄存器位数选择为Byte

最终可以在DMA1和DMA2看见对应通道的配置

生成工程讲解

生成工程,在dma.c中可以看到从DMA句柄结构体的定义来配置我们的M TO M,以及DMA外设通道4和5在中断向量表的优先级,CubeMX都自动配置好了

由于DMA的外设到内存和内存到外设是和我们的外设紧密联系的,所以工程自动将DMA配置的这一部分代码放在了外设文件里。

再看usart.c中串口的配置。首先定义了串口句柄结构体和两个DMA句柄结构体(分别是发送和接收)。接着进行了串口参数的初始化和串口用到的GPIO配置

然后初始化了我们串口外设到内存的DMA句柄结构体 并 紧接着调用了__HAL_LINKDMA()函数,将我们的串口外设和DMA建立链接在一起。

以TX为例,__HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);函数其中第一个参数是串口句柄结构体,第二个参数是串口句柄结构体中的DMA句柄指针成员hdmatx,第三个是我们已经实例化了的DMA句柄结构体。所以__HAL_LINKDMA函数的作用是将串口句柄结构体中的DMA句柄指针hdmatx赋值具体实例化为hdma_usart1_tx,从而关联为实际的DMA功能。

通过查看stm32fxx_hal_dma.c文件的指导说明,我们了解到DMA的具体用法。如

HAL_DMA_Start()可以开启DMA传输

HAL_DMA_PollForTransfer()可以对DMA的传输结束状态进行轮询

HAL_DMA_Start_IT()可以开启DMA传输中断

HAL_DMA_IRQHandler()为具体的DMA中断函数

最后还有一些DMA使能、检测标志函数的相关说明

DMA具体实现功能讲解

存储器到存储器

首先定义一个无符号整型的常量数组并初始化(存储在FLASH中),如何定义一共普通的无符号数组,两个数组成员个数都是32。

然后再main函数中进行相关的初始化配置,再使用DMA时,必须调用

HAL_DMA_Start(&DMA_Handle,(uint32_t)aSRC_Const_Buffer,(uint32_t)aDST_Buffer,BUFFER_SIZE);

函数来软件启动DMA(存储器到存储器必须软件开启),四个参数分别为DMA的句柄结构体、源地址、目标地址和传输数据大小

当传输完数据之后,我们可以通过调用__HAL_DMA_GET_FLAG(&DMA_Handle,DMA_FLAG_TC6)函数来判断DMA传输的状态

最后烧入程序,通过Debug进行调试

实验现象

存储器到外设

首先初始化串口和DMA,具体注意DMA的配置(与前面CubeMX一致)

然后填充要发送的数据,接着调用HAL_UART_Transmit_DMA(&UartHandle, (uint8_t *)SendBuff ,SENDBUFF_SIZE);函数,第一个参数为串口句柄结构体、第二个参数为发送缓冲区、第三个参数为发送缓冲区的大小。

通过这个函数来使用串口1的DMA发送功能,这样就可以进行DMA的传输了

具体进入HAL_UART_Transmit_DMA函数,可以发现其中调用了一些UART DAM句柄结构体中的回调函数,最终通过HAL_DMA_Start_IT()函数传输数据到串口数据寄存器中发送出去

进入HAL_DMA_Start_IT()中可以找到DMA的使能__HAL_DMA_ENABLE(hdma);

实验现象

DMA一般模式

DMA循环模式

我们可以发现DAM在一直不停得传输。在传输的过程中,CPU可以做自己的事情,如进行LED灯的闪烁显示等

 

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

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

相关文章

多元回归预测 | Matlab基于粒子群算法优化深度置信网络(PSO-DBN)的数据回归预测,matlab代码回归预测,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab基于基于粒子群算法优化深度置信网络(PSO-DBN)的数据回归预测,matlab代码回归预测,多变量输入模型,多变量输入模型 评价

聊聊不同集群的微服务如何通过feign调用

前言 之前业务部门的某项目微服务调用关系如下图 后因业务改造需要,该项目需要将服务A部署到另外一个集群,但服务A仍然需要能调用到服务B,调用关系如下图 之前调用方式是负责服务B的开发团队提供相应的feign客户端包给到服务A开发团队&…

三种Linux内核代码在线阅读工具

记录一下 1 . 可在线阅读uboot,kernel,busybox(rootfs),可搜索字符串,函数跳 https://lxr.missinglinkelectronics.com/ 界面如下: 2. 显示界面跟代码编辑器很像,同样可以函数跳转 https://elixir.boot…

如何让罗技29方向盘像视频中的那样转动起来?

​​​​​​​[vlog]Autoware Carla G29 自动驾驶仿真_哔哩哔哩_bilibili 话接上文,在我之前一篇博客中已经讲解了如何给罗技29方向盘装上力反馈,也就是在拨动方向盘的时候感觉有一个力组织你过度的拨动方向盘,其实它真正的用处是用于实现对…

【Web3】认识区块链

目录 区块链特征 区块链类型 区块链的概念 区块链特征 去中心化:区块链是由一个分布在多个参与者之间的网络组成,没有中央机构或中介控制整个系统。所有参与者共同维护和验证账本的完整性,减少了单点故障和集中式控制的风险。共识机制&…

【HTTPS】采用的加密策略, 什么是中间人攻击? 什么是证书?

文章目录 前言一、认识 HTTPS 协议1, 对称加密2, 非对称加密 二、HTTPS 加密策略1, 只采用对称加密 : 不安全2, 引入非对称加密3, 中间人攻击之偷梁换柱4, 引入证书4.1 什么是证书4.2, 证书如何能解决"中间人攻击" 总结 前言 各位读者好, 我是小陈, 这是我的个人主页…

日历与时钟

目录 公历 黑色星期五 生物韵律 公历 在公历中,当年份为4的整数倍,但不是100的整数倍时,会出现闰年的现象。 y40 mod(y,4) 0 && mod(y,100)||mod(y,400)0 输出当时的年、月、日、时、分、秒 f%6d %6d %6d %6d %6d %9.3f\n cclock …

MySQL学习基础篇(八)---聚合函数

MySQL学习基础篇(八)—聚合函数 聚合(或聚集、分组)函数,它是对一组数据进行汇总的函数,输入的是一组数据的集合,输出的是单个值。 1. 聚合函数介绍 什么是聚合函数:聚合函数作用于一组数据,…

前端实战——尚品汇(网页开发)

/* 基础设置 */ .container {width: 1190px;margin: 0 auto; } /* #region顶部导航条start */ .topbar {height: 30px;background-color: #ececec; } .welcome {height: 30px;line-height: 30px;font-size: 0;color: #666; } .welcome span,.welcome a {font-size: 12px; } .we…

AIGC - Stable Diffusion WebUI 图像生成工具的环境配置

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/131528224 Stable Diffusion WebUI 是一款基于深度学习的图像生成工具,根据用户的输入文本或图像,生成高质量的新图像&…

关于VMware虚拟空间的创建、Linux系统的安装

文章目录 前言一、Windows用户安装VMware软件1.1 下载VMware1.2 正常安装VMware后,该软件是要收费的,但是下面的链接可以让你使用很久 二、Mac用户安装VMware软件2.1 下载macOS版本:VMware Fusion2.2 正常安装VMware后,该软件是要…

「2024」预备研究生mem-形式逻辑强化:推矛盾

一、推矛盾 易错题:重点 重点: 二、课后题 三、每日一练

新版本vscode使用配置文件功能,解决不同项目使用不同的插件

如果你同时有vue2,vue3的项目。一定会遇到插件的问题。因为vue2项目插件是使用vetur的,vue3是使用volar的。 以前vscode为了在不同项目中能使用不同的配置文件,是使用工作区的概念去解决的,但是比较复杂而且不好用。 现在新版本的vscode&…

【温故而知新】Android架构模式

Android项目工程中常用的架构模式有MVC, MVP, MVVM以及现在新出的MVI。 下面一起温故而知新。 MVC MVC(Model-View-Controller)是一种在Android应用程序中使用的架构模式,用于实现松耦合、可测试和可维护的应用程序。 MVC架构模式包括三个…

高级篇十六、多版本并发控制(重要)

目录 1、什么是MVCC2、快照读与当前读2.1 快照读2.2 当前读 3、复习3.1 隔离级别3.2 隐藏字段、Undo Log版本链 4、MVCC实现原理之ReadView4.1 什么是ReadView? 1、什么是MVCC MVCC (Multiversion Concurrency Control),多版本并…

Django之ORM的锁,开启事务,Ajax

一、行锁 select_for_update(nowaitFalse, skip_lockedFalse) 注意必须用在事务里面,至于如何开启事务,我们看下面的事务一节 Book.objects.select_for_update().filter(nid3) # 锁住nid3的行select_for_update中的两个参数了解即可,因为在…

机器学习入门

AI人工智能 ANI 弱人工智能,狭义人工智能,指的是一种针对特定任务或领域进行优化的人工智能,例如语音识别、图像识别、自然语言处理、推荐系统 AGI 通用人工智能,强人工智能, ASI 超级人工智能,超人工智…

Erupt框架学习

Erupt框架学习 Erupt框架Erupt简介学习EruptEruptFieldErupt的逻辑删除Erupt的自定义按钮多数据源配置 Erupt框架 Erupt简介 最近因为工作所以接触到了一个低代码框架Erupt。这是一个通用的配置管理框架,主打就是零前端代码,急速开发通用管理框架。 Er…

C# 如何调用python,避免重复造轮子

文章目录 原因资源调用python文件需求解决方案1、C#里面运行python引入python文件,再调用其中的方法启动python脚本,监听返回值改造一下,可以入参的python调用查看是否等待python运行完成之后再运行C#如果参数比较复杂 开一个python网络后端 …