STM32DMA数据传输

news2024/11/15 13:32:59

我估计大多数人学这么久连听说都没听说过DMA,更不用提知道它是干嘛的。其实DMA的本质就是一个数据的搬运工。平常的时候当我们没有配置的时候,一直都是CPU在搬运数据,但是这个活又累又没有技术含量,所以DMA的重要性还是有的。

目录

1.DMA的数据搬运:

1.DMA数据路径

2.DMA搬运模式

优先级的设定:

通道:

搬运是否循环: 

指针是否递增:

2.DMA的框架

3.DMA函数与配置

函数

配置:

内存-内存配置:

内存-外设的配置:

外设-内存(重点)

好了,祝你看完就会。


1.DMA的数据搬运:

1.DMA数据路径

简单来说,只有三种:

内存--内存               DMA_MEMORY_TO_MEMORY

外设--内存               DMA_PERIPH_TO_MEMORY

内存--外设                DMA_MEMORY_TO_PERIPH

这里的外设其实是指的STM32板子上的外设,比如GPIO口啊,串口啊等等。而所谓内存,从代码上来看其实就是一个变量。

2.DMA搬运模式

这里可配置的东西有:

优先级设定;指针是否递增;搬运模式是否循环;通道的选择。其实都没有什么难度,只不过看起来花里胡哨的,我依次讲解。

优先级的设定:

DMA的优先级采用的是硬件+软件,什么意思呢?其实本质是 通道号+软件优先级。在使用过程中,我们往往同时配置多个通道,当两个通道内的数据同时到达时,优先看软件优先级,再看通道号。注意:通道号越小优先级越。

通道:

DMA的通道其实和定时器的有点像,DMA通过通道连接各种外设,从而实现数据传输。在配置中,外设往往也配置了对应的DMA句柄来接应。所以这里对于通道只需要知道要按照表格对应来选择就行。
 注意:通常情况下芯片都只有DMA1没有DMA2.

搬运是否循环: 

DMA_Mode_Normal(正常模式):一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次 DMA_Mode_Circular(循环传输模式) :当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输。注意:当循环结束后假设给内存设定为了指针递增,此时指针会重置回到缓存最开始指向的地方。(假设缓存为arr[4]传输完成时指向arr[3],循环结束后指针会自动回归为arr[0])

指针是否递增:

数据源和目标缓存都有一个对应的指针来指向它从而使得数据知道存哪,在配置中也是分别配置是否递增的。不用想那么复杂,就一句话:数据来自或者要去内存则为递增,外设则不递增

解释:因为外设来源的数据往往放在xx寄存器里,数据一直是更新覆盖的;所以不用递增。内存就不一样了,内存往往是数组或者一个xxbuf,不递增的话就覆盖掉了。

另外还有一项配置叫数据对齐模式,其实都一般配置为:DMA_PDATAALIGN_BYTE

2.DMA的框架

3.DMA函数与配置

函数

__HAL_RCC_DMA1_CLK_ENABLE(…)        使能DMA时钟的

HAL_DMA_Init(…)                                           跟TIM的INIT用法一样

HAL_DMA_Start(…)                                        搬运函数。

__HAL_LINKDMA(…)                                      连接内存到外设数据通道的。

__HAL_DMA_GET_FLAG(…)                          获取DMA寄存器标志位的

配置:

基本的配置的步骤为:
          DMA时钟使能:__HAL_RCC_DMA1_CLK_ENABLE();
                                        |
