STM32DMA数据传输

我估计大多数人学这么久连听说都没听说过DMA,更不用提知道它是干嘛的。其实DMA的本质就是一个数据的搬运工。平常的时候当我们没有配置的时候,一直都是CPU在搬运数据,但是这个活又累又没有技术含量,所以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(循环传输模式) :当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输。

指针是否递增:

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

解释:因为外设来源的数据往往放在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://xiahunao.cn/news/3281017.html

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

相关文章

YOLOv9最新最全代码复现(论文复现)

YOLOv9最新最全代码复现&#xff08;论文复现&#xff09; 本文所涉及所有资源均在传知代码平台可获取 文章目录 YOLOv9最新最全代码复现&#xff08;论文复现&#xff09;引言YOLOv9模型概述模型框架图环境搭建及训练推理环境配置数据集准备训练过程测试和评估实践应用 报错修…

【机器学习西瓜书学习笔记——神经网络】

机器学习西瓜书学习笔记【第四章】 第五章 神经网络5.1神经元模型5.2 感知机与多层网络学习感知机学习率成本/损失函数梯度下降 5.3 BP神经网络&#xff08;误差逆传播&#xff09;5.4 全局最小与局部极小5.5 其他常见神经网络RBF网络RBF 与 BP 最重要的区别 ART网络 第五章 神…

Vue组件库移动端预览实现原理

引言 大家如果使用过移动端组件库&#xff08;比如&#xff1a;Vant&#xff09;&#xff0c;会发现在网站右侧有一个手机端的预览效果。 而且这个手机端预览的内容和外面的组件代码演示是同步的&#xff0c;切换组件的时候&#xff0c;移动端预览的内容也会发生相应的变化。 …

守护线程(Daemon Threads)详解:与非守护线程的区别

守护线程&#xff08;Daemon Threads&#xff09;详解&#xff1a;与非守护线程的区别 1、守护线程是什么&#xff1f;2、守护线程与非守护线程的区别2.1 JVM关闭行为2.2 任务性质2.3 线程设置2.4 示例代码 3、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收…

pytorch 绘制Depth Anything网络结构

pytorch 绘制模型的网络结构有很多中方法&#xff0c;个人比较喜欢 torchview 生成的 Graphviz 风格的图片。 Graphviz介绍 Graphviz是一款开源的图形可视化软件&#xff0c;其名称来源于“Graph Visualization Software”的缩写。它通过使用一种名为DOT的描述语言来定义图形…

不踩雷的护眼大路灯有哪些?五款盲选不踩雷的护眼大路灯推荐

不踩雷的护眼大路灯有哪些&#xff1f;作为一名专业的实测博主温馨提示大家&#xff0c;虽然护眼落地灯是个好东西&#xff0c;它能够提供柔和舒适的环境光&#xff0c;减少对眼睛的伤害&#xff0c;但是千万别乱买跟风&#xff0c;盲目入手踩雷率80%以上。那么如何辨别一盏护眼…

创客项目秀 | 基于 XIAO 开发板的语音向导

背景 柴火创客空间作为大湾区科技创新的窗口&#xff0c;每年到访空间的社区伙伴众多&#xff0c;为了更好的进行空间信息交互&#xff0c;我们希望有一个装置是可以解决&#xff1a;当空间管理员不在现场的时候&#xff0c;到访者可以通过装置获得清晰的介绍与引导。 为了解…

vue2 封装插槽组件

