STM32CubeMX教程12 DMA 直接内存读取
使用STM32CubeMX软件配置STM32F407开发板上串口USART1进行DMA传输数据,然后实现与实验“STM32CubeMX教程9 USART/UART 异步通信”相同的目标
1、准备材料
开发板(正点原子stm32f407探索者开发板V2.4)
ST-LINK/V2驱动
STM32CubeMX软件(Version 6.10.0)
keil µVision5 IDE(MDK-Arm)
CH340G Windows系统驱动程序(CH341SER.EXE)
XCOM V2.6串口助手
2、实验目标
使用STM32CubeMX软件配置STM32F407开发板上串口USART1进行DMA传输数据,然后实现与实验“STM32CubeMX教程9 USART/UART 异步通信”相同的目标
3、实验流程
3.0、前提知识
直接存储器访问(DMA)是实现存储器与外设、存储器与存储器之间高效传输数据的一种方法,其拥有①从外设到存储器、②从存储器到外设和③从存储器到存储器三种传输模式,STM32F407有DMA1和DMA2两个DMA控制器,其中DMA2可以实现上述三种传输模式,DMA1仅可以实现前两种传输模式
每个DMA均有8个流(stream),每个流又有8个通道,但是每个流只能同时使用8个通道中的一个,支持DMA的外设一般可以选择8个流共64个通道中的某2个通道发起DMA请求,当多个流共同发起DMA请求时,由DMA仲裁器来决定谁先发送(软件可以配置DMA流的优先级),如下图所示为STM32F407的DMA1/2具体的外设请求映射表(注释1)
举个例子:当使用USART1_TX发起DMA请求时,USART1_TX可以选择的DMA通道只有DMA2 Stream7 CH4,因此DMA请求从DMA2 Stream7 CH4发起,然后经过仲裁器到存储器端口处取要发送的数据,然后经过FIFO(可以配置不使用),最后将数据送到外设端口USART1,上述描述如下图中黄色标注所示(注释1)
在使用DMA传输数据时一般需要设置①DMA请求和②DMA流两个主要参数,其中DMA请求就是存储器或者外设发起的传输需求,而DMA流就是进行DMA传输的数据链路(上图黄色链路)
使用某个外设的DMA传输时,一般流程为“添加外设具体的DMA请求 -> 选择该DMA请求的流Stream ->设置DMA优先级 -> 设置DMA请求模式 -> 设置地址递增(一般为外设地址不变,内存地址递增) -> 配置FIFO -> 配置传输数据的数据宽度 -> 启动DMA流全局中断 -> 在程序中以DMA方式启动外设”
先入先出(FIFO)可以想象成一个缓存区,启用FIFO之后,可以将输出暂存在该缓存区中,然后当数据量一旦达到了FIFO设置的阈值才会从缓存区中取出来发送出去,当DMA传输的源和目标的数据宽度不同时,FIFO将变得非常有用(可以在FIFO中改变传输数据的宽度)
3.1、CubeMX相关配置
请先阅读“STM32CubeMX教程1 工程建立”实验3.4.1小节配置RCC和SYS
3.1.1、时钟树配置
系统时钟树配置均设置为STM32F407总线能达到的最高时钟频率,无需使用LSE,具体如下图所示
3.1.2、外设参数配置
具体配置如下图所示,下面对每个序号配置做详细说明
- 在Pinout & Configuration页面左边功能分类栏目Connectivity中单击其中USART1,在页面中间USART1 Mode and Configuration中将串口模式设置为异步通信工作模式,无硬件流控制,然后在Configuration页面中设置USART1的相关参数,具体的USART1基本参数配置与实验“STM32CubeMX USART/UART异步通信”相同,在页面下方的参数配置栏上面会有很多标签,当前处于的位置为参数设置
- 单击DMA Settings进入USART1的DMA配置页面,支持DMA功能的外设在这里都会有DMA Settings页面
- 单击ADD按键来增加外设的DMA,如果有可以使用的DMA流则会以列表形式供用户选择,这里可以选择发送TX和接收RX两个串口的DMA请求
- 选择可用的DMA流,DMA的方向会根据用户选择的DMA请求自动推断,一般只有一个选项,接着选择DMA流的优先级(注意与DMA中断优先级区分)
- 设置USART_RX的DMA模式为循环模式,并设置地址递增,由于外设USART1的地址固定,因此不需要地址递增,而接收的数据存储到内存里需要地址递增的存储
- 下方左侧为配置是否使用FIFO,如果使用则需要配置阈值,右边配置外设和内存的数据宽度,由于串口传输数据是以字节方式传输,存储也是,因此这里均选择Byte,下方为突发传输设置,当不使用FIFO时只能为单次传输,而当使用FIFO时,突发传输可以配置为4、8或16增量突发传输
3.1.3、外设中断配置
在Pinout & Configuration页面左边System Core/NVIC中勾选USART1和DMA2 stream2/7全局中断,然后选择合适的中断优先级即可,步骤如下图所示
3.2、生成代码
请先阅读“STM32CubeMX教程1 工程建立”实验3.4.3小节配置Project Manager
单击页面右上角GENERATE CODE生成工程
3.2.1、外设初始化调用流程
在生成的工程主函数main()中调用MX_DMA_Init()函数对USART1 RX/TX用到的DMA时钟及其流的中断进行了配置
在MX_USART1_UART_Init()函数中相比较于“STM32CubeMX教程9 USART/UART 异步通信”实验增加了USART1 RX/TX DMA相关的参数配置及初始化
3.2.2、外设中断调用流程
勾选DMA2 stream2/7全局中断后,会在生成的工程文件stm32f4xx_it.c中新增全局中断函数DMA2_Stream2_IRQHandler()和DMA2_Stream7_IRQHandler()
这两个中断服务函数均调用了HAL库的DMA中断统一管理函数HAL_DMA_IRQHandler(),该函数中根据各种标志判断DMA传输完成/失败/一半完成等事件,然后根据不同的事件调用不同的回调函数,这里DMA传输完成之后调用了hdma->->XferCpltCal1back(),这是个函数指针
上述过程如下图所示
这个函数指针在以DMA方式启动USART1发送或者接收数据时被指向DMA传输完成回调UART_DMATransmitCpl()函数
在该DMA传输完成回调UART_DMATransmitCplt()函数中最终调用了USART1_TX传输完成回调HAL_UART_TxCpltCallback()函数,该函数在串口实验中我们重新实现过,也就是说DMA最终的传输完成中断回调函数使用了外设的中断回调函数
其他的中断事件回调流程类似,上述过程如下图所示
3.2.3、添加其他必要代码
本实验与“STM32CubeMX教程9 USART/UART 异步通信”实验代码一致,除以下三个方面需要修改
①将主函数开始的以中断方式启动的串口接收函数修改为以DMA方式启动的HAL_UART_Receive_DMA函数
②主循环中将以中断方式启动的串口发送函数修改为以DMA方式启动的HAL_UART_Transmit_DMA函数
③删除空闲回调函数中的再次启动串口接收函数HAL_UART_Receive_IT(huart, rxBuffer, RX_CMD_LEN)
其他所有代码无需修改,具体修改后的代码如下所示
为什么删除再次启动串口接收中断的函数?
在串口章节的实验中,我们提到当以中断方式启动串口接收之后,串口接收完毕一次就不能自动接收第二次了,用户必须手动再次调用中断接收函数才可以重新接收,因此我们才在串口空闲回调函数里重新启用,以此来实现无限次的自动接收
但是在本实验中,我们已经配置了串口USART1的DMA接收模式为循环模式,在该模式下其接收完毕一次之后能够继续接收,因此不需要我们在空闲回调函数中重新启用
如果将串口USART1的DMA发送模式配置为循环模式,当按下按键触发时,会出现一直发送的情况,读者可以尝试尝试,这显然不符合我们的预想,因此串口USART1的DMA发送模式配置为了普通模式
4、常用函数
/*以DMA方式启动USART接收*/
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
/*以DMA方式启动USART传输*/
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)
5、烧录验证
5.1、具体步骤
“使用‘STM32CubeMX STM32F4 HAL库 USART/UART异步通信’实验的STM32CubeMX工程 -> 增加配置USART1的DMA相关设置 -> NVIC中启动DMA的全局中断并选择合适的中断优先级 -> 在生成的代码上进行3处修改(修改内容参看3.2小节)”
5.2、实验现象
实验现象与“STM32CubeMX教程9 USART/UART 异步通信”实验现象一致
6、注释详解
注释1:图片来自STM32F4xx中文参考手册 RM0090
参考资料
更多内容请浏览 OSnotes的CSDN博客
STM32CubeMX教程12 DMA 直接内存读取的更多相关文章
- 深度学习与CV教程(12) | 目标检测 (两阶段,R-CNN系列)
作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/37 本文地址:http://www.showmeai.tech/article-det ...
- NODE-WEBKIT教程(12)全屏
node-webkit教程(12)全屏 文/玄魂 目录 node-webkit教程(12)全屏 前言 12.1 和全屏有关的三个api Window.enterFullscreen() Window ...
- TLB与内存寻址,内存读取,虚拟内存的相关原理
TLB(Translation Lookaside Buffer)转换检测缓冲区是一个内存管理单元,用于改进虚拟地址到物理地址转换速度的缓存. TLB是一个小的,虚拟寻址的缓存,其中每一行都保存着一个 ...
- 深入理解java虚拟机-第12章Java内存模型与线程
第12章 Java内存模型与线程 Java内存模型 主内存与工作内存: java内存模型规定了所有的变量都在主内存中,每条线程还有自己的工作内存. 工作内存中保存了该线程使用的主内存副本拷贝,线程对 ...
- PE文件从文件加载到内存,再从内存读取,然后存盘到文件
// mem.cpp : 定义控制台应用程序的入口点. //PE文件从文件加载到内存,再从内存读取,然后存盘到文件 #include "stdafx.h" #include < ...
- ffmpeg从内存读取文件
正常情况,ffmpeg直接从文件读取 AVFormatContext * _ctx = NULL; avformat_open_input(&_ctx, _filePath, 0, 0); 我 ...
- [译]Vulkan教程(12)图形管道基础之入门
[译]Vulkan教程(12)图形管道基础之入门 Introduction 入门 Over the course of the next few chapters we'll be setting u ...
- [转帖]Linux教程(12)- linux输入输出重定向
Linux教程(12)- linux输入输出重定向 2018-08-21 22:57:02 钱婷婷 阅读数 49更多 分类专栏: Linux教程与操作 Linux教程与使用 版权声明:本文为博主原 ...
- 黑马lavarel教程---12、lavarel验证码
黑马lavarel教程---12.lavarel验证码 一.总结 一句话总结: 用插件的时候仔细看插件的版本要求 1.lavarel安装验证码插件的时候,如果(可选)需要定义自己的配置,则需要生成配置 ...
- Directx11教程(12) 禁止alt+enter全屏窗口
原文:Directx11教程(12) 禁止alt+enter全屏窗口 在D3D11应用程序中,我们按下alt+enter键,会切换到全屏模式.有时候,我们在WM_SIZE中有一些代码,全 ...
随机推荐
- PDFPlumber使用入门
目录 背景 教程开始 应用场景 安装 命令行使用 可选参数 Python包 简单样例 读取PDF pdfplumber.PDF类 pdfplumber.Page类 对象(Object) chars / ...
- Arduino 板的说明
Arduino 板的说明 在本章中,我们将了解 Arduino 板上的不同组件.将学习 Arduino UNO 板,因为它是 Arduino 板系列中最受欢迎的.此外,它是开始使用电子和编码的最佳板. ...
- mooc第五单元《管理组织》单元测试
第五单元<管理组织>单元测试 返回 本次得分为:30.00/50.00, 本次测试的提交时间为:2020-08-30, 如果你认为本次测试成绩不理想,你可以选择 再做一次 . 1 ...
- 【IOC,AOP】spring的基础概念
IOC 控制反转 对象的创建控制权转交给外部实体,就是控制反转.外部实体便是IOC容器.其实就是以前创建java对象都是我们new一下,现在我们可以把这个new交给IOC容器来做,new出来的对象也会 ...
- CF1676G
题目简化和分析: 求一颗子树的黑白两数是否相等. 我们设黑 \(1\),白 \(-1\),若某一棵子树的权值为 \(0\),说明此刻的黑白个数相等,贡献加一. 从根搜索,每次将值传递给父亲,判断父亲此 ...
- Zuul 2.1.5 设计分析
前言 https://github.com/Netflix/zuul zuul 是 SpringCloud 家族老兵,使用 Java 微服务大部分都在使用 zuul 作为网关.既然他如此重要,那么我们 ...
- MySQL索引、事务与存储引擎
MySQL索引.事务与存储引擎 索引介绍 1.索引的概念 索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址(类似于C语言的链表通过指针指向数据记录的内存地址). 使用 ...
- AGC 补题笔记
[AGC001] A.BBQ Easy 由于最大数肯定要和一个比自己小的数搭配保留该数,不如选择保留次大数,如此递归即解.因此将序列排序后输出序号为奇数的数即可. B.Mysterious Light ...
- springboot项目在docker中运行
前端时间需要把项目打包到docker中运行,于是就让组员去探索,最后整个过程是这样的. 首先我们做java开发,一般都是使用springboot开发,开发完成,我们需要把springboot项目打包成 ...
- Java 中 field 和 variable 区别及相关术语解释(转)
https://www.jianshu.com/p/08e2d85d3ce9 这是一个以前从没仔细想过的问题--最近在阅读Java Puzzlers,发现其大量使用了"域"这个词, ...