DMA原理和应用

目录

1.什么是DMA

2.DMA的意义

3.DMA搬运的数据和方式

4.DMA 控制器和通道

5.DMA通道的优先级

6.DMA传输方式

7.DMA应用

实验一: 内存到内存搬运

CubeMX配置:

​编辑用到的库函数:

代码实现思路:

实验二: 内存到外设搬运

CubeMX配置:

​编辑用到的库函数:

代码实现思路:

实验三: 外设到内存搬运

CubeMX配置

用到的库函数

代码实现思路:


1.什么是DMA

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

简单描述:DMA就是一个数据搬运工!

2.DMA的意义

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

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

3.DMA搬运的数据和方式

DMA搬运存储器、外设的数据

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

三种搬运方式:

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

存储器→存储器

存储器→外设

外设→存储器

4.DMA 控制器和通道

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

DMA1有7个通道:

DMA2有5个通道:

5.DMA通道的优先级

优先级管理采用软件+硬件:

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

6.DMA传输方式

DMA_Mode_Normal(正常模式)

  • 一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次

DMA_Mode_Circular(循环传输模式)

  • 当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是 多次传输模式

指针递增模式:

  • 外设和存储器指针在每次传输后可以自动向后递增或保持常量。
  • 当设置为增量模式时,下一个要 传输的地址将是前一个地址加上增量值。

7.DMA应用

实验一: 内存到内存搬运

实验要求:

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

CubeMX配置:

DMA 配置:

串口配置:


用到的库函数:

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. 打印数组内容

代码示例:

#include <stdio.h>

uint32_t src_buf[16] = {	
	0x00000000,0x11111111,0x22222222,0x33333333,
	0x44444444,0x55555555,0x66666666,0x77777777,
	0x88888888,0x99999999,0xAAAAAAAA,0xBBBBBBBB,
	0xCCCCCCCC,0xDDDDDDDD,0xEEEEEEEE,0xFFFFFFFF
};
uint32_t des_buf[16];


//重定向printf
int fputc(int ch, FILE *f)
{      
    unsigned char temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,0xffff);  
    return ch;
}

int main(void)
{
  int i= 0;
/*
1. 开启数据传输
2. 等待数据传输完成
3. 打印数组内容
*/
  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
	
	//1. 开启数据传输
	HAL_DMA_Start(&hdma_memtomem_dma1_channel1,(uint32_t)src_buf,(uint32_t)des_buf,sizeof(uint32_t) * 16);

	//2. 等待数据传输完成
	while(__HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1, DMA_FLAG_TC1) == RESET);
	//3. 打印数组内容
	for (i = 0; i < 16; i++)
	printf("Buf[%d] = %X\r\n", i, des_buf[i]);
		
  while (1)
  {
		
  }

}

烧录代码,打开串口助手:

实验二: 内存到外设搬运

实验要求:

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

CubeMX配置:

DMA配置:

串口配置:


用到的库函数:

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发送

代码示例:

#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"

unsigned char sendbuf[100];

int main(void)
{
  int i;
  //1. 准备数据
  for(i=0;i<100;i++){
	  sendbuf[i] = 'z';
  }
  //2. 将数据通过串口DMA发送
  HAL_UART_Transmit_DMA(&huart1,sendbuf,100);
	
  while (1)
  {
		HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
		HAL_Delay(300);
  }
}

烧录代码,打开串口助手

实验三: 外设到内存搬运

实验要求:

使用DMA的方式将串口接收缓存寄存器的值搬运到内存中,同时闪烁LED1。

CubeMX配置

DMA配置:

串口和中断配置:

用到的库函数

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. 在中断服务函数中,清除中断标志位,关闭DMA传输(防止干扰)
  • 6. 计算刚才收到了多少个字节的数据
  • 7. 处理缓冲区数据,开启DMA传输,开始下一帧接收

代码示例:

main.c

uint8_t rcvBuf[BUF_SIZE]; // 接收数据缓存数组
uint8_t rcvLen = 0; // 接收一帧数据的长度
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能IDLE空闲中断
HAL_UART_Receive_DMA(&huart1,rcvBuf,100); // 使能DMA接收中断
while (1)
{
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
    HAL_Delay(300);
}

