STM32速成笔记—DMA

news2024/9/22 3:43:17

文章目录

  • 一、什么是DMA
  • 二、DMA有什么作用
  • 三、STM32的DMA
    • 3.1 DMA请求
    • 3.2 DMA通道
    • 3.3 仲裁器
  • 四、DMA配置
    • 4.1 DMA配置步骤
    • 4.2 DMA结构体成员
  • 五、DMA配置程序
    • 5.1 ADC1初始化程序
    • 5.2 DMA初始化程序

一、什么是DMA

DMA全程Direct Memory Access,即直接存储器访问。简单来讲,它的功能是把数据从一个地址搬运到另一个地址。通常有三个传输方向,分别是内存到内存,内存到外设和外设到内存。
DMA示意图

二、DMA有什么作用

直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。

比如在串口接收或者发送时可以直接利用DMA将接收内容直接搬运到接收数组。或者利用DMA将准备发送的数据搬运到发送的缓冲区。再或者利用DMA把数据搬运到特定的地址,或者从特定的地址利用DMA搬运数据出来。总而言之,在平时的开发过程中,DMA是非常常用的。

三、STM32的DMA

STM32F103ZET6有两个DMA,12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

STM32F103ZET6的DMA特性

3.1 DMA请求

DMA请求

如果一个外设想要通过DMA传输数据,必须先给DMA控制器发送DMA请求。DMA控制器收到请求后,会给外设一个应答信号。当外设收到应答信号后,也会给DMA控制器一个应答信号。当DMA控制器收到外设的应答信号后,启动DMA传输。

前面介绍STM32F103ZET6有两个DMA,12个通道,同的 DMA 控制器的通道对应着不同的外设请求。根据中文参考手册,对应关系如下
DMA1对应外设
DMA1对应外设
DMA2对应外设
DMA2对应外设

3.2 DMA通道

DMA具有12个独立可编程的通道,每个通道对应不同外设的DMA请求。虽然每个通道可以接收多个外设的DMA请求,但是同一时间只能接收一个。
DMA通道

3.3 仲裁器

当有多个DMA请求时,需要仲裁器来决定响应的先后顺序。仲裁器决定相应顺序的方法有两种

  • 软件判定
    软件中可以通过设置DMA_CCRx寄存器来设置DMA通道的优先级。共有四个优先级可以设置,分别是非常高,高,中和低。
  • 硬件判定
    当遇到两个或者多个相同优先级的DMA通道请求时,仲裁器根据DMA通道的编号来决定响应顺序。DMA通道编号越低,优先级越高。另外,DMA1拥有比DMA2更高的优先级。
    仲裁器

四、DMA配置

4.1 DMA配置步骤

  • 使能DMA时钟
  • 初始化DMA通道,包括配置通道,外设和内存地址,传输数据量等
  • 使能外设DMA功能
  • 开启DMA通道传输
  • 查询DMA通道状态

4.2 DMA结构体成员

  • DMA_PeripheralBaseAddr:外设地址,外设地址,通过DMA_CPAR寄存器设置,一般设置为外设的数据寄存器地址,比如要进行串口DMA 传输,那么外设基地址为串口接收/发送数据存储器USART1->DR 的地址,表示方法为&USART1->DR。如果是存储器到存储器模式则设置为其中一个存储区地址。
  • DMA_Memory0BaseAddr:存储器地址,通过DMA_CMAR寄存器设置,一般设置为我们自定义存储区的首地址,即我们存放DMA传输数据的内存地址。比如我们定义一个u32类型数组,直接写数组首地址(直接使用数组名)即可,在DMA传输的时候就可以发送数组数据,或者把数组用来接收其他数据。
  • DMA_DIR:数据传输方向选择,可选择外设到存储器、存储器到外设以及存储器到存储器。通过设定DMA_CCR寄存器的DIR[1:0]位的值决定。
  • DMA_BufferSize:用来设置一次传输数据的大小,通过DMA_CNDTR寄存器设置。
  • DMA_PeripheralInc:用来设置外设地址是递增还是不变,通过DMA_CCR寄存器的PINC位设置,如果设置为递增,那么下一次传输的时候地址加1。通常外设只有一个数据寄存器,所以一般不会使能该位,即配置为DMA_PeripheralInc_Disable。
  • DMA_MemoryInc:用来设置内存地址是否递增,通过DMA_CCR寄存器的MINC位设置。我们自定义的存储区一般都是存放多个数据的,所以需要使能存储器地址自动递增功能,即配置为DMA_MemoryInc_Enable。
  • DMA_PeripheralDataSize:外设数据宽度选择,可以为字节(8位)、半字(16位)、字(32位),通过DMA_CCR寄存器的PSIZE[1:0]位设置。
  • DMA_Mode:DMA传输模式选择,可选择一次传输或者循环传输,通过DMA_CCR寄存器的CIRC位来设定。比如我们要从内存(存储器)中传输64个字节到串口,如果设置为循环传输,那么它会在64个字节传输完成之后继续从内存的第一个地址传输,如此循环。这里我们设置为一次传输完成之后不循环。所以设置值为DMA_Mode_Normal。
  • DMA_Priority:用来设置DMA通道的优先级,有低,中,高,超高四种级别,可通过DMA_CCR寄存器的PL[1:0]位来设定。DMA优先级只有在多个DMA数据流同时使用时才有意义。
  • DMA_M2M:用来设置存储器到存储器模式,使用存储器到存储器时用到,设定DMA_CCR 的位 14 MEN2MEN 即可启动存储器到存储器模式。

