STM32开发——DMA(数据搬运)

news2024/11/24 22:48:46

目录

1.DMA简介

 2.从内存到内存搬运

2.1CubeMX设置

2.2函数代码

3.内存到外设

3.1CubeMX配置

3.2 函数代码

4.外设到内存

4.1CubeMX配置

4.1函数代码 


1.DMA简介

DMA(Direct Memory Access,直接存储器访问) 提供在外设与内存、存储器和存储器、外设
与外设之间的高速数据传输使用。它允许不同速度的硬件装置来沟通,而不需要依赖于
CPU,在这个时间中,CPU对于内存的工作来说就无法使用。

DMA的意义
代替 CPU 搬运数据,为 CPU 减负。
1. 数据搬运的工作比较耗时间;
2. 数据搬运工作时效要求高(有数据来就要搬走);
3. 没啥技术含量(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(循环传输模式)
当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是
多次传输模式

 2.从内存到内存搬运

需求:使用DMA的方式将数组A的内容复制到数组B中,搬运完之后将数组B的内容打印到屏幕。

2.1CubeMX设置

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

 用到的库函数

1. 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)

2. __HAL_DMA_GET_FLAG

#define __HAL_DMA_GET_FLAG(__HANDLE__, __FLAG__) (DMA1->ISR & (__FLAG__))
参数一:HANDLE,DMA通道句柄
参数二:FLAG,数据传输标志。DMA_FLAG_TCx表示数据传输完成标志
返回值:FLAG的值(SET/RESET)

代码实现
1. 开启数据传输
2. 等待数据传输完成
3. 打印数组内容

2.2函数代码

#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]);

3.内存到外设

需求:使用DMA的方式将内存数据搬运到串口1发送寄存器,同时闪烁LED1。

3.1CubeMX配置

 用到的库函数
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)

代码实现
1. 准备数据
2. 将数据通过串口DMA发送

3.2 函数代码

#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);
}

4.外设到内存

项目需求:使用DMA的方式将串口接收缓存寄存器的值搬运到内存中,并通过串口打印或者DMA打印。

4.1CubeMX配置

  

4.1函数代码 

用到的库函数
1. __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,需要使能的中断
返回值:无
2. 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)
3. __HAL_UART_GET_FLAG
#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR &
(__FLAG__)) == (__FLAG__))
参数一:HANDLE,串口句柄
参数二:FLAG,需要查看的FLAG
返回值:FLAG的值
4. __HAL_UART_CLEAR_IDLEFLAG
#define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
参数一:HANDLE,串口句柄
返回值:无
5. HAL_UART_DMAStop
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)
参数一:UART_HandleTypeDef *huart,串口句柄
返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
6. __HAL_DMA_GET_COUNTER
#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR)
参数一:HANDLE,串口句柄
返回值:未传输数据大小

代码实现
如何判断串口接收是否完成?如何知道串口收到数据的长度?
使用串口空闲中断(IDLE)!
串口空闲时,触发空闲中断;
空闲中断标志位由硬件置1,软件清零
利用串口空闲中断,可以用如下流程实现DMA控制的任意长数据接收:

  1. 使能IDLE空闲中断;
  2. 使能DMA接收;
  3. DMA不断传输数据到缓冲区;
  4. 一帧数据接收完毕,串口暂时空闲,触发串口空闲中断;
  5. 在中断服务函数中,清除IDLE空闲中断标志位,关闭DMA传输(防止干扰);
  6. 计算刚才收到了多少个字节的数据。
  7. 处理缓冲区数据,使能DMA接收,开始下一帧接收。

main.c

#define size 100

uint8_t rcv_array[size];// 接收数据缓存数组
uint8_t rcv_len;// 接收一帧数据的长度

__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);// 使能IDLE空闲中断
HAL_UART_Receive_DMA(&huart1,rcv_array,rcv_len);// 使能DMA接收中断

stm32f1xx_it.c

#define size 100