main.h

#define BUF_SIZE 100

stm32f1xx_it.c

extern uint8_t rcvBuf[BUF_SIZE];
extern uint8_t rcvLen;

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)) // 判断IDLE标志位是否被置位
    {
        __HAL_UART_CLEAR_IDLEFLAG(&huart1);// 清除标志位
        HAL_UART_DMAStop(&huart1); // 停止DMA传输,防止干扰
        uint8_t temp=__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
        rcvLen = BUF_SIZE - temp; //计算数据长度
        HAL_UART_Transmit_DMA(&huart1, rcvBuf, rcvLen);//发送数据
        HAL_UART_Receive_DMA(&huart1, rcvBuf, BUF_SIZE);//开启DMA
    }
    /* USER CODE END USART1_IRQn 1 */
}

烧录代码,打开串口助手,将接收到的数据通过串口发送到上位机:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VR智慧景区:VR赋能文旅产业,激活消费潜能

随着国家数字化战略的不断深入实施&#xff0c;文旅产业数字化转型的步伐也在逐渐加快&#xff0c;以VR技术赋能文旅产业&#xff0c;让文旅景区线上线下双渠道融合&#xff0c;进一步呈现文化底蕴、激活消费潜能。 VR智慧景区以沉浸式、互动式、科技感的方式&#xff0c;将景区…

Vellum —— Constraint 约束

目录 Stretch Bend Pin Drag 解算器对DOP外节点的约束属性&#xff0c;只会读取起始帧的值&#xff1b; Stretch 保持点间的初始距离&#xff1b; Stiffness 越高的stiffness&#xff0c;就需要越多的迭代来收敛&#xff0c;如constraint iterations或substeps(子步会更好)…

Codeforces Round 909 (Div. 3)(A~G)(启发式合并)

1899A - Game with Integers 题意&#xff1a;给定一个数 , 两个人玩游戏&#xff0c;每人能够执行 操作&#xff0c;若操作完是3的倍数则获胜&#xff0c;问先手的人能否获胜&#xff08;若无限循环则先手的人输&#xff09;。 思路&#xff1a;假如一个数模3余1或者2&#…

Java编程中,异步操作流程中,最终一致性以及重试补偿的设计与实现

一、背景 微服务设计中&#xff0c;跨服务的调用&#xff0c;由于网络或程序故障等各种原因&#xff0c;经常会出现调用失败而需要重试。另外&#xff0c;在异步操作中&#xff0c;我们提供接口让外部服务回调。回调过程中&#xff0c;也可能出现故障。 这就要求我们主动向外…

金融业务系统: Service Mesh用于安全微服务集成

随着云计算的不断演进&#xff0c;微服务架构变得日益复杂。为了有效地管理这种复杂性&#xff0c;人们开始采用服务网格。在本文中&#xff0c;我们将解释什么是Service Mesh&#xff0c;为什么它对现代云架构至关重要&#xff0c;以及它是如何解决开发人员今天面临的一些最紧…

基于SSM的智能仓储系统研究与设计

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

RMI协议详解

前言特点应用示例存在的问题应用场景拓展 前言 RMI&#xff08;Remote Method Invocation&#xff0c;远程方法调用&#xff09;是Java中的一种远程通信协议&#xff0c;用于实现跨网络的对象方法调用。RMI协议基于Java的分布式计算&#xff0c;可以让客户端程序调用远程服务器…

如何用继承和多态来打印个人信息

1 问题 在python中的数据类型中&#xff0c;我们常常运用继承和多态。合理地使用继承和多态可以增强程序的可扩展性使代码更简洁。那么如何使用继承和多态来打印个人信息&#xff1f; 2 方法 打印基本信息添加子类&#xff0c;再定义一个class&#xff0c;可以直接从Person类继…

mac苹果笔记本应用程序在哪?有什么快捷方式吗?

苹果笔记本电脑一直以来都被广泛使用&#xff0c;而苹果的操作系统 macOS 也非常受欢迎。一台好的笔记本电脑不仅仅依赖于硬件配置&#xff0c;还需要丰富多样的应用程序来满足用户的需求。苹果笔记本应用程序在哪&#xff0c;不少mac新手用户会有这个疑问。在这篇文章中&#…

