STM32—DMA
什么是DMA?
DMA(Direct Memory Access,直接存储器访问)
提供在外设与内存、存储器和存储器、外设与外设之间的高速数据传输使用。它允许不同速度的硬件装置来沟通,而不需要依赖于CPU,在这个时间中,CPU对于内存的工作来说就无法使用。
简单描述:就是一个数据搬运工!!
DMA的意义
代替 CPU 搬运数据,为 CPU 减负。
- 数据搬运的工作比较耗时间;
- 数据搬运工作时效要求高(有数据来就要搬走);
- 没啥技术含量(CPU 节约出来的时间可以处理更重要的事)。

搬运什么数据?
这里的外设指的是spi、usart、iic、adc 等基于APB1
、APB2或AHB时钟的外设,而这里的存储器包括自身的闪存(flash)或者内存(SRAM)以及外设的存储设备都可以作为访问地源或者目的。
三种搬运方式:
- 存储器→存储器(例如:复制某特别大的数据buf)
- 存储器→外设 (例如:将某数据buf写入串口TDR寄存器)
- 外设→存储器 (例如:将串口RDR寄存器写入某数据buf)
存储器→存储器
存储器→外设

外设→存储器

DMA 控制器
STM32F103有2个 DMA 控制器,DMA1有7个通道,DMA2有5个通道。
一个通道每次只能搬运一个外设的数据!! 如果同时有多个外设的 DMA 请求,则按照优先级进行响应。
DMA1有7个通道:每个通道都有其能够搬运的外设

DMA2有5个通道
DMA及通道的优先级
优先级管理采用软件+硬件:
-
软件: 每个通道的优先级可以在DMA_CCRx寄存器中设置,有4个等级 最高级>高级>中级>低级
-
硬件: 如果2个请求,它们的软件优先级相同,则较低编号的通道比较高编号的通道有较高的优先权。 比如:如果软件优先级相同,通道2优先于通道4
DMA传输方式
- DMA_Mode_Normal(正常模式)
一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次 - DMA_Mode_Circular(循环传输模式)
当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输模式
指针递增模式
外设和存储器指针在每次传输后可以自动向后递增或保持常量。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值。


实验一、内存到内存搬运
实验要求
使用DMA的方式将数组A的内容复制到数组B中,搬运完之后将数组B的内容打印到屏幕。
CubeMX配置

重定向 printf 的话记得将下面这个勾打开:

用到的库函数
- HAL_DMA_Start
> HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
参数一:DMA_HandleTypeDef *hdma,DMA通道句柄
参数二:uint32_t SrcAddress,源内存地址
参数三:uint32_t DstAddress,目标内存地址
参数四:uint32_t DataLength,传输数据长度。注意:需要乘以sizeof(uint32_t)返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
- __HAL_DMA_GET_FLAG
#define __HAL_DMA_GET_FLAG(__HANDLE__, __FLAG__) (DMA1->ISR & (__FLAG__))
参数一:HANDLE,DMA通道句柄
参数二:FLAG,数据传输标志。DMA_FLAG_TCx表示数据传输完成标志
返回值:FLAG的值(SET/RESET)
代码实现
- 开启数据传输
- 等待数据传输完成
- 打印数组内容
#define BUF_SIZE 16// 源数组uint32_t srcBuf[BUF_SIZE] = {0x00000000,0x11111111,0x22222222,0x33333333,0x44444444,0x55555555,0x66666666,0x77777777,0x88888888,0x99999999,0xAAAAAAAA,0xBBBBBBBB,0xCCCCCCCC,0xDDDDDDDD,0xEEEEEEEE,0xFFFFFFFF};// 目标数组uint32_t desBuf[BUF_SIZE];int fputc(int ch, FILE *f)
{ unsigned char temp[1]={ch};HAL_UART_Transmit(&huart1,temp,1,0xffff); return ch;
}main函数里:// 开启数据传输HAL_DMA_Start(&hdma_memtomem_dma1_channel1, (uint32_t)srcBuf, (uint32_t)desBuf, sizeof(uint32_t) * BUF_SIZE);// 等待数据传输完成while(__HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1, DMA_FLAG_TC1) == RESET);// 打印数组内容for (i = 0; i < BUF_SIZE; i++)printf("Buf[%d] = %X\r\n", i, desBuf[i]);
实验二、内存到外设搬运
实验要求:
使用DMA的方式将内存数据搬运到串口1发送寄存器,同时闪烁LED1。
CubeMX配置
DMA配置

