STM32-笔记35-DMA(直接存储器访问)

一、什么叫DMA?

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

简单来说,就是一个数据的搬运工

而搬运的路径有三种方式:如下所示

  • 存储器→存储器(例如:复制某特别大的数据buf)
  • 存储器→外设 (例如:将某数据buf写入串口TDR寄存器)
  • 外设→存储器 (例如:将串口RDR寄存器写入某数据buf)

        存储器我们知道,就是内存,存储器到存储器之间的搬运,也就是从内存中一地方搬运到内存中另一个地方。

        那么这里的外设是指什么呢?

        外设指的是spi、usart、iic、adc 等基于APB1 、APB2或AHB时钟的外设,而这里的存储器包括 自身的闪存(flash)或者内存(SRAM)以及外设的存储设备都可以作为访问地源或者目的

具体在参考手册中DMA框图中可以看见外设有哪些

 二、DMA存在的意义?

        代替 CPU 搬运数据,为 CPU 减负。

  • 1. 数据搬运的工作比较耗时间;
  • 2. 数据搬运工作时效要求高(有数据来就要搬走);
  • 3. 没啥技术含量(CPU 节约出来的时间可以处理更重要的事)。

三、搬运的流程

存储器→存储器

存储器→外设

外设→存储器

四、DMA控制器-通道

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

每个通道传输特定外设的数据

五、DMA优先级管理

优先级管理采用软件+硬件:
        软件: 每个通道的优先级可以在DMA_CCRx寄存器中设置,有4个等级
最高级>高级>中级>低级
        硬件: 如果2个请求,它们的软件优先级相同,则较低编号的通道比较高编号的通道有较高的优先权。
        比如:如果软件优先级相同,通道2优先于通道4

六、DMA传输方式

 

七、DMA寄存器及库函数介绍
 

八、小实验1:DMA内存到内存数据搬运

实验目的

使用DMA将一个大数组的数据搬运到另一个位置。

复制项目文件19-串口打印功能

重命名为40-DMA实验(内存到内存)

新建文件夹dma dma.c  dma.h

打开项目文件

加载文件

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dma.h"int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */led_init();//初始化led灯uart1_init(115200);dma_init();printf("hello word!\r\n");dma_transmit();//    led1_ON();
//    led1_OFF();while(1){ led1_ON();led2_OFF();delay_ms(500);led1_OFF();led2_ON();delay_ms(500);}
}

dma.c

#include "dma.h"
#include "stdio.h"
#define BUF_SIZE 16uint32_t src_buf[BUF_SIZE] = {0x00000000,0x11111111,0x22222222,0x33333333,0x44444444,0x55555555,0x66666666,0x77777777,0x88888888,0x99999999,0xAAAAAAAA,0xBBBBBBBB,0xCCCCCCCC,0xDDDDDDDD,0xEEEEEEEE,0xFFFFFFFF
};
uint32_t dst_buf[BUF_SIZE] = {0};DMA_HandleTypeDef dma_handle = {0};
//初始化dma函数
void dma_init(void)
{__HAL_RCC_DMA1_CLK_ENABLE();//使能DMA1dma_handle.Instance = DMA1_Channel1;//选择DMA1通道1(由于是内存传递内存所以不管哪一个通道都可以)dma_handle.Init.Direction = DMA_MEMORY_TO_MEMORY;//方向:内存到内存//内存的设置dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//内存数据对其方式:字节方式对齐dma_handle.Init.MemInc = DMA_MINC_ENABLE;//内存增量启动//外设的设置dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据对其方式dma_handle.Init.PeriphInc = DMA_PINC_ENABLE;//外设增量模式启动dma_handle.Init.Mode = DMA_NORMAL;//DMA的模式:正常模式和循环模式dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//DMA优先级:中级(只有一个DMA设置成什么无所谓)HAL_DMA_Init(&dma_handle);
}
//准备数据传输
void dma_transmit(void)
{//启动DMA传输HAL_DMA_Start(&dma_handle,(uint32_t)src_buf,(uint32_t)dst_buf,sizeof(uint32_t) * BUF_SIZE);//查询DMA传输状态while(__HAL_DMA_GET_FLAG(&dma_handle,DMA_FLAG_TC1) == RESET);//打印数据int i;for(i=0;i<BUF_SIZE;i++){printf("buf[%d]:%X\r\n",i,dst_buf[i]);}}

dma.h

#ifndef __DMA_H__
#define __DMA_H__#include "sys.h"void dma_init(void);
void dma_transmit(void);#endif

九、小实验2:DMA内存到外设数据搬运

实验目的

使用DMA将一个大数组的数据通过串口1发送 。

内存通过DMA将数据搬运到外设中

USART1-TX负责将内存中的数据按照指定的格式和波特率发送到外部设备‌

复制项目文件40-DMA实验(内存到内存)

重命名项目文件41-DMA实验(内存到外设)

打开项目文件

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dma.h"uint8_t send_buf[1000] = {0};
extern UART_HandleTypeDef uart1_handle;int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */led_init();//初始化led灯uart1_init(115200);dma_init();//printf("hello word!\r\n");int i = 0;for(i=0;i<1000;i++){send_buf[i] = 'A';}//串口句柄,发送的数据,发送的个数HAL_UART_Transmit_DMA(&uart1_handle,send_buf,1000);//以DMA的方式发送数据while(1){ led1_ON();led2_OFF();delay_ms(500);led1_OFF();led2_ON();delay_ms(500);}
}