五、DMA配置程序

这里以配置DMA,将ADC采集到的数据搬运到内存中的某一个数组中为例,讲解一下DMA的配置和使用方法。

5.1 ADC1初始化程序

ADC使用TIM4的通道4触发,具体配置可见本系列另一篇文章STM32速成笔记—ADC。这里在之前配置的基础上需要开启ADC的DMA传输,在初始化ADC时加上下面的程序

ADC_DMACmd(ADC1,ENABLE);   // 使能ADC的DMA传输

ADC初始化程序如下

/*
 *==============================================================================
 *函数名称:ADC1_Init
 *函数功能:初始化ADCx
 *输入参数:无
 *返回值:无
 *备  注:TIM4通道4触发AD转换,使能了DMA
 *==============================================================================
 */
void ADC1_Init(void)
{
	// 结构体定义
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStructure;
	
	// 开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);
	
	// 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	// 规则通道配置
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);
	
	// GPIO配置
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道1
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	// ADC参数配置
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式	
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4;   // TIM2通道2触发
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐	
	ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1 
	ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化
	
	// 使能外部触发
	ADC_ExternalTrigConvCmd(ADC1, ENABLE);
	ADC_DMACmd(ADC1,ENABLE);   // 使能ADC的DMA传输
	ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器
	
	// ADC校准
	ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器
	while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态
	
	ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态
	while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序

	ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能
}

5.2 DMA初始化程序

由上面的介绍可知,ADC1是DMA1的通道1,我们配置一下DMA1的通道1,使能传输完成中断。

/*
 *==============================================================================
 *函数名称:DMA1_Init
 *函数功能:DMA1初始化
 *输入参数:souAddr:数据源地址;desAddr:数据目的地址
 *返回值:无
 *备  注:数据传输宽度为16位,外设到内存,循环传输,使能了传输完成中断
 *==============================================================================
 */
void DMA1_Init (u32 souAddr,u32 desAddr)
{
	// 结构体定义
	DMA_InitTypeDef DMA_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	// 使能DMA时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	//DMA1初始化
	DMA_DeInit(DMA1_Channel1);
	DMA_InitStructure.DMA_PeripheralBaseAddr = souAddr;   // 数据源地址
	DMA_InitStructure.DMA_MemoryBaseAddr = desAddr;   // 目的地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;   //   传输方向(外设到内存)
	DMA_InitStructure.DMA_BufferSize = 128;   // 一次传输数据大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;   // 外设地址不自增
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;   // 内存地址自增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;   // 外设数据宽度选择
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;   // 内存数据宽度选择
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;   // DMA模式:循环传输
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;   // 优先级:高
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;   // 禁止内存到内存的传输
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);   // 配置DMA1
	
	// 使能传输完成中断
	DMA_ITConfig(DMA1_Channel1,DMA_IT_TC, ENABLE);
	
	// NVIC配置
	NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	// 使能DMA1通道1
	DMA_Cmd(DMA1_Channel1,ENABLE);
}

