(STM32笔记)十二、DMA的基础知识与用法 第三部分

news2025/1/10 14:00:26

我用的是正点的STM32F103来进行学习,板子和教程是野火的指南者
之后的这个系列笔记开头未标明的话,用的也是这个板子和教程。

DMA的基础知识与用法

  • 三、DMA程序验证
    • 1、DMA 存储器到存储器模式实验
      • (1)DMA结构体解释
      • (2)DMA结构体初始化
      • (3)程序验证
    • 2、DMA 存储器到外设模式实验
      • (1)串口初始化
      • (2)DMA结构体初始化
      • (3)程序验证

三、DMA程序验证

1、DMA 存储器到存储器模式实验

(1)DMA结构体解释

typedef struct
{
	uint32_t DMA_PeripheralBaseAddr; 	// 外设地址
	uint32_t DMA_MemoryBaseAddr; 		// 存储器地址
	uint32_t DMA_DIR; 					// 传输方向
	uint32_t DMA_BufferSize; 			// 传输数目
	uint32_t DMA_PeripheralInc; 		// 外设地址增量模式
	uint32_t DMA_MemoryInc; 			// 存储器地址增量模式
	uint32_t DMA_PeripheralDataSize; 	// 外设数据宽度
	uint32_t DMA_MemoryDataSize; 		// 存储器数据宽度
	uint32_t DMA_Mode; 					// 模式选择
	uint32_t DMA_Priority; 				// 通道优先级
	uint32_t DMA_M2M; 					// 存储器到存储器模式
} DMA_InitTypeDef;
  1. DMA_PeripheralBaseAddr
    外设地址,设定 DMA_CPAR 寄存器的值;一般设置为外设的数据寄存器地址,如果是存储器到存储器模式则设置为其中一个存储器地址。

  2. DMA_Memory0BaseAddr
    存储器地址,设定 DMA_CMAR 寄存器值;一般设置为我们自定义存储区的首地址。

  3. DMA_DIR
    传输方向选择,可选外设到存储器、存储器到外设。它设定 DMA_CCR 寄存器的DIR[1:0] 位的值。这里并没有存储器到存储器的方向选择,当使用存储器到存储器时,只需要把其中一个存储器当作外设使用即可。
    在这里插入图片描述

  4. DMA_BufferSize
    设定待传输数据数目,初始化设定 DMA_CNDTR 寄存器的值。
    这个DMA_BufferSize 就是要传输的次数。
    这个并不是指字节大小,而是指DMA的传输次数,传输的字节大小由DMA_PeripheralDataSize设置。
    DMA传输数据时,从DMA_MemoryBaseAddr+0地址开始放,直到DMA_MemoryBaseAddr+DMA_BufferSize-1,此时 DMA_CNDTR 变成0,传输结束。

  5. DMA_PeripheralInc
    如果配置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定 DMA_CCR 寄存器的 PINC 位的值;一般外设都是只有一个数据寄存器,所以一般不会使能该位。
    在这里插入图片描述

  6. DMA_MemoryInc
    如果配置为 DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定 DMA_CCR 寄存器的 MINC 位的值;我们自定义的存储区一般都是存放多个数据的,所以要使能存储器地址自动递增功能。
    在这里插入图片描述

  7. DMA_PeripheralDataSize
    外设数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_CCR 寄存器的 PSIZE[1:0] 位的值。
    在这里插入图片描述

  8. DMA_MemoryDataSize
    存储器数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_CCR 寄存器的 MSIZE[1:0] 位的值。当外设和存储器之间传数据时,两边的数据宽度应该设置为一致大小。
    在这里插入图片描述

  9. DMA_Mode
    DMA 传输模式选择,可选一次传输Normal或者循环传输Circular,它设定 DMA_CCR 寄存器的CIRC 位的值。例程我们的 ADC 采集是持续循环进行的,所以使用循环传输模式。
    在这里插入图片描述

  10. DMA_Priority
    软件设置通道的优先级,有 4 个可选优先级分别为非常高、高、中和低,它设定 DMA_CCR 寄存器的 PL[1:0] 位的值。DMA 通道优先级只有在多个 DMA 通道同时使用时才有意义,如果是单个通道,优先级可以随便设置。
    在这里插入图片描述

  11. DMA_M2M
    存储器到存储器模式,使用存储器到存储器时用到,设定 DMA_CCR 的位 14 MEN2MEN 即可启动存储器到存储器模式。
    在这里插入图片描述

