当前位置: 首页 > news >正文

STM32DMA数据传输

我估计大多数人学这么久连听说都没听说过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返回值变化。

好了,祝你看完就会。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Golang之OpenGL(一)
  • 平舌、翘舌音学习: z、c、s--zh、ch、sh
  • 使用 MinIO、Langchain 和 Ray Data 构建分布式嵌入式子系统
  • electron-builder打包vue2项目问题合集
  • Java | Leetcode Java题解之第316题去除重复字母
  • MongoDB简介及其在Java中的应用
  • 大语言模型(LLM)快速理解
  • 记录一次服务器被(crontab)木马入侵事件
  • 【Nuxt】服务端渲染 SSR
  • [Meachines] [Easy] Mirai Raspberry树莓派默认用户登录+USB挂载文件读取
  • 栈和队列——2.逆波兰表达式求值
  • ReactiveStream
  • 智慧水务项目(二)django(drf)+angular 18 创建通用model,并对orm常用字段进行说明
  • 23. Hibernate 性能之缓存与缓存算法
  • Java重修笔记 第二十七天 匿名内部类
  • Golang-长连接-状态推送
  • java小心机(3)| 浅析finalize()
  • JSDuck 与 AngularJS 融合技巧
  • Leetcode 27 Remove Element
  • Ruby 2.x 源代码分析:扩展 概述
  • 码农张的Bug人生 - 见面之礼
  • 配置 PM2 实现代码自动发布
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 学习ES6 变量的解构赋值
  • 正则与JS中的正则
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • #include<初见C语言之指针(5)>
  • #java学习笔记(面向对象)----(未完结)
  • #LLM入门|Prompt#3.3_存储_Memory
  • (4)事件处理——(2)在页面加载的时候执行任务(Performing tasks on page load)...
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (不用互三)AI绘画工具应该如何选择
  • (附源码)springboot金融新闻信息服务系统 毕业设计651450
  • (四)汇编语言——简单程序
  • (五)c52学习之旅-静态数码管
  • (一)Thymeleaf用法——Thymeleaf简介
  • (一)基于IDEA的JAVA基础10
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .NET 4.0中的泛型协变和反变
  • .NET开发不可不知、不可不用的辅助类(一)
  • .pyc文件是什么?
  • @property @synthesize @dynamic 及相关属性作用探究
  • [ C++ ] STL---string类的使用指南
  • [ 英语 ] 马斯克抱水槽“入主”推特总部中那句 Let that sink in 到底是什么梗?
  • [Armbian] 部署Docker版Home Assistent,安装HACS并连接米家设备
  • [Asp.net MVC]Bundle合并,压缩js、css文件
  • [AUTOSAR][诊断管理][ECU][$37] 请求退出传输。终止数据传输的(上传/下载)
  • [bzoj1912]异象石(set)
  • [bzoj4240] 有趣的家庭菜园
  • [C#]使用深度学习算法opencvsharp部署RecRecNet广角图像畸变矫正校正摄像广角镜头畸变图像
  • [C++]——带你学习类和对象
  • [C++]运行时,如何确保一个对象是只读的
  • [CF]Codeforces Round #551 (Div. 2)
  • [CSS]一文掌握