// DMA1中断服务函数
void  DMA1_Channel1_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA1_IT_TC1)!=RESET)
	{
		DMA_Cmd(DMA1_Channel1,DISABLE);
		while (1)
		{}
	}
	// 清除中断标志位
	DMA_ClearITPendingBit(DMA1_IT_TC1);
}

定义一个存储AD转换结果的数组,初始化时,程序如下

u16 gAdcAdValue[128];   // 存储AD值

DMA1_Init(ADC1->DR,(u32)&gAdcAdValue);   // DMA1初始化

中断服务函数中将存储标志位置1表示存储完成

u8 gDmaAdcSaveFlag = 0;   // ADC数据存储标志位

// DMA1中断服务函数
void  DMA1_Channel1_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA1_IT_TC1)!=RESET)
	{
		gDmaAdcSaveFlag = 1;   // 存储标志位置1,表示存储完成
	}
	// 清除中断标志位
	DMA_ClearITPendingBit(DMA1_IT_TC1);
}

上面的配置就可以实现ADC采集,DMA将采集结果搬运到内存中的一个数组里面。

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

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

相关文章

如何保护阿里云服务器免受DDoS攻击和恶意访问?有哪些防护措施?

如何保护阿里云服务器免受DDoS攻击和恶意访问?有哪些防护措施?   [本文由阿里云代理商[聚搜云]撰写]   随着互联网技术的不断发展,网络安全问题日益严峻,保护服务器免受DDoS攻击和恶意访问成为了每个企业和网站建设者的关注重…

W3B x Sui Hacker House|深入了解Sui和Move语言