安装 element-ui npm install element-ui --save ---force main.js 导入 import Vue from vue; import ElementUI from element-ui; import element-ui/lib/theme-chalk/index.css; import App from ./App.vue; Vue.use(ElementUI); new Vue({ el: #app, render: h > h(Ap…

全渠道AI数字化商品管理 零售品牌增长“超级引擎”

随着“流量红利”时代的终结 品牌面临增速放缓、利润下滑的双重挑战。 消费者的诉求日益理性和个性化&#xff0c; 国内外品牌角逐市场份额 A1、大数据等先进技术迅猛发展 品牌商品计划管理变得更加复杂而多维。 零售品牌正加速数字化与全渠道融合以应对挑战。 可持续盈利…

对于一家企业来说,电气数字化是否有那么重要?

时代大背景下&#xff0c;尤其是在复杂的国际与社会环境交织之中&#xff0c;全社会的“数字化”转型已成必然之势。对于电子产业而言&#xff0c;“数字化”无疑是重大机遇。 众所周知&#xff0c;在蒸汽机时代&#xff0c;身为机械工程师堪称幸运&#xff0c;彼时涌现出众多…

MySQL 将查询结果导出到文件(select … into Statement)

我们经常会遇到需要将SQL查询结果导出到文件&#xff0c;以便后续的传输或数据分析的场景。为了满足这个需求&#xff0c;MySQL的select语句提供了into子句可以将的查询结果直接导出到文本文件。本文就MySQL中select…into的用法进行演示。 文章目录 一、select…into语句简介…

AWS账号注册:AWS 用借记卡注册是否有风险?

亚马逊云服务&#xff08;Amazon Web Services&#xff0c;简称 AWS&#xff09;作为全球领先的云服务提供商&#xff0c;吸引了众多企业和个人用户。注册 AWS 账户时&#xff0c;提供支付方式是必要的步骤&#xff0c;许多用户会选择使用借记卡来完成注册。那么&#xff0c;使…

idea、webstorm、navicat等2024大佬总结亲测可用

宝藏网址&#xff0c;亲测可用。 关于JetBrains全家桶激活。 扫码关注&#xff1a;JAVA和人工智能。回复 idea 或 webStorm 或 navicat 获取 仅学习使用&#xff0c;不要用于商业用途&#xff01;

【剑指offer】

剑指offer 面试题67&#xff1a;字符串转成整数面试题1&#xff1a;赋值运算符函数面试题3&#xff1a;数组中重复的数字 面试题67&#xff1a;字符串转成整数 LeedCode&#xff1a;LCR 192. 把字符串转换成整数 (atoi) 测试atoi的功能和异常效果 #include <iostream> #…

二叉树的介绍及其顺序结构的实现

Hello, 亲爱的小伙伴们&#xff0c;你们的作者菌又回来了&#xff0c;之前我们学习了链表、顺序表、栈等常见的数据结构&#xff0c;今天我们将紧跟之前的脚步&#xff0c;继续学习二叉树。 好&#xff0c;咱们废话不多说&#xff0c;开始我们今天的正题。 1.树 1.1树的概念和…

vue3框架Arco Design输入邮箱选择后缀

使用&#xff1a; <a-form-item field"apply_user_email" label"邮箱&#xff1a;" ><email v-model"apply_user_email" class"inputborder topinputw"></email> </a-form-item>import email from /componen…

Java语言程序设计基础篇_编程练习题***15.35/15.34 (动画:自回避随机漫步)

***15.34 (模拟&#xff1a;自回避随机漫步) 在一个网格中的自回避漫步是指从一个点到另一点的过程中&#xff0c;不重复两次访问一个点。自回避漫步已经广泛应用在物理、化学和数学学科中。它们可以用来模拟像溶剂和聚合物这样的链状物。编写一个程序&#xff0c;显示一个从中…

Educational Codeforces Round 168 (Rated for Div. 2)

据说这场比赛非常简单&#xff0c;但本蒟蒻却认为比以往还要难(;༎ຶД༎ຶ) A.Strong Password 输入样例&#xff1a; 4 a aaa abb password输出样例&#xff1a; wa aada abcb pastsword思路&#xff1a; 我们只需在原来字符串中连续的两个字符之间插入一个不同的字符&…

React 学习——自定义Hook实现,使用规则

使用规则&#xff1a; 只能在组件中或者其他自定义Hook函数中调用只能在组件的顶层调用&#xff0c;不能嵌套在 if、for、其他函数中 import { useState } from "react"// 封装函数 function useToggle(){const [show,setShow] useState(true);const toggle ()&…

机器学习算法——常规算法,在同的业务场景也需要使用不同的算法(二)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…