extern uint8_t rcv_array[size];
extern uint8_t rcv_len;

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){  //如果uart1发送中断空闲了
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);     //清除空闲标志位
		HAL_UART_DMAStop(&huart1);              //停止MDA传输
		
		//开始处理接收数组
		rcv_len= size-__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);   //计算接收数组长度
		//HAL_UART_Transmit(&huart1,rcv_array,rcv_len,100);    //发送数据方式1
		HAL_UART_Transmit_DMA(&huart1,rcv_array,rcv_len);    //发送数据方式2
		HAL_UART_Receive_DMA(&huart1,rcv_array,size);        //开启输出DMA接收
	}
  /* USER CODE END USART1_IRQn 1 */
}

实现效果

 

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

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

相关文章

APM二次开发(二):添加一个任务

固件版本 APM copter 4.3.1 参考&#xff1a;https://ardupilot.org/dev/docs/code-overview-scheduling-your-new-code-to-run-intermittently.html APM添加任务比PX4要简单很多&#xff0c;直接在调度器里添加函数即可。 先定义一个要调度的函数my_test() 然后加到调度器中…

C++ [STL容器反向迭代器]

本文已收录至《C语言和高级数据结构》专栏&#xff01; 作者&#xff1a;ARMCSKGT STL容器反向迭代器 前言正文适配器反向迭代器反向迭代器框架默认成员函数反向迭代器的遍历反向迭代器的比较反向迭代器数据访问反向迭代器代码测试反向迭代器 最后 前言 我们知道STL大部分容器…

(2023最新版)互联网大厂1120道Java面试真题附答案详解

很多 Java 工程师的技术不错&#xff0c;但是一面试就头疼&#xff0c;10 次面试 9 次都是被刷&#xff0c;过的那次还是去了家不知名的小公司。 问题就在于&#xff1a;面试有技巧&#xff0c;而你不会把自己的能力表达给面试官。 应届生&#xff1a;你该如何准备简历&#…

5.3.4 因特网的路由协议(四)BGP协议

5.3.4 因特网的路由协议&#xff08;四&#xff09;BGP协议 我们学习的RIP协议&#xff08;5.3.2 因特网的路由协议&#xff08;二&#xff09;基于距离向量算法的RIP协议&#xff09;和OSPF协议&#xff08;5.3.3 因特网的路由协议&#xff08;三&#xff09;OSPF协议&#x…

Python真的对初学者友好吗?其实可以从以下几点就能看出(收藏)

本文内容里我给大家分享的是一篇关于学习python有哪些必要条件&#xff0c;需要的朋友们可以学习下。 编程零基础&#xff0c;可以学习 Python 吗&#xff1f;这是很多初学者经常问我的一个问题。 当然&#xff0c;在计算机方面的基础越好&#xff0c;对学习任何一门新的编程…

强制使用本地GNSS作为时钟源带来的思考

1.背景知识 BMCA&#xff08;最佳时钟源选择算法&#xff09;&#xff1a;它是在PTP网络里面用来选择最佳时钟源的一种常见算法&#xff0c;它的执行过程包含一下四步&#xff1a; 时钟源发现&#xff1a;在网络中的PTP设备会交换时钟源信息。每个设备会公告自己的时钟源特性&…

联想拯救者电脑触摸板用不了了

文章目录 问题分析解决1. 解决方法一2. 解决方法二3. 解决方法三 问题 电脑触摸板用不了了&#xff0c;无论使用怎样的操作均未能完成对鼠标的操作 分析 这是因为被误触了“游戏模式”&#xff0c;就会出现“防误触”开关 解决 1. 解决方法一 &#xff08;开机输入密码前…

汽车EDI:如何与SAS建立 EDI 连接?

SAS Automotive Systems &#xff08;以下简称为&#xff1a;SAS&#xff09;是一家全球领先的汽车零部件制造商&#xff0c;总部位于德国。该公司专注于汽车电子技术和系统集成领域&#xff0c;为世界各大汽车制造商提供创新的解决方案。 EDI&#xff08;电子数据交换&#x…

MFC扩展库BCGControlBar Pro v33.5亮点 - Ribbon Bar等全新升级

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v33.5已正式发布了&#xff0c;此版本包含了Ribbon&#xff08;功能区&#xff09;自定义…

『 前端三剑客 』:CSS常用属性