Web3 Builders(W3B)作为Hacker House的践行者,将于6月23日(周五)早上8点(GMT8)举办首期 W3B x Sui Hacker House 系列活动分享会。本期活动邀请到Sui联合创始人Sam Blackshear(Move语…

【机器学习】机器学习的基本概念

机器学习是我们现在接触人工智能领域首先要去掌握的知识,下面是我学习记录的一些关于机器学习的基础、常见的概念和定义。 目录 机器学习定义 机器学习过程 假设关系 训练数据 损失函数(正向传播) 优化(反向传播&#xff0…

PC市场:寒冬敲响警钟,蓄势待发的复兴之路

近年来,PC市场的增长确实放缓,但这并不意味着它已经进入了寒冬。相反,PC市场正在蓄势待发,寻找复兴之路。 首先,PC市场仍然是一个巨大的市场。尽管移动设备的普及使得一些用户更多地使用手机和平板电脑,但…

C++:虚函数

C面向对象的三个特性,封装继承多态。在继承的关系中,所有的东西都可以被继承下来,如数据可以被继承下来在内存,而函数的继承则是继承调用权。 虚函数主要是通过虚函数表来实现,每个类都有自己的虚表,当你创…

RevCol:大模型架构设计新范式,给神经网络架构增加了一个维度!

点击蓝字 关注我们 关注并星标 从此不迷路 计算机视觉研究院 公众号ID|计算机视觉研究院 学习群|扫码在主页获取加入方式 论文地址:https://arxiv.org/pdf/2212.11696.pdf 项目代码:https://github.com/megvii-research/RevCol 计…

把金融航母开进智能峡湾,总共分几步?

试想一下,有这么一家街头小店。夫妻两个勤奋经营,诚信待客,广受街里街坊的欢迎。他们流水稳定,蒸蒸日上,商业信誉很好,甚至是非物质文化遗产的传承者。这样一家店,在扩大经营,拓展业…

SpringBoot集成WebSocket实现消息实时推送(提供Gitee源码)

前言:在最近的工作当中,客户反应需要实时接收消息提醒,这个功能虽然不大,但不过也用到了一些新的技术,于是我这边写一个关于我如何实现这个功能、编写、测试到部署服务器,归纳到这篇博客中进行总结。 目录 …

C语言代码函数栈帧的创建与销毁(修炼内功)

目录 在前期的学习中我们可能有很多困惑 例如:局部变量是怎么创建的 为什么局部变量的值是随机值 函数是怎么样传参的 传参的顺序是什么 形参和实参的关系是什么 函数调用是怎么做的 函数掉调用结束后怎么返回的 这篇博客我们来修炼自己的内功,掌握好这篇…

【MATLAB第42期】基于MATLAB的贝叶斯优化决策树分类算法与网格搜索、随机搜索对比,含对机器学习模型的评估度量介绍

【MATLAB第42期】基于MATLAB的贝叶斯优化决策树分类算法与网格搜索、随机搜索对比,含对机器学习模型的评估度量介绍 网格搜索、随机搜索和贝叶斯优化是寻找机器学习模型参数最佳组合、交叉验证每个参数并确定哪一个参数具有最佳性能的常用方法。 一、 评估指标 1、…

STM32F103基于HAL工程挂载FatFS驱动SD卡

STM32F103基于HAL工程挂载FatFS驱动SD卡 📌基于标准库驱动《STM32挂载SD卡基于Fatfs文件系统读取文件信息》 🎬驱动实验效果: 🔨通过STM32cubemx配置SPI1作为访问SD、TF卡通讯方式。 🔧在STM32cubemx配置中挂载中…

Scala入门【变量和数据类型】

目录 Scala基本认知 Hello Scala 方法的定义 伴生对象 Java: Scala: 2、变量和数据类型 2.1、注释 2.2、变量和常量 Java中的变量和常量 Scala基本语法 2.3、标识符 2.4、字符串 基本语法 2.5、标准输入输出 基本语法 2.6、文件的读写 …

6-8 二分查找

今天是端午节,祝大家端午节快乐~ 竟然这样,还不点点赞。 言归正传┏ (゜ω゜)☞ 目录 引入 二分查找算法思想 时间复杂度O(logN) 二分查找算法描述 二分查找算法的框架如下: 例题1: 例题2&#x…

秒懂SpringBoot之如何集成SpringDoc(全网目前最新最系统最全面的springdoc教程)

[版权申明] 非商业目的注明出处可自由转载 出自:shusheng007 文章目录 概述概念解释SpringDoc使用简单集成配置文档信息配置文档分组使用注解TagOperationSchemaParameterParametersApiResponses 和ApiResponse 认证授权无需认证需要认证 总结源码 概述 近来颇为懈…

13. WebGPU 正交投影

在上一篇文章中,讨论了矩阵的工作原理。讨论了如何通过 1 个矩阵和一些神奇的矩阵数学来完成平移、旋转、缩放,甚至从像素到裁剪空间的投影。实现 3D 操作 只需要再向前迈一小步。 在之前的 2D 示例中,将 2D 点 (x, y) 乘以 3x3 矩阵。要实现…

【机器学习】——续上:卷积神经网络(CNN)与参数训练

目录 引入 一、CNN基本结构 1、卷积层 2、下采样层 3、全连接层 二、CNN参数训练 总结 引入 卷积神经网络(CNN)是一种有监督深度模型框架,尤其适合处理二维数据问题,如行人检测、人脸识别、信号处理等领域,是带…

19c rac添加节点

在正常的节点 [rootdb1 ~]# xhost access control disabled, clients can connect from any host [rootdb1 ~]# su - grid ASM1:/home/griddb1>export DISPLAY:1.0 ASM1:/home/griddb1>$ORACLE_HOME/gridSetup.sh [rootdb2 ~]# /tmp/GridSetupActions2021-09-16_…

基于Nginx1.22+PHP8+MySQL8安装Discuz! X3.5

基于Nginx1.22PHP8MySQL8安装Discuz! X3.5 1. 安装PHP82. 安装MySQL83. 配置Nginx1.224. 安装Discuz! X3.5 1. 安装PHP8 更新系统: yum update安装EPEL存储库: yum install epel-release安装Remi存储库(提供了最新的 PHP 版本)&…

【十三】druid 原理解析

druid 原理解析 先前写了一篇博客关于druid集成相关的,这里来分析一下druid原理,结合这两篇文章希望读者能够把druid理解透彻。 一、druid介绍 Druid连接池是阿里巴巴开源的数据库连接池项目。Druid连接池为监控而生,内置强大的监控功能&…

【前端JS交互篇】ECMA核心语法 ——常量、变量、数据类型、各种弹框

一、javascript简介 1.1 Javascript简史 在WEB日益发展的同时,网页的大小和复杂性不断增加,受制于网速的限制,为完成简单的表单验证而频繁地与服务器交换数据只会加重用户的负担,当时走在技术革新最前沿的Netscape(网…