dma.c

#include "dma.h"
#include "stdio.h"extern UART_HandleTypeDef uart1_handle;
DMA_HandleTypeDef dma_handle = {0};
//初始化dma函数
void dma_init(void)
{__HAL_RCC_DMA1_CLK_ENABLE();//使能DMA1dma_handle.Instance = DMA1_Channel4;//选择DMA1通道4,内存到外设通道4-以DMA的方式发送(tx)给串口1数据dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;//方向:内存到外设//内存的设置dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//内存数据对其方式:字节方式对齐dma_handle.Init.MemInc = DMA_MINC_ENABLE;//内存增量启动:地址递增//外设的设置dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据对其方式dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;//外设不递增dma_handle.Init.Mode = DMA_NORMAL;//DMA的模式:正常模式和循环模式dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//DMA优先级:中级(只有一个DMA设置成什么无所谓)HAL_DMA_Init(&dma_handle);//链接:内存链接到串口1:外设的句柄,成员变量,dma的句柄__HAL_LINKDMA(&uart1_handle,hdmatx,dma_handle);
}

dma.h

#ifndef __DMA_H__
#define __DMA_H__#include "sys.h"void dma_init(void);#endif

结果:

十、小实验3:DMA外设到内存数据搬运

实验目的

使用DMA接收串口1发的数据。

串口通过DMA将数据搬运到内存中

USART1-RX负责将外部设备‌中的数据按照指定的格式和波特率发送到内存,

也就是内存接收外部设备中的数据

所以这里的USART1-RX和USART1-TX是针对内存来讲(暂时先这么记)

复制项目文件41-DMA实验(内存到外设)

重命名项目文件42-DMA实验(外设到内存)

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dma.h"int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */led_init();//初始化led灯uart1_init(115200);dma_init();printf("hello word!\r\n");while(1){ led1_ON();led2_OFF();delay_ms(500);led1_OFF();led2_ON();delay_ms(500);}
}

dma.c

#include "dma.h"
#include "stdio.h"
#include "uart1.h"extern UART_HandleTypeDef uart1_handle;
DMA_HandleTypeDef dma_handle = {0};
extern uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];                                    /* UART1接收缓冲区 *///初始化dma函数
void dma_init(void)
{__HAL_RCC_DMA1_CLK_ENABLE();//使能DMA1dma_handle.Instance = DMA1_Channel5;//选择DMA1通道5,外设到内存通道5-以DMA的方式发送(rx)给串口1数据dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;//方向:外设到内存//内存的设置dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//内存数据对其方式:字节方式对齐dma_handle.Init.MemInc = DMA_MINC_ENABLE;//内存增量启动:地址递增//外设的设置dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据对其方式dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;//外设不递增dma_handle.Init.Mode = DMA_NORMAL;//DMA的模式:正常模式和循环模式dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//DMA优先级:中级(只有一个DMA设置成什么无所谓)HAL_DMA_Init(&dma_handle);//链接:内存链接到串口1:外设的句柄,成员变量,dma的句柄__HAL_LINKDMA(&uart1_handle,hdmarx,dma_handle);//串口通过DMA接收HAL_UART_Receive_DMA(&uart1_handle,uart1_rx_buf,UART1_RX_BUF_SIZE);//将数据接收到uart1_rx_buf缓冲区里面长度是UART1_RX_BUF_SIZE这么长
}