用到的库函数
HAL_UART_Transmit_DMA
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
参数一:UART_HandleTypeDef *huart,串口句柄
参数二:uint8_t *pData,待发送数据首地址
参数三:uint16_t Size,待发送数据长度
返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
代码实现
- 准备数据
- 将数据通过串口DMA发送
#define BUF_SIZE 1000// 待发送的数据unsigned char sendBuf[BUF_SIZE];main函数里// 准备数据for (i = 0; i < BUF_SIZE; i++)sendBuf[i] = 'A';// 将数据通过串口DMA发送HAL_UART_Transmit_DMA(&huart1, sendBuf, BUF_SIZE);while (1)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);HAL_Delay(100);
}
实验三、外设到内存搬运
实验要求
使用DMA的方式将串口接收缓存寄存器的值搬运到内存中,同时闪烁LED1。
CubeMX配置
DMA配置:

串口中断配置

用到的库函数
- __HAL_UART_ENABLE
#define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U)
== UART_CR1_REG_INDEX)? ((__HANDLE__)->Instance->CR1 |= ((__INTERRUPT__) &
UART_IT_MASK)): \
(((__INTERRUPT__) >> 28U) == UART_CR2_REG_INDEX)? ((__HANDLE__)->Instance->CR2 |= ((__INTERRUPT__) &
UART_IT_MASK)): \
((__HANDLE__)->Instance->CR3 |=((__INTERRUPT__) & UART_IT_MASK)))
参数一:HANDLE,串口句柄
参数二:INTERRUPT,需要使能的中断
返回值:无
- HAL_UART_Receive_DMA
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
参数一:UART_HandleTypeDef *huart,串口句柄
参数二:uint8_t *pData,接收缓存首地址
参数三:uint16_t Size,接收缓存长度
返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
- __HAL_UART_GET_FLAG
#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR &
(__FLAG__)) == (__FLAG__))
参数一:HANDLE,串口句柄
参数二:FLAG,需要查看的FLAG返回值:FLAG的值
- __HAL_UART_CLEAR_IDLEFLAG
#define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
参数一:HANDLE,串口句柄
返回值:无
- HAL_UART_DMAStop
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)
参数一:UART_HandleTypeDef *huart,串口句柄
返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
- __HAL_DMA_GET_COUNTER
#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR)
参数一:HANDLE,串口句柄
返回值:未传输数据大小
代码实现
如何判断串口接收是否完成?如何知道串口收到数据的长度?
使用串口空闲中断(IDLE)!
- 串口空闲时,触发空闲中断;
- 空闲中断标志位由硬件置1,软件清零
利用串口空闲中断,可以用如下流程实现DMA控制的任意长数据接收:
使能IDLE空闲中断;
使能DMA接收中断;
收到串口接收中断,DMA不断传输数据到缓冲区;
一帧数据接收完毕,串口暂时空闲,触发串口空闲中断;
在中断服务函数中,清除中断标志位,关闭DMA传输(防止干扰);
计算刚才收到了多少个字节的数据。
处理缓冲区数据,开启DMA传输,开始下一帧接收。
有三个文件需要修改:
main.c
uint8_t rcvBuf[BUF_SIZE]; // 接收数据缓存数组uint8_t rcvLen = 0; // 接收一帧数据的长度__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能IDLE空闲中断HAL_UART_Receive_DMA(&huart1,rcvBuf,100); // 使能DMA接收中断while (1)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);HAL_Delay(300);
}
main.h
#define BUF_SIZE 100
stm32f1xx_it.c
extern uint8_t rcvBuf[BUF_SIZE];extern uint8_t rcvLen;void USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 *//* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);/* USER CODE BEGIN USART1_IRQn 1 */if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) == SET)) // 判断IDLE标志位是否被置位{ __HAL_UART_CLEAR_IDLEFLAG(&huart1);// 清除标志位HAL_UART_DMAStop(&huart1); // 停止DMA传输,防止干扰uint8_t temp=__HAL_DMA_GET_COUNTER(&hdma_usart1_rx); rcvLen = BUF_SIZE - temp; //计算数据长度HAL_UART_Transmit_DMA(&huart1, rcvBuf, rcvLen);//发送数据HAL_UART_Receive_DMA(&huart1, rcvBuf, BUF_SIZE);//开启DMA}/* USER CODE END USART1_IRQn 1 */}
相关文章:
STM32—DMA
什么是DMA? DMA(Direct Memory Access,直接存储器访问) 提供在外设与内存、存储器和存储器、外设与外设之间的高速数据传输使用。它允许不同速度的硬件装置来沟通,而不需要依赖于CPU,在这个时间中,CPU对于内存的工作来…...
C语言刷题(3)——“C”
各位CSDN的uu们你们好呀,今天小雅兰的内容还是做几道题噢,好好复习一下之前的知识点,现在,就让我们开始复习吧 牛客网在线编程_编程学习|练习题_数据结构|系统设计题库 倒置字符串_牛客题霸_牛客网 BC40 竞选社长 BC41 你是天才…...
搭建Vue工程
搭建Vue工程 localhost 127.0.0.1 域名 IP 192.168.0.28 联网IP 最后都会渲染到一个页面里面,有多少个页面就有多少个页面模板。 vue里面改webpack配置 vue.config.js 配置参考 | Vue CLI /assets /api* 开发的时候用到的请求后台地址 和 项目真实部署上线的时候 请…...
C语言汉诺塔问题【图文详解】
汉诺塔1. 什么是汉诺塔2. 有关汉诺塔的有趣故事3. 利用动画来演示汉诺塔4. 如何用C语言实现汉诺塔1. 什么是汉诺塔 源于印度古老传说的益智玩具 汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具。大梵天创造…...
1、RocketMQ概述
文章目录1 MQ概述1.1 MQ简介1.2 MQ用途1.3 常见MQ产品1.4 MQ常见协议2 RocketMQ概述2.1 RocketMQ简介2.2 RocketMQ发展历程尚硅谷RocketMQ教程-讲师:Reythor雷(老雷) 我们缺乏的不是知识,而是学而不厌的态度 1 MQ概述 1.1 MQ简介…...
【POJ 3352】Road Construction 题解(Tarjan算法求边双连通分量缩点)
描述 现在几乎是夏天,这意味着几乎是夏天的施工时间!今年,负责偏远岛热带岛屿天堂道路的好心人希望修复和升级岛上各个旅游景点之间的各种道路。 道路本身也很有趣。由于岛上的奇怪风俗,道路的安排使得它们不会在交叉路口相遇&…...
Python—单分支结构
(1)if分支语句 Python中if语句的语法结构: if <条件表达式>: 满足条件运行的代码1 满足条件运行的代码2 代码示例: age 12 if age > 18:print(去上网)if 1 1 2 and :print(我满足条件了)if 1 …...
rabbitmq添加用户,虚拟机步,设置rabbitmq配置文件
第一步,登录后台控制页面 http://ip:15672第二步,添加用户和权限 重点:选择Admin和Users 第三步,添加虚拟机 点击侧边的Virtual Hosts 第四步将虚拟机和用户搭配 注意新建好后,在虚拟机列表中,点击虚拟机…...
Codeforces Round#853 div2 A-C
Codeforces Round#853 div2 A-C 等了很久终于迎来了一场cf比赛,白天出去玩了一圈,晚上回来打比赛,这次只出了A,B题。C题思路很巧妙,赛时没做出来,看了大佬学习到了,还是很不错。 A.Serval and Mocha’s A…...
软考之操作系统知识
目录 1.进程管理-进程的概念 2.进程的三态图和五态图 3.进程的同步与互斥 4.PV操作应用 5.死锁问题 6.银行家算法 7.存储管理 8.段式存储组织 9.段页式存储组织 10.页面置换算法 11.磁盘管理 12.作业管理 13.索引文件结构 14.树型目录结构 15.空闲存储空间管理 …...
【线性代数/计算复杂性理论】积和式的指数时间算法:Ryser算法
文章目录一、积和式的定义二、Ryser算法三、代码实现一、积和式的定义 积和式(permanent)是一种和行列式长得很像的矩阵函数。在介绍积和式之前,我们先看看行列式(determinant)的定义。 首先需要引入“排列”&#x…...
代码随想录 NO52 | 动态规划_leetcode 647. 回文子串 516.最长回文子序列
动态规划_leetcode 647. 回文子串 516.最长回文子序列今天是动态规划最后一天的题了,整个过程已经接近尾声了! 647. 回文子串 确定dp数组(dp table)以及下标的含义 本题如果我们定义,dp[i] 为 下标i结尾的字符串有 dp…...
【数据挖掘】1、综述:背景、数据的特征、数据挖掘的六大应用方向、有趣的案例
目录一、背景1.1 学习资料1.2 数据的特征1.3 数据挖掘的应用案例1.4 获取数据集1.5 数据挖掘的定义二、分类三、聚类四、关联分析五、回归六、可视化七、数据预处理八、有趣的案例8.1 隐私保护8.2 云计算的弹性资源8.3 并行计算九、总结一、背景 1.1 学习资料 推荐书籍如下&a…...
【架构师】零基础到精通——康威定律
博客昵称:架构师Cool 最喜欢的座右铭:一以贯之的努力,不得懈怠的人生。 作者简介:一名Coder,软件设计师/鸿蒙高级工程师认证,在备战高级架构师/系统分析师,欢迎关注小弟! 博主小留言…...
Could not extract response: no suitable HttpMessageConverter
版本:spring-cloud-openfeign-core-2.1.1.RELEASE.jar,spring-webmvc-5.1.14.RELEASE.jar,jetty-server-9.4.41.v20210516.jar,tomcat-embed-core-9.0.48.jar 问题背景 生产服务请求下游服务时偶发抛出下面的异常,下…...
文献计量三大定律之一---洛特卡定律及普赖斯定律
科学生产率是洛特卡定律的基础,科学生产率”(Scientific Productivity))是指科学家(科研人员)在科学上所表现出的能力和工作效率,通常用其生产的科学文献的数量来衡量。 1926年,洛特卡在一篇论文中提出了科…...
2023年软考高级网络规划设计师
网络规划设计师是软考高级考试科目之一,也是比较难的科目,据官方数据统计网规每年的通过率很低,而且每年只有下半年11月份考一次,如果是直接裸考,估计很悬哦~ 但是你参加考试获得证书的过程就是一个学习网络规划系统知…...
数据治理驱动因素 -报考题
数据治理并不是到此为止,而是需要直接与企业战略保持一致。数据治理越显著地帮助解决组织问题,人们越有可能改变行为、接受数据治理实践。数据治理的驱动因素大多聚焦于减少风险或者改进流程。(1)减少风险1)一般性风险…...
2023淘宝天猫38节红包满减优惠活动时间是从几月几号什么时候开始?
2023年淘宝天猫38节活动将于2023年3月2日中午12点正式开始,活动将持续至2023年3月8日晚上23点59分。届时,淘宝天猫将推出一系列的优惠活动和红包福利,为广大女性用户送上节日的祝福和福利。在这个特别的节日里,淘宝天猫为女性用户…...
Hive表优化、表设计优化、Hive表数据优化(ORC)、数据压缩、存储优化
文章目录Hive表优化Hive表设计优化分区表结构 - 分区设计思想分桶表结构 - Join问题Hive中的索引Hive表数据优化常见文件格式TextFileSequenceFileParquetORC数据压缩存储优化 - 避免小文件生成存储优化 - 合并输入的小文件存储优化 - ORC文件索引Row Group IndexBloom Filter …...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