(2)DMA结构体初始化

		DMA_InitTypeDef DMA_InitStructure;

		// 开启DMA时钟
		RCC_AHBPeriphClockCmd(DMA_CLOCK, ENABLE);
		// 源数据地址
    	DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)ShuZu_YouShu;	//数组有32个数,每个数都是一个字,且使用const定义(存储在FLASH)
		// 目标地址
		DMA_InitStructure.DMA_MemoryBaseAddr 		= (uint32_t)ShuZu_MeiShu;	//数组有32个数,每个数都是一个字
		// 方向:外设到存储器(这里的外设是内部的FLASH)	
		DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralSRC;
		// 传输大小	
		DMA_InitStructure.DMA_BufferSize 			= 32;						//因为数组有32个数,所以设32
		// 外设(内部的FLASH)地址递增	    
		DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Enable;
		// 内存地址递增
		DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable;
		// 外设数据单位	
		DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Word;	//因为每个数大小都是一个字,所以宽度设为Word
		// 内存数据单位
		DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Word;	 //因为每个数大小都是一个字,所以宽度设为Word
		// DMA模式,一次或者循环模式
		DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal ;			//一次传输模式
		//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  
		// 优先级:高	
		DMA_InitStructure.DMA_Priority 				= DMA_Priority_High;
		// 使能内存到内存的传输
		DMA_InitStructure.DMA_M2M 					= DMA_M2M_Enable;			//因为本次是FLASH传输到SRAM,所以是存储器到存储器
		// 配置DMA通道		   
		DMA_Init(DMA_CHANNEL, &DMA_InitStructure);
    	//清除DMA数据流传输完成标志位
    	DMA_ClearFlag(DMA_FLAG_TC);
    	// 使能DMA
		DMA_Cmd(DMA_CHANNEL,ENABLE);

其中,

  1. 需开启DMA时钟才能使用DMA功能:
// 开启DMA时钟
RCC_AHBPeriphClockCmd(DMA_CLOCK, ENABLE);
  1. ShuZu_YouShu定义如下:
uint32_t ShuZu_YouShu[32]= {0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
							0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
							0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
							0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
							0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
							0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
							0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
							0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};
  1. 需清除 DMA 标志位后再使用DMA功能,防止不必要的干扰:
//清除DMA数据流传输完成标志位
    DMA_ClearFlag(DMA_FLAG_TC);

(3)程序验证

DMA传输过后,在DEBUG界面可以看到,ShuZu_YouShu的数字传输到了ShuZu_MeiShu
在这里插入图片描述

2、DMA 存储器到外设模式实验

具体实验为 将数组中的数字 传输至 串口

(1)串口初始化

代码解释均放入注释中,正文不再复述

/**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率 为 115200
	USART_InitStructure.USART_BaudRate = 115200;
	// 配置 数据字长 为 8
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置 停止位 为 1
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置 校验位 为 0
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

(2)DMA结构体初始化

因为数据是从存储器到串口,所以设置存储器为源地址,串口的数据寄存器为目标地址。
要发送的数据有很多且都先存储在存储器中,则存储器地址指针递增;串口数据寄存器只有一个,则外设地址地址不变。
两边数据单位设置成一致,传输模式可选一次或者循环传输,只有一个 DMA 请求,优先级随便设。
最后调用 DMA_Init 函数把这些参数写到 DMA 的寄存器中,然后使能 DMA开始传输。

void USARTx_DMA_Config(void)
{
		DMA_InitTypeDef DMA_InitStructure;
	
		// 开启DMA时钟
		RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
		// 设置DMA源地址:串口数据寄存器地址*/
		DMA_InitStructure.DMA_PeripheralBaseAddr 	= USART_DR_ADDRESS;
		// 内存地址(要传输的变量的指针)
		DMA_InitStructure.DMA_MemoryBaseAddr 		= (u32)ShuZu_YouShu;
		// 方向:从内存到外设	
		DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralDST;
		// 传输数量
		DMA_InitStructure.DMA_BufferSize 			= 5000;		// 传输数量	设为 5000,这个数随便设,只要不超过数组大小就可
		// 外设地址不增	    
		DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable;
		// 内存地址自增
		DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable;
		// 外设数据单位	
		DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte;	//因为每次传输1bit,所以设为Byte
		// 内存数据单位
		DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;	 	//因为每次传输1bit,所以设为Byte
		// DMA模式,一次或者循环模式
		DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal ;				//一次传输模式
		//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;	
		// 优先级:中	
		DMA_InitStructure.DMA_Priority 				= DMA_Priority_Medium; 
		// 禁止内存到内存的传输
		DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable;
		// 配置DMA通道		   
		DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);		
		// 使能DMA
		DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
}

其中,

  1. 外设地址DMA_PeripheralBaseAddr为串口地址(需查阅开发手册),即:
    在这里插入图片描述
    在这里插入图片描述