react antd下拉选择框选项内容换行

下拉框选项字太多&#xff0c;默认样式是超出就省略号&#xff0c;需求要换行全展示&#xff0c;选完在选择框里还是要省略的 .less: .aaaDropdown {:global {.ant-select-dropdown-menu-item {white-space: pre-line !important;word-break: break-all !important;}} } html…

MAC电脑连接外接显示屏,颜色显示有问题,又粉、紫色蒙版,问题处理(1)

问题描述 买了一个显示器&#xff0c;想给mac做分屏使用&#xff0c;结果连上之后发现&#xff0c;整个屏幕像是被蒙上了一层紫色的蒙版。 就像下面展示的一样&#xff1a; 解决 将显示器颜色空间改为RGB颜色空间即可。 打开显示器菜单&#xff0c;找到颜色空间选项&#…

PCL_点云分割_基于法线微分分割

一、概述 PCL_点云分割_基于法线微分分割_点云法向量微分-CSDN博客 利用不同的半径&#xff08;大的半径、小半径&#xff09;来计算同一个点的法向量差值P。判断P的范围&#xff0c;从而进行分割。 看图理解&#xff1a; 二、计算流程 1、计算P点小半径的法向量Ns 2、计…

Spring Boot - devtools 热部署

spring-boot-devtools是Spring Boot提供的一组开发工具&#xff0c;它旨在提高开发体验。这些工具包括应用程序的自动重新启动、自动刷新和远程调试等功能。下面是将spring-boot-devtools整合到Spring Boot应用程序中的步骤&#xff1a; 0、启用"Build project automatic…

nodejs+vue面向中小学课堂教学辅助软件系统的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计

主要功能有&#xff0c;管理员通过后台会对此教学辅助进行审核&#xff0c;管理员在还可以进行首页、个人中心、学生管理、教师管理、班级信息管理、科目名称管理、课程信息管理、教学资料管理、作业信息管理、作业提交管理、作业成绩管理、在线考试管理、试题管理、考试管理、…

2023年11月15号期中测验判断题(Java)

1-1 局部变量可以与成员变量重名。 正确答案&#xff1a;T 解释&#xff1a; 局部变量可以和成员变量重名&#xff0c;通常&#xff0c;为了区分局部变量和成员变量&#xff0c;会使用this关键字&#xff08;C称this指针&#xff0c;python是self关键字&#xff09;来特别声…

基于SSM的设备配件管理和设备检修系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

FISCOBCOS入门(十)Truffle测试helloworld智能合约

本文带你从零开始搭建truffle以及编写迁移脚本和测试文件,并对测试文件的代码进行解释,让你更深入的理解truffle测试智能合约的原理,制作不易,望一键三连 在windos终端内安装truffle npm install -g truffle 安装truffle时可能出现网络报错,多试几次即可 truffle --vers…

Unity 问题 之 Text 组件空格导致 自动/强制 换行 的问题处理

Unity 问题 之 Text 组件空格导致 自动/强制 换行 的问题处理 目录 Unity 问题 之 Text 组件空格导致 自动/强制 换行 的问题处理 一、简单介绍 二、问题现象 三、解决方法 四、解决后的显示效果 五、注意事项 一、简单介绍 Unity 在开发中&#xff0c;记录一些报错问题…

宝塔https403默认串站问题解决

1 前言 宝塔面板 https 串站 在这里引用宝塔官方说法:在未指定 SSL 默认站点时,未开启 SSL 的站点使用 HTTPS 会直接访问到已开启 SSL 的站点 相信使用宝塔面板的盆友,应该都遇到过宝塔这个 https 串站问题。很多盆友遇到了,但忽略了它,觉得没啥影响的,就置之不理了... …

剑指offer --- 用两个栈实现队列的先进先出特性

目录 前言 一、读懂题目 二、思路分析 三、代码呈现 总结 前言 当我们需要实现队列的先进先出特性时&#xff0c;可以使用栈来模拟队列的行为。本文将介绍如何使用两个栈来实现队列&#xff0c;并给出具体的思路和代码实现。 一、读懂题目 题目&#xff1a;用两个栈实现一…