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

我用的是正点的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);// 使能DMADMA_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);// 配置串口的工作参数// 配置波特率 为 115200USART_InitStructure.USART_BaudRate = 115200;// 配置 数据字长 为 8USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 配置 停止位 为 1USART_InitStructure.USART_StopBits = USART_StopBits_1;// 配置 校验位 为 0USART_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);		// 使能DMADMA_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.mzph.cn/news/892269.shtml

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

相关文章

MySQL 如何赶上 PostgreSQL 的势头?

原文地址 我与 MySQL 社区的前辈交谈时&#xff0c;经常遇到这个问题&#xff1a;「为什么 MySQL 这么棒&#xff0c;而且&#xff08;至少根据 DB-Engines 的计算&#xff09;仍然比 PostgreSQL 更流行&#xff1b;但它的地位在下降&#xff0c;PostgreSQL 却势不可挡地越来越…

完全二叉树的删除

&#xff08;1&#xff09;删除叶子节点 找到要删除的节点 targetNode找到要删除节点的父节点parent&#xff08;父节点是否存在&#xff09;要删除的节点是父节点的左子树还是右子树如果是左子树&#xff0c;则parent.leftnull;如果是右子树则parent.rightnull。 &#xff08;…

Docker入门之docker基本命令

Docker入门之docker基本命令 官方网站&#xff1a;https://www.docker.com/ 1. 拉取官方镜像并创建容器&#xff08;以redis为例&#xff09; 拉取官方镜像 docker pull redis# 如果不需要添加到自定义网络使用这个命令&#xff0c;如需要&#xff0c;直接看第二步 docker r…

玩转大语言模型——ollama导入huggingface下载的模型

ollama导入huggingface模型 前言gguf模型查找相关模型下载模型 导入Ollama配置参数文件导入模型查看导入情况 safetensfors模型下载模型下载llama.cpp配置环境并转换 前言 ollama在大语言模型的应用中十分的方便&#xff0c;但是也存在一定的问题&#xff0c;比如不能使用自己…

DFS之迭代加深、双向DFS、IDA*

迭代加深 迭代加深&#xff1a; 170. 加成序列 满足如下条件的序列 X X X&#xff08;序列中元素被标号为 1 、 2 、 3 … m 1、2、3…m 1、2、3…m&#xff09;被称为“加成序列”&#xff1a; X [ 1 ] 1 X[1]1 X[1]1 X [ m ] n X[m]n X[m]n X [ 1 ] < X [ 2 ] < …

Ansible之批量管理服务器

文章目录 背景第一步、安装第二步、配置免密登录2.1 生成密钥2.2 分发公钥2.3 测试无密连接 背景 Ansible是Python强大的服务器批量管理 第一步、安装 首先要拉取epel数据源&#xff0c;执行以下命令 yum -y install epel-release安装完毕如下所示。 使用 yum 命令安装 an…

【Linux网络编程】第二十二弹---深入理解 I/O 多路转接之 epoll:系统调用、工作原理、代码演示及应用场景

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】 目录 1、I/O 多路转接之 epoll 1.1、epoll 初识 1.2、epoll 的相关系统调用 1.2.1、epoll_create 1.2.2、epol…

双向列表的实现(C++)

一.实现思路 主要是一个空间存储一个数值&#xff0c;然后为了索引后面的数据单元和前面的数据单元&#xff0c;所以在每个空间里面还要存储前面和后面数据单元的指针&#xff0c;就形成了每个数据单元 后面就是要管理的是双向列表的头结点和尾节点&#xff0c;方便实现后面的头…

国产信创实践(国能磐石服务器操作系统CEOS +东方通TongHttpServer)

替换介绍&#xff1a; 国能磐石服务器操作系统CEOS 对标 Linux 服务器操作系统&#xff08;Ubuntu, CentOS&#xff09; 东方通TongHttpServer 对标 Nginx 负载均衡Web服务器 第一步&#xff1a; 服务器安装CEOS映像文件&#xff0c;可直接安装&#xff0c;本文采用使用VMware …