DMA初始化(通道选择;优先级;指针递增;数据对齐;搬运模式:HAL_DMA_Init()
                                        |
DMA搬运:如果是和外设进行交互,那么这一步会变化,在代码中详解。
                                        |
                查询DMA数据是否传输正常完成

内存-内存配置:
DMA_HandleTypeDef hdma_handle = {0};
void DMA_INIT(){
	__HAL_RCC_DMA1_CLK_ENABLE();	
	//内存配置
	hdma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
	hdma_handle.Init.MemInc = DMA_MINC_ENABLE;
	//外设配置(目标存储配置)
	hdma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
	hdma_handle.Init.PeriphInc = DMA_PINC_ENABLE;
	//模式;优先级;转运方向配置
	hdma_handle.Init.Mode = DMA_NORMAL;
	hdma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;
	hdma_handle.Init.Direction = DMA_MEMORY_TO_MEMORY;
	hdma_handle.Instance = DMA1_Channel1;	
	HAL_DMA_Init(&hdma_handle);

}
void DMA_Transport(){
	HAL_DMA_Start(&hdma_handle,(uint32_t)Sroce_buf,(uint32_t)Target_buf,sizeof(uint32_t) * 16);
	while(__HAL_DMA_GET_FLAG(hdma_handle,DMA_FLAG_TC1) == RESET){
		int i = 0;
		for(i = 0;i<16;i++){
			printf("data[%d] = %X \r\n",i,Target_buf[i]);
		}
	}
}
内存-外设的配置:
#include "dma.h"
extern UART_HandleTypeDef uart1_handle;
DMA_HandleTypeDef hdma_handle = {0};
void DMA_INIT(){
	__HAL_RCC_DMA1_CLK_ENABLE();
	hdma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
	hdma_handle.Init.MemInc = DMA_MINC_ENABLE;

	hdma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
	hdma_handle.Init.PeriphInc = DMA_PINC_DISABLE;			//外设内存不可递增。
	
	hdma_handle.Init.Mode = DMA_NORMAL;
	hdma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;
	hdma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;		//内存到外设
	hdma_handle.Instance = DMA1_Channel4;
	
	HAL_DMA_Init(&hdma_handle);
	
	//链接函数,这里是吧DMA和外设连接起来,中间的参数是外设句柄中的DMA成员变量
	//可以理解为,每一个外设都配有DMA成员变量,为的就是和DMA连接。
	__HAL_LINKDMA(&uart1_handle,hdmatx,hdma_handle);
}

在main中:
	DMA_INIT();	
	HAL_UART_Transmit_DMA(&uart1_handle,Send_buf,1024);

 这里注意 HAL_UART_Transmit_DMA(&uart1_handle,Send_buf,1024); 是使用了外设对应的API,利用传输过来的句柄进行传输。

外设-内存(重点)
#include "dma.h"
#include "stdio.h"
#include "uart1.h"

extern UART_HandleTypeDef uart1_handle;
extern uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];
DMA_HandleTypeDef dma_handle = {0};
void dma_init(void)
{
    __HAL_RCC_DMA1_CLK_ENABLE();
    dma_handle.Instance = DMA1_Channel5;
    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.Priority = DMA_PRIORITY_MEDIUM;
    dma_handle.Init.Mode = DMA_NORMAL;
    HAL_DMA_Init(&dma_handle);
    
    __HAL_LINKDMA(&uart1_handle, hdmarx, dma_handle);
    
    HAL_UART_Receive_DMA(&uart1_handle, uart1_rx_buf, UART1_RX_BUF_SIZE);
}

基本配置中有两个重点,一个是extern外面串口的变量,另一个是链接到外设的函数,如果展开的来看的话,LINK函数内部是把该句柄赋值给了串口句柄中的成员变量DMA句柄

第二个重点是: HAL_UART_Receive_DMA该函数配置的是 串口句柄 目标缓存 传输大小,其中,传输大小最为重要,这里传输大小配置为了缓存区的长度大小。

void USART1_IRQHandler(void)
{
    if (__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_IDLE) != RESET)        
    {                         
        __HAL_UART_CLEAR_IDLEFLAG(&uart1_handle);
        HAL_UART_DMAStop(&uart1_handle);
        uart1_rx_len = UART1_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(&dma_handle);
        printf("recv: %s, recv_len: %d\r\n", uart1_rx_buf, uart1_rx_len);
        uart1_rx_clear();
        HAL_UART_Receive_DMA(&uart1_handle, uart1_rx_buf, UART1_RX_BUF_SIZE);
    }
}

这里代码很短但是非常有机制可谈,其中长度的计算就非常有的说:
首先:DMA的传输长度被配置为了缓存器的长度
其次:DMA在传输过程中当没有传输足够配置的长度但数据源为空时会进入等待
然后:这里配置了HAL_UART_DMAStop函数使得DMA停止传输并保持了当前状态。
再然后:使用__HAL_DMA_GET_COUNTER会返回当前未传输任务中还未传输的长度
最后:使用缓存大小 - __HAL_DMA_GET_COUNTER的返回值 = 已传输的值。