// 外设寄存器地址
#define 	PERIPH_BASE         ((uint32_t)0x40000000)
#define 	APB2PERIPH_BASE     (PERIPH_BASE + 0x10000)
#define  	USART1_BASE         (APB2PERIPH_BASE + 0x3800)
#define  	USART_DR_ADDRESS    (USART1_BASE+0x04)
  1. ShuZu_YouShu数组填充为 A-Z共26个字母,再加一个回车
  /*填充将要发送的数据*/
  for(i=0; i < 5000; i++)
  {
    ShuZu_YouShu[i]	 = 'A'+ i%27;
    if(ShuZu_YouShu[i] == '[')
	{
		ShuZu_YouShu[i] = '\n';
	}
  }

(3)程序验证

使用USART_DMACmd函数打开USART 的DMA请求。

/* USART1 向 DMA发出TX请求 */
  USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);

它接收三个参数:

  • 第一个参数用于设置串口外设,可以是 USART1/2/3UART4/5 这 5 个参数可选。
  • 第二个参数设置串口的具体 DMA 请求,有串口发送请求 USART_DMAReq_Tx 和接收请求 USART_DMAReq_Rx 可选。
  • 第三个参数用于设置启动请求 ENABLE 或者关闭请求 DISABLE

运行该函数后 USART 的DMA 发送传输就开始了,根据配置存储器的数据会发送到串口。

验证结果如下:
在这里插入图片描述

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

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

相关文章

论文笔记(六十一)Implicit Behavioral Cloning

Implicit Behavioral Cloning 文章概括摘要1 引言2 背景&#xff1a;隐式模型的训练与推理3 隐式模型与显式模型的有趣属性4 policy学习成果5 理论见解&#xff1a;隐式模型的通用逼近性6 相关工作7 结论 文章概括 引用&#xff1a; inproceedings{florence2022implicit,titl…

高斯函数Gaussian绘制matlab

高斯 约翰卡尔弗里德里希高斯&#xff0c;&#xff08;德语&#xff1a;Johann Carl Friedrich Gau&#xff0c;英语&#xff1a;Gauss&#xff0c;拉丁语&#xff1a;Carolus Fridericus Gauss&#xff09;1777年4月30日–1855年2月23日&#xff0c;德国著名数学家、物理学家…

vue的路由守卫逻辑处理不当导致部署在nginx上无法捕捉后端异步响应消息等问题

近期对前端的路由卫士有了更多的认识。 何为路由守卫&#xff1f;这可能是一种约定俗成的名称。就是VUE中的自定义函数&#xff0c;用来处理路由跳转。 import { createRouter, createWebHashHistory } from "vue-router";const router createRouter({history: cr…

如何在 Ubuntu 22.04 上使用 LEMP 安装 WordPress 教程

简介&#xff1a; 本教程旨在指导你如何在 Ubuntu 22.04 上使用 LEMP 栈安装 WordPress。 WordPress 是一个用 PHP 编写的开源内容管理系统。LEMP 栈是 Linux&#xff0c;NGINX&#xff0c;MySQL 和 PHP 的缩写。WordPress 非常用户友好&#xff0c;并提供了多种选项&#xff…

PySide6基于QSlider实现QDoubleSlider

我在写小工具的时候&#xff0c;需要一个支持小数的滑动条。 我QSpinBox都找到了QDoubleSpinBox&#xff0c;QSlider愣是没找到对应的东西。 网上有好多对QSlider封装实现QDoubleSlider的文章。 似乎Qt真的没有这个东西&#xff0c;需要我们自行实现。 于是我也封装了一个&…

升级 Spring Boot 3 配置讲解 —— 支持断点传输的文件上传和下载功能

学会这款 &#x1f525;全新设计的 Java 脚手架 &#xff0c;从此面试不再怕&#xff01; 在现代 Web 应用中&#xff0c;文件上传和下载是非常常见的需求。然而&#xff0c;当文件较大时&#xff0c;传统的上传下载方式可能会遇到网络不稳定或传输中断的问题。为了解决这些问题…

Backend - C# EF Core 执行迁移 Migrate

目录 一、创建Postgre数据库 二、安装包 &#xff08;一&#xff09;查看是否存在该安装包 &#xff08;二&#xff09;安装所需包 三、执行迁移命令 1. 作用 2. 操作位置 3. 执行&#xff08;针对visual studio&#xff09; 查看迁移功能的常用命令&#xff1a; 查看…

KG-CoT:基于知识图谱的大语言模型问答的思维链提示

一些符号定义 知识图谱实体数量&#xff1a; n n n 知识图谱中关系类型数量&#xff1a; m m m 三元组矩阵&#xff1a; M ∈ { 0 , 1 } n n m \textbf{M} \in \{0, 1\}^{n \times n \times m} M∈{0,1}nnm&#xff0c; M i j k 1 M_{ij}^k 1 Mijk​1则说明实体 i i i和实…

HTML+CSS+JS制作中国传统节日主题网站(内附源码,含5个页面)