文章目录 一 . CSS常用元素属性1.1 字体家族和 字体大小1.2 设置字体粗细 font-weight1.3 文字样式1.4 文字颜色1.5 文本对齐1.6 文本装饰1.7 文本缩进1.8 背景属性1.9 边框设置 二 . 元素的显示模式2.1 块级元素2.2 行内元素2.3 css 盒子模型 三 . 弹性布局3.1 开启弹性布局3.…

springboot+vue地方美食分享网站(java项目源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的地方美食分享网站。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风…

Python基础(9)——Python运算符

Python基础&#xff08;9&#xff09;——Python运算符 文章目录 Python基础&#xff08;9&#xff09;——Python运算符目标运算符的分类1. 算数运算符2. 赋值运算符3. 复合赋值运算符4. 比较运算符5. 逻辑运算符5.1 拓展 总结 目标 掌握常用运算符的作用 运算符的分类 算数…

weboffice获取外部剪切板内容解决方案

1、初始化引入sdk时&#xff0c;需要传入监听函数 -- getClipboardData 在移动端 APP 需要从系统剪切板获取数据时&#xff0c;可以使用该接口。 目前仅支持移动端表格以及文字&#xff0c;并且 JSSDK 版本为 v1.1.6 可以通过传入 获取系统剪切板数据函数 在文档粘贴的时候&…

「深度学习之优化算法」(四)遗传算法

1. 遗传算法简介 遗传算法(Genetic Algorithms,GA)是一种模拟自然中生物的遗传、进化以适应环境的智能算法。由于其算法流程简单,参数较少优化速度较快,效果较好,在图像处理、函数优化、信号处理、模式识别等领域有着广泛的应用。   在遗传算法(GA)中,每一个待求问题…

“鸡兔同笼”问题蕴含的数学思维、数学思想

郭靖 [摘 要]“鸡兔同笼”问题是我国古代数学里的经典问题&#xff0c;出自《孙子算经》&#xff0c;也是小学数学的拓展内容。“鸡兔同笼”问题是一类题的总述&#xff0c;其背后隐藏着不同的解题策略与思维。教师应剖析由“鸡兔同笼”问题延伸出来的解题思路与思考方式&…

社交电商以人为核心,流量营销矩阵该如何打造

​ 从流量运营的角度来看&#xff0c;社交电商和传统电商最大的区别在于&#xff0c;社交电商在一开始就不是以流量为核心&#xff0c;而是以人为核心。 具体来说&#xff0c;就是通过社交关系链把消费者串联起来&#xff0c;打造一个巨大的流量池。在这个流量池中&#xff0…

经典游戏|像素鸟

基于C#制作一个经典的像素风休闲娱乐小游戏|像素鸟。 一、项目搭建1.1、创建1.2、界面设计二、功能实现2.1、初始化游戏2.2、绘制管道2.3、地面移动2.4、控制小鸟下落的重复执行事件2.5、结语一、项目搭建 1.1、创建 打开Visual Studio,右侧选择创建新项目。 搜索框输入winf…

AI实景三维建模开放网页版,无需下载客户端,照片/视频转3D模型

近年来&#xff0c;随着实景中国建设进程的推进&#xff0c;从日常生活中道路、桥梁等基础设施的实景三维数字化&#xff0c;到虚拟现实、元宇宙数字展厅、虚拟数字人等互联网新基建参与方的不断增加&#xff0c;对现实物品进行1:1建模再映射到数字空间中的需求领域已经越来越广…

Docker部署(3)——Dockerfile文件参数

一、Dockerfile文件参数 Dockerfile 是用于构建 Docker 镜像的文件(之前的项目就是将jar包通过Dockerfile文件(D要记得大写&#xff01;&#xff01;)&#xff0c;打包成一个镜像&#xff0c;当然后面也有一键化部署&#xff0c;使用插件来完成&#xff0c;方式有很多&…

Benewake(北醒) TF-LC02 (TTL) 雷达不使用TTL转USB转接板在Arduino Uno上的运用

目录 前言Benewake(北醒) TF-LC02产品简要说明Arduino开发板介绍Benewake(北醒) TF-LC02 接口及通讯协议说明接口定义串口协议说明通讯协议说明功能码说明 接线示意图例程说明配置软硬串口定义获取TOF数据的结构获取雷达距离数据的协议解析通过主循环发送获取距离指令&#xff…