我用数字再解释一遍:假设配置DMA传输长度为10,此时串口接收到了4,DMA传输进缓存区4的数据,此时数据源为空DMA进入等待;于此同时串口接收完成触发空闲中断,此时调用函数Stop使得DMA停止传输保持当前状态,GET_COUNTER返回的值为6,所以直接10 - 6 = 4也就是接收到的数据长度。

其中,Stop函数是必要的,因为如果长时间没有接收或者新的数据进入都会导致GET返回值变化。

好了,祝你看完就会。

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

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

相关文章

centos8.5.2111切换阿里云镜像

备份 mv /etc/yum.repos.d/CentOS-Linux-BaseOS.repo /etc/yum.repos.d/CentOS-Linux-BaseOS.repo.backup下载最新的源 wget -O /etc/yum.repos.d/CentOS-Linux-BaseOS.repo http://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo更改第二个配置文件 cd /etc/yum.repo…

自定义CustomRatingBar控件

通过自定义RatingBar的样式实现⭐️⭐️⭐️指示器的方式功能过于受限&#xff0c;而且显示的样式阴影会受到影响。 系统自带显示&#xff1a; 自定义样式&#xff1a; 因此简单自一个符合要求的 CustomRatingBar 支持设置星星数量支持设置星星Rating(float)支持设置空显示…

计算机毕业设计选题推荐-健康饮食系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

【优秀python 数据分析案例】基于python的穷游网酒店数据采集与可视化分析的设计与实现

1 需求分析 1.1 用户需求 1.1.1 背景与现状 穷游网是国内知名的旅游社区&#xff0c;在其网站上&#xff0c;用户可以自由分享旅行经验和攻略&#xff0c;也可以浏览其他用户的经验和攻略&#xff0c;以便更好地规划自己的旅行。而酒店信息是旅行攻略中不可或缺的一部分&#…

正点原子imx6ull-mini-Linux驱动之设备树下的 platform 驱动编写(15)

上一章我们详细的讲解了 Linux 下的驱动分离与分层&#xff0c;以及总线、设备和驱动这样的驱动 框架。基于总线、设备和驱动这样的驱动框架&#xff0c;Linux 内核提出来 platform 这个虚拟总线&#xff0c;相应 的也有 platform 设备和 platform 驱动。上一章我们讲解了传统的…

vue2学习 -- 脚手架

文章目录 1. 使用2. render函数3. vue.config.js配置文件4. ref属性5. props配置6. mixin7. 插件8. scoped 1. 使用 脚手架文档 安装脚手架 npm install -g vue/cli 选择工作目录&#xff0c;创建项目 vue create name 2. render函数 关于不同版本的Vue.js vue.js与vue.run…

在 VueJS 项目中实现多个可拖拽的弹出框(多个可拖拽el-dialog弹出框,共用同一函数)

前言 在项目开发中&#xff0c;弹出框&#xff08;Dialog&#xff09;是常见的UI组件。默认情况下&#xff0c;弹出框的位置是固定的&#xff0c;但在某些场景下&#xff0c;我们希望用户可以自由拖动弹出框的位置&#xff0c;以提升用户体验。之前单个视频拖拽弹框&#xff0…

Spring Boot 参数校验 Validation 使用

概述 当我们想提供可靠的 API 接口&#xff0c;对参数的校验&#xff0c;以保证最终数据入库的正确性&#xff0c;是必不可少的活。前、后端校验都是保证参数的准确性的手段之一&#xff0c;前端校验并不安全&#xff0c;任何人都可以通过接口来调用我们的服务&#xff0c;就算…

springboot电影购票系统-计算机毕业设计源码85384

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1系统开发流程 2.2.2 用户登录流程 2.2.3 系统操作流程 2.2.4 添加信息流程 2.2.5 修改信息流程 2.2.6 删除信息流程 2.3 系统功能…

ComfyUI插件:ComfyUI layer style 节点(三)

前言&#xff1a; 学习ComfyUI是一场持久战&#xff0c;而ComfyUI layer style 是一组专为图片设计制作且集成了Photoshop功能的强大节点。该节点几乎将PhotoShop的全部功能迁移到ComfyUI&#xff0c;诸如提供仿照Adobe Photoshop的图层样式、提供调整颜色功能&#xff08;亮度…