dma.h

#ifndef __DMA_H__
#define __DMA_H__#include "sys.h"void dma_init(void);#endif

结果:

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始开发纯血鸿蒙应用之实现起始页

从零开始开发纯血鸿蒙应用 一、前言二、主要页面三、应用起始页四、MainPageContent 实现1、一级结构2、二级结构2.1、EmptyContent2.2、FileListContent2.2.1、ViewAction&#xff1a;2.2.2、EditAction2.2.3、DeleteAction2.2.4、ShareAction 五、载入起始页的时机五、总结 一…

Pytorch初学

创建虚拟环境 python控制台&#xff0c;jupyter notebook,python文件运行的差异&#xff0c;后续结合使用三者。 jupter主要可以对代码进行分割单独运行&#xff0c;主要做一些探索性工作。 数据集的常见存储模式 1、以标签命名图像。 2、单独存储图像的地址。 加载数据集…

Anthropic 的人工智能 Claude 表现优于 ChatGPT

在人工智能领域&#xff0c;竞争一直激烈&#xff0c;尤其是在自然语言处理&#xff08;NLP&#xff09;技术的发展中&#xff0c;多个公司都在争夺市场的主导地位。OpenAI的ChatGPT和Anthropic的Claude是目前最具影响力的两款对话型AI产品&#xff0c;它们都能够理解并生成自然…

【Linux系列】并发与顺序执行:在 Linux 脚本中的应用与选择

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

更换WordPress主题的基础知识及注意事项

更换WordPress主题是优化和升级网站的重要步骤&#xff0c;不仅能够增强网站的视觉效果&#xff0c;还能改进用户体验并提高网站性能。然而&#xff0c;在进行该操作时&#xff0c;必须格外谨慎&#xff0c;避免数据丢失或功能失调的风险。本文将介绍在更换主题前需要采取的基本…

conda指定路径安装虚拟python环境

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” -------------------------------------------------------------…

HTML 迷宫游戏

HTML 迷宫游戏 相关资源文件已经打包成压缩文件&#xff0c;可双击index.html直接运行程序&#xff0c;且文章末尾已附上相关源码&#xff0c;以供大家学习交流&#xff0c;博主主页还有更多Python相关程序案例&#xff0c;秉着开源精神的想法&#xff0c;望大家喜欢&#xff0…

PDFMathTranslate: Star13.8k,一款基于AI的PDF文档全文双语翻译PDF文档全文双语翻译,保留格式神器,你应该需要它

嗨&#xff0c;大家好&#xff0c;我是小华同学&#xff0c;关注我们获得“最新、最全、最优质”开源项目和高效工作学习方法 PDFMathTranslate是一个开源项目&#xff0c;旨在为用户提供便捷的PDF科学论文翻译解决方案。它不仅能够翻译文本&#xff0c;还能保留公式、图表、目…

调整Python+Pytest+Allure+Yaml+Pymysql框架中需要执行的用例顺序

当pytest框架中有时时候会因为用例的前后关联关系需要调整用例执行顺序时则可以跟进具体的要求调整pytest.ini配置文件中执行用例文件夹的前后顺序 当如果是需要调整某个文件夹中用例的执行顺序时&#xff0c;则跟进具体的文件调整对应testcases中test_*.py文件中的执行顺序

毕业项目推荐:基于yolov8/yolov5/yolo11的动物检测识别系统(python+卷积神经网络)

文章目录 概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式&#xff09;功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…