一、作品介绍 HTMLCSSJS制作一个中国传统节日主题网站&#xff0c;包含首页、节日介绍页、民俗文化页、节日活动页、联系我们页等5个静态页面。其中每个页面都包含一个导航栏、一个主要区域和一个底部区域。 二、页面结构 1. 顶部横幅区 包含传统中国风格的网站标题中国传统…

大模型WebUI:Gradio全解11——Chatbot:融合大模型的多模态聊天机器人(1)

大模型WebUI&#xff1a;Gradio全解11——Chatbots&#xff1a;融合大模型的聊天机器人&#xff08;1&#xff09; 前言本篇摘要11. Chatbot&#xff1a;融合大模型的多模态聊天机器人11.1 gr.ChatInterface()快速创建Chatbot11.1.1 定义聊天函数1. 随机回答“是”或“否”的聊…

springboot + vue+elementUI图片上传流程

1.实现背景 前端上传一张图片&#xff0c;存到后端数据库&#xff0c;并将图片回显到页面上。上传组件使用现成的elementUI的el-upload。、 2.前端页面 <el-uploadclass"upload-demo"action"http://xxxx.xxx.xxx:9090/file/upload" :show-file-list&q…

开源生成式物理引擎Genesis,可模拟世界万物

这是生成大模型时代 —— 它们能生成文本、图像、音频、视频、3D 对象…… 而如果将所有这些组合到一起&#xff0c;我们可能会得到一个世界&#xff01; 现在&#xff0c;不管是 LeCun 正在探索的世界模型&#xff0c;还是李飞飞想要攻克的空间智能&#xff0c;又或是其他研究…

【PPTist】批注、选择窗格

前言&#xff1a;本篇文章研究批注和选择窗格两个小功能 一、批注 批注功能就是介个小图标 点击可以为当前页的幻灯片添加批注&#xff0c;还能删除之前的批注 如果我们增加了登录功能&#xff0c;还可以在批注上显示当前的用户名和头像&#xff0c;不过现在是写死的。 左侧…

Vue进阶(贰幺贰)npm run build多环境编译

文章目录 一、前言二、实施三、总结&#xff1a;需要打包区分不同环境四、拓展阅读 一、前言 项目开发阶段&#xff0c;会涉及打包部署到多个环境应用场景&#xff0c;在不同环境中&#xff0c;需要进行项目层面的区分&#xff0c;做不同的操作&#xff0c;可以利用打包的--mo…

Elasticsearch—索引库操作(增删查改)

Elasticsearch中Index就相当于MySQL中的数据库表 Mapping映射就类似表的结构。 因此我们想要向Elasticsearch中存储数据,必须先创建Index和Mapping 1. Mapping映射属性 Mapping是对索引库中文档的约束&#xff0c;常见的Mapping属性包括&#xff1a; type&#xff1a;字段数据类…

“AI智慧组卷系统:让考试变得更简单、更公平!

大家好&#xff0c;我是一名资深的产品经理&#xff0c;今天咱们就来聊聊教育领域的一款黑科技产品——AI智慧组卷系统。在这个信息技术飞速发展的时代&#xff0c;AI技术已经渗透到了我们生活的方方面面&#xff0c;教育行业也不例外。下面我就用大白话给大家介绍一下这个AI智…

单元测试概述入门

引入 什么是测试&#xff1f;测试的阶段划分&#xff1f; 测试方法有哪些&#xff1f; 1.什么是单元测试&#xff1f; 单元测试&#xff1a;就是针对最小的功能单元&#xff08;方法&#xff09;&#xff0c;编写测试代码对其正确性进行测试。 2.为什么要引入单元测试&#x…

三、Angular 路由

一、简介 Angular 的路由服务是一个可选的服务&#xff0c;它用来呈现指定的 URL 所对应的视图。它并不是Angular 核心库的一部分&#xff0c;而是位于 angular/router 包中。像其他 Angular 包一样&#xff0c;路由服务在用户需要时才从此包中导入。 [1]. 创建路由模块 默认…

【MATLAB】绘制投资组合的有效前沿

文章目录 一、数据准备二、有效前沿三、代码3.1 数据批量读取、预处理3.2 绘制可行集3.3 绘制有效前沿3.4 其它-最大夏普率 一、数据准备 准备多个股票的的历史数据&#xff0c;目的就是找到最优的投资组合。 下载几个标普500里面的公式的股票数据吧&#xff0c;下载方法也可…

JuiceFS 2024:开源与商业并进,迈向 AI 原生时代

即将过去的 2024 年&#xff0c;是 JuiceFS 开源版本推出的第 4 年&#xff0c;企业版的第 8 个年头。回顾过去这一年&#xff0c;JuiceFS 社区版依旧保持着快速成长的势头&#xff0c;GitHub 星标突破 11.1K&#xff0c;各项使用指标增长均超过 100%&#xff0c;其中文件系统总…