Linux——修改USB网卡设备节点名称

修改驱动&#xff1a; 测试&#xff1a; 参考资料&#xff1a; https://blog.csdn.net/ablexu2018/article/details/144868950

上手体验微软全新整合的王炸平台Fabric

体验确实不错&#xff0c;微软强大的生态能力。 把可视化&#xff0c;数仓&#xff0c;数据胡&#xff0c;数据工厂&#xff0c;机器学习&#xff0c;数据监控等技术都整合到一个平台了。所有数据全都存储在统一的one lake数据中心&#xff0c;消除数据孤岛问题。而且不同角色可…

浅析PCIe链路均衡技术原理与演进

在现代计算机硬件体系的持续演进中&#xff0c;PCIe技术始终扮演着核心角色&#xff0c;其作为连接 CPU 与各类周边设备的关键高速通信链路&#xff0c;不断推动着计算机性能边界的拓展。而 PCIe Link Equalization均衡技术&#xff0c;作为保障数据在高速传输过程中准确性与稳…

东京大学联合Adobe提出基于指令的图像编辑模型InstructMove,可通过观察视频中的动作来实现基于指令的图像编辑。

东京大学联合Adobe提出的InstructMove是一种基于指令的图像编辑模型&#xff0c;使用多模态 LLM 生成的指令对视频中的帧对进行训练。该模型擅长非刚性编辑&#xff0c;例如调整主体姿势、表情和改变视点&#xff0c;同时保持内容一致性。此外&#xff0c;该方法通过集成蒙版、…

Wireshark 学习笔记1

1.wireshark是什么 wireshark是一个可以进行数据包的捕获和分析的软件 2.基本使用过程 &#xff08;1&#xff09;选择合适的网卡 &#xff08;2&#xff09;开始捕获数据包 &#xff08;3&#xff09;过滤掉无用的数据包 &#xff08;4&#xff09;将捕获到的数据包保存为文件…

学习threejs,导入babylon格式的模型

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.BabylonLoader babyl…

Redis缓存穿透、缓存击穿、缓存雪崩

缓存穿透 定义&#xff1a; 查询一个不存在的数据&#xff0c;mysql查询不到数据也不会直接写入缓存&#xff0c;就会导致每次请求都查数据库 例子&#xff1a; 一个get请求&#xff1a;api/news/getById/-1 解决方案 方案一&#xff1a;缓存空数据 缓存空数据&#xff0c;查…

用Kimi做研究:准实验设计的智能解决方案

目录 1.研究策略设计 2.过程框架设计 3.背景变量 4.细节设计 准实验设计是一种介于实验与观察研究之间的研究方法&#xff0c;准实验设计是在无法完全控制实验条件的情况下进行因果关系的探索。与传统实验设计相比&#xff0c;准实验设计不具备随机分配实验对象到各处理组的…

RIS智能无线电反射面:原理、应用与MATLAB代码示例

一、引言 随着无线通信技术的快速发展,人们对通信系统的容量、覆盖范围、能效以及安全性等方面的要求日益提高。传统的无线通信系统主要通过增加基站数量、提高发射功率和优化天线阵列等方式来提升性能,但这些方法面临着资源有限、能耗高和成本上升等挑战。因此,探索新的无线…

解决nginx多层代理后应用部署后访问发现css、js、图片等样式加载失败

一般是采用前后端分离部署方式&#xff0c;被上一层ng代理后&#xff0c;通过域名访问报错&#xff0c;例如&#xff1a;sqx.com.cn/应用代理路径。 修改nginx配置&#xff0c;配置前端页面的路径&#xff1a; location / {proxy_pass http://前端页面所在服务器的IP:PORT;pro…

IoT平台在设备远程运维中的应用

IoT平台是物联网技术的核心组成部分&#xff0c;实现了设备、数据、应用之间的无缝连接与交互。通过提供统一的设备管理、数据处理、安全监控等功能&#xff0c;IoT平台为企业构建了智能化、可扩展的物联网生态系统。在设备远程运维领域&#xff0c;IoT平台发挥着至关重要的作用…