BloombergGPT: A Large Language Model for Finance——面向金融领域的大语言模型

这篇文章介绍了BloombergGPT&#xff0c;一个专门为金融领域设计的大语言模型&#xff08;LLM&#xff09;。以下是文章的主要内容总结&#xff1a; 背景与动机&#xff1a; 大语言模型&#xff08;如GPT-3&#xff09;在多个任务上表现出色&#xff0c;但尚未有针对金融领域的…

CS·GO搬砖流程详细版

说简单点&#xff0c;就是Steam买了然后BUFF上卖&#xff0c;或许大家都知道这点&#xff0c;但就是一些操作和细节问题没那么明白。我相信&#xff0c;你看完这篇文章以后&#xff0c;至少会有新的认知。 好吧&#xff0c;废话少说&#xff0c;直接上实操&#xff01; 首先准…

(六)CAN总线通讯

文章目录 CAN总线回环测试第一种基于板载CAN测试第一步确认板载是否支持第二步关闭 CAN 接口将 CAN 接口置于非活动状态第三步 配置 CAN 接口第一步 设置 CAN 接口比特率第二步 设置 CAN 启用回环模式第三步 启用 CAN 接口 第四步 测试CAN总线回环捕获 CAN 消息发送 CAN 消息 第…

【微服务】4、服务保护

微服务架构与组件介绍 单体架构拆分&#xff1a;黑马商城早期为单体架构&#xff0c;后拆分为微服务架构。跨服务调用与组件使用 服务拆分后存在跨服务远程调用&#xff0c;如下单需查询商品信息&#xff0c;使用openfeign组件解决。服务间调用关系复杂&#xff0c;需维护服务…

SQL从入门到实战

学前须知 sqlzoo数据介绍 world nobel covid ge game、goal、eteam teacher、dept movie、casting、actor 基础语句 select&from SELECT from WORLD Tutorial - SQLZoo 基础查询select单列&多列&所有列&别名应用 例题一 SELECT name, continent, population …

QML学习(八) Quick中的基础组件:Item,Rectangle,MouseArea说明及使用场景和使用方法

上一篇中我们从设计器里可以看到Qt Quick-Base中有几大基础组件&#xff0c;如下图&#xff0c;这篇文章先介绍下Item&#xff0c;Rectangle&#xff0c;MouseArea这三个的说明及使用场景和使用方法 Item Item 是 QML 中所有可视元素的基类&#xff0c;是一个非常基础和通用的…

设计模式(1)——面向对象和面向过程,封装、继承和多态

文章目录 一、day11. 什么是面向对象2. 面向对象的三要素&#xff1a;继承、封装和多态2.1 封装**2.1.1 封装的概念****2.1.2 如何实现封装****2.1.3 封装的底层实现**2.1.4 为什么使用封装&#xff1f;&#xff08;好处&#xff09;**2.1.5 封装只有类能做吗&#xff1f;结构体…

Java到底是值传递还是引用传递????

在搞懂这个问题之前, 我们要首先了解什么是值传递, 什么是引用传递? 值传递: 传递的是数据的副本&#xff0c;修改副本不会影响原始数据。引用传递: 传递的是数据的引用&#xff08;地址&#xff09;&#xff0c;修改引用会直接影响原始数据. 也就是说&#xff0c;值传递和引…

VLMs之Agent之CogAgent:《CogAgent: A Visual Language Model for GUI Agents》翻译与解读

VLMs之Agent之CogAgent&#xff1a;《CogAgent: A Visual Language Model for GUI Agents》翻译与解读 导读&#xff1a;这篇论文介绍了CogAgent&#xff0c;一个专注于图形用户界面 (GUI) 理解和导航的视觉语言模型 (VLM)。这篇论文提出了一种新的视觉语言模型 CogAgent&#…

23.行号没有了怎么办 滚动条没有了怎么办 C#例子

新建了一个C#项目&#xff0c;发现行号没有了。 想把行号调出来&#xff0c;打开项目&#xff0c;选择工具>选项> 如下图&#xff0c;在文本编辑器的C#里有一个行号&#xff0c;打开就可以了 滚动条在这里&#xff1a;