哪里可以找到数据恢复软件?5 款顶级数据恢复软件分享

在当今的数字时代&#xff0c;我们的数据既是我们最宝贵的资产&#xff0c;也是我们最大的弱点。由于硬件故障、意外删除或软件问题&#xff0c;丢失重要文档、珍贵照片或对职业至关重要的项目的风险始终存在。值得庆幸的是&#xff0c;强大的数据恢复软件可以帮助找回最初看似…

[每周一更]-(第108期):如何保护你的JavaScript代码

文章目录 一、框架如何实现JS的保护1. 模块化和组件化2. 使用环境变量3. 代码混淆和最小化vue.config.js 4. 使用请求库和拦截器axios.js 文件在组件中使用 Axios 拦截器 5. 服务端处理敏感逻辑6. 安全最佳实践使用 CSP 7. 依赖前端框架的内置安全特性8. 数据验证和清理 二、原…

【linux】【操作系统】内核之traps.c源码阅读

C 文件traps.c 是 Linux 内核的一部分&#xff0c;主要处理硬件陷阱和故障。文件中包含多个函数来处理不同类型的异常和错误。下面是详细的解析&#xff1a; 概览 目的&#xff1a;此文件负责处理各种硬件异常和故障。它包括了处理特定类型错误以及初始化异常处理器的函数。文…

uniapp0基础编写安卓原生插件和调用第三方jar包(Ch34的jar包)和如何解决android 如何Application初始化

前言 我假设你会uniapp安卓插件开发了,如果不会请看这篇文章,这篇文章是0基础教学。 这篇文章我们将讲一下如何使用CH34XUARTDriver.jar进行开发成uniapp插件。 它的难点是:uniapp如何Application初始化第三方jar包 先去官网下载CH340/CH341的USB转串口安卓免驱应用库:h…

Spring实现自定义注解

一&#xff0c; 背景 目前部门有一个培训&#xff0c;需要讲一下Spring的使用&#xff0c;看到有同学提出问题&#xff0c;想自定义实现一个打日志的注解&#xff0c;下面就记录一下实现过程。 环境&#xff1a; Spring 6.1.5, 不使用Spring Boot. 二&#xff0c;实现步骤 …

Mysql--权限与安全管理

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、权限表 MySQL服务器通过权限表来控制用户对数据库的访问&#xff0c;权限表存放在MySQL数据库中&#xff0c;由MySQL_install_db脚本初始化。存储…

【工具篇】华为VRP通用操作系统 —— 配置文件管理

文章目录 配置文件分类配置文件命令配置文件工作原理 配置文件分类 设备的配置文件通常有两种类型&#xff1a; 1、启动配置文件&#xff08;Startup Configuration&#xff09;&#xff1a; 这是设备启动时加载的配置文件&#xff0c;包含了设备的基本配置信息&#xff0c;如…

Linux 内核源码分析---资源分配及系统总线

资源管理 Linux提供通用的构架&#xff0c;用于在内存中构建数据结构。这些结构描述了系统中可用的资源&#xff0c;使得内核代码能够管理和分配资源。 其中关键的数据结构resource如下&#xff1a; 用于连接parent, child, sibling成员规则如下&#xff1a; 1、每个子结点只…

接口测试学习笔记1

一、行业背景和测试分层 1、招聘需求 1&#xff09;手工测试&#xff1a;业务需求、业务逻辑 2&#xff09;自动化测试&#xff1a;业务逻辑 技术规范 功能自动化 QTP、Selenium 性能自动化 LoadRunner、JMeter 接口自动化 Postman、Fiddler、JMeter、SoapUI... …

值得一读!六本网络安全学习必备书籍推荐

在网络安全领域不断发展的今天&#xff0c;深入学习和掌握相关知识显得尤为重要。以下为大家推荐六本有助于提升网络安全技能的经典书籍。 一、《白帽子讲 Web 安全》 这本书由吴翰清撰写&#xff0c;涵盖了 Web 安全的诸多方面&#xff0c;包括常见的攻击手段、防御方法以及安…