STM32标准库学习笔记-9.DMA 直接存储器存取

news2024/9/27 17:27:39

参考教程:【STM32入门教程-2023版 细致讲解 中文字幕】

DMA(Direct Memory Access)

  • DMA(Direct Memory Access)直接存储器存取
  • DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
  • 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
  • 每个通道都支持软件触发和特定的硬件触发
  • STM32F103C8T6 DMA资源:DMA1(7个通道)
STM32存储器映射

        通俗的讲映射就是一种特殊的对应关系

        Cortex‐M3 支持4GiB 存储空间。整块4G存储器开始地址标为0x00000000,结束地址为0xFFFFFFFF,地址的位数是32位,那么2^32=4,294,967,296。

        这4GiB的空间指的是地址空间,每个地址对应一个具体的设备。CPU通过地址,然后对应到地址存储单元或者寄存器,进行读取或者写入数据即可。4GiB是它最大支持的地址数目,但是实际可能没有使用那么多。

类型

起始地址

存储器

用途

ROM

0x0800 0000

程序存储器Flash

存储C语言编译后的程序代码

0x1FFF F000

系统存储器

存储BootLoader,用于串口下载

0x1FFF F800

选项字节

存储一些独立于程序代码的配置参数

RAM

0x2000 0000

运行内存SRAM

存储运行过程中的临时变量

0x4000 0000

外设寄存器

存储各个外设的配置参数

0xE000 0000

内核外设寄存器

存储内核各个外设的配置参数

DMA框图

DMA2仅存在于大容量产品和互联型产品。

DMA简化结构图

DMA请求

数据宽度与对齐

数据转运+DMA示意图

ADC扫描模式+DMA

DMA教程代码实例:
#include "stm32f10x.h"                  // Device header

uint16_t MyDMA_Size;					//定义全局变量,用于记住Init函数的Size,供Transfer函数使用

/**
  * 函    数:DMA初始化
  * 参    数:AddrA 原数组的首地址
  * 参    数:AddrB 目的数组的首地址
  * 参    数:Size 转运的数据大小(转运次数)
  * 返 回 值:无
  */
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
	MyDMA_Size = Size;					//将Size写入到全局变量,记住参数Size
	
	/*开启时钟*/
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);						//开启DMA的时钟
	
	/*DMA初始化*/
	DMA_InitTypeDef DMA_InitStructure;										//定义结构体变量
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;						//外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据宽度,选择字节
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;			//外设地址自增,选择使能
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;							//存储器基地址,给定形参AddrB
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据宽度,选择字节
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器地址自增,选择使能
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;						//数据传输方向,选择由外设到存储器
	DMA_InitStructure.DMA_BufferSize = Size;								//转运的数据大小(转运次数)
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//模式,选择正常模式
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;								//存储器到存储器,选择使能
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//优先级,选择中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);							//将结构体变量交给DMA_Init,配置DMA1的通道1
	
	/*DMA使能*/
	DMA_Cmd(DMA1_Channel1, DISABLE);	//这里先不给使能,初始化后不会立刻工作,等后续调用Transfer后,再开始
}

/**
  * 函    数:启动DMA数据转运
  * 参    数:无
  * 返 回 值:无
  */
void MyDMA_Transfer(void)
{
	DMA_Cmd(DMA1_Channel1, DISABLE);					//DMA失能,在写入传输计数器之前,需要DMA暂停工作
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);	//写入传输计数器,指定将要转运的次数
	DMA_Cmd(DMA1_Channel1, ENABLE);						//DMA使能,开始工作
	
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);	//等待DMA工作完成
	DMA_ClearFlag(DMA1_FLAG_TC1);						//清除工作完成标志位
}
AD+DMA教程代码实例:
#include "stm32f10x.h"                  // Device header

uint16_t AD_Value[4];					//定义用于存放AD转换结果的全局数组

/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);		//开启DMA1的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0、PA1、PA2和PA3引脚初始化为模拟输入
	
	/*规则组通道配置*/
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);	//规则组序列1的位置,配置为通道0
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);	//规则组序列2的位置,配置为通道1
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);	//规则组序列3的位置,配置为通道2
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);	//规则组序列4的位置,配置为通道3
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;											//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;							//模式,选择独立模式,即单独使用ADC1
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;						//数据对齐,选择右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;			//外部触发,使用软件触发,不需要外部触发
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;							//连续转换,使能,每转换一次规则组序列后立刻开始下一次转换
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;								//扫描模式,使能,扫描规则组的序列,扫描数量由ADC_NbrOfChannel确定
	ADC_InitStructure.ADC_NbrOfChannel = 4;										//通道数,为4,扫描规则组的前4个通道
	ADC_Init(ADC1, &ADC_InitStructure);											//将结构体变量交给ADC_Init,配置ADC1
	
	/*DMA初始化*/
	DMA_InitTypeDef DMA_InitStructure;											//定义结构体变量
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;				//外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;	//外设数据宽度,选择半字,对应16为的ADC数据寄存器
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;			//外设地址自增,选择失能,始终以ADC数据寄存器为源
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;					//存储器基地址,给定存放AD转换结果的全局数组AD_Value
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;			//存储器数据宽度,选择半字,与源数据宽度对应
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;						//存储器地址自增,选择使能,每次转运后,数组移到下一个位置
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;							//数据传输方向,选择由外设到存储器,ADC数据寄存器转到数组
	DMA_InitStructure.DMA_BufferSize = 4;										//转运的数据大小(转运次数),与ADC通道数一致
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;								//模式,选择循环模式,与ADC的连续转换一致
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;								//存储器到存储器,选择失能,数据由ADC外设触发转运到存储器
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;						//优先级,选择中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);								//将结构体变量交给DMA_Init,配置DMA1的通道1
	
	/*DMA和ADC使能*/
	DMA_Cmd(DMA1_Channel1, ENABLE);							//DMA1的通道1使能
	ADC_DMACmd(ADC1, ENABLE);								//ADC1触发DMA1的信号使能
	ADC_Cmd(ADC1, ENABLE);									//ADC1使能
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
	
	/*ADC触发*/
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//软件触发ADC开始工作,由于ADC处于连续转换模式,故触发一次后ADC就可以一直连续不断地工作
}

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

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

相关文章

OpenCV图像滤波(20)模糊处理函数stackBlur()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 stackBlur() 函数用于对图像进行模糊处理。该函数对图像应用了 stackBlur 技术。stackBlur 可以生成与高斯模糊相似的结果,而且随着模…

ICMAN水位接近式检测方案(非接触式)

ICMAN水位液位接近式检测方案(非接触式) 我们的很多家用电器都会需要:液位检测 缺水&溢水提醒保护、高低液位提醒 液位传感器 像健康家电——烧水煮茶熬养生汤的烧水壶、豆浆机、养生壶等需要缺水保护和防溢液提醒; 像清洁…

ArcGis在线地图插件Maponline(好用版)

ArcGis加载插件,可在线浏览谷歌地图、天地图、高德地图、必应地图等多种,包含街道、影像、标注地图等信息(谷歌地图需自备上网手段),免费注册账号即可使用,可加载无水印底图。 与大地2000坐标无需配准直接使…

【吊打面试官系列-Memcached面试题】说说memcached 和 MySQL 的 query cache 相比,有什么优缺点?

大家好,我是锋哥。今天分享关于 【说说memcached 和 MySQL 的 query cache 相比,有什么优缺点? 】面试题,希望对大家有帮助; 说说memcached 和 MySQL 的 query cache 相比,有什么优缺点? 把 mem…

C语言典型例题45

《C程序设计教程(第四版)——谭浩强》 习题3.5 给一个不多于5位的正整数,要求: 1.求出它是几位数; 2.分别输出每一位数字; 3.按逆序输出各位数字,例如&#xff1…

修改了原神4.8版本升级数据表

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>原神4.8版本升级数据表</title> </head&…

jQuery的遍历和动画

一&#xff0c;前言 jQuery提供了一系列的方法来帮助开发者轻松地遍历和操作DOM元素集合&#xff0c;同时jQuery 提供了一些简单的方法来实现基本的动画效果&#xff0c;如淡入淡出、滑动等&#xff0c;同时还允许用户创建自定义动画序列。 二&#xff0c;遍历 &#xff08;1&…

【TCP/IP】确认应答、超时重传机制和TCP报头

TCP 相关机制 TCP 基本特点&#xff1a;有连接、可靠传输、面向字节流、全双工 有连接、面向字节流和全双工都能在前面的代码中体现有连接&#xff1a;必须要先调用 accept 建立联系才能处理面向字节流&#xff1a;会拿到 clientSocket 对象的 InputStream 和 OutputStream&a…

[书生大模型实战营][L0][Task2] Python 开发前置知识

0. 任务&#xff1a;在 InternStudio 环境中实现功能&#xff1a; python 实现 wordcount函数&#xff0c;统计英文字符串单词的使用频率&#xff0c;并返回字典&#xff1b;vscode 远程调试 InternStudio 中的 python 代码 1. wordcount 函数实现 string.punctuation 是一个…

从【人工智能】到【计算机视觉】,【深度学习】引领的未来科技创新与变革

前几天偶然发现了一个超棒的人工智能学习网站&#xff0c;内容通俗易懂&#xff0c;讲解风趣幽默&#xff0c;简直让人欲罢不能。忍不住分享给大家&#xff0c;点击这里立刻跳转&#xff0c;开启你的AI学习之旅吧&#xff01; 前言 – 人工智能教程https://www.captainbed.cn/l…

CentOS迁移案例 | 保障轨道交通安全、发挥基础设施效能,麒麟信安操作系统支撑某市轨道交通畅行无忧

为缓解城市交通拥堵难题&#xff0c;某市轨道交通公司计划新建一条贯穿城市关键区域、沿路设立20座站点的轨道交通线路&#xff0c;并基于麒麟信安操作系统构建轨道交通信号系统。 轨道交通信号系统是列车核心控制系统&#xff0c;负责列车运行的自动化控制&#xff0c;及对整…

【python】python代码打包工具cx_Freeze的介绍、原理、用法及实战案例分析

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

学习机MP3语音芯片方案:WMV硬解码芯片,N9301-316

在当今数字化时代&#xff0c;学习机已不再仅仅是静态的物品。随着技术的快速发展和不断探索创新方法&#xff0c;人们将音乐与互动学习相融合&#xff0c;为儿童创造出更加富有创意和学习机产品。其中&#xff0c;九芯电子N9301系列MP3解码芯片在这一领域的应用正在崭露头角&a…

Android常见控件(一)

目录 前言 一、TextView控件 常用属性 二、Button控件 设置点击事件的方式 1.在布局文件中指定onClick的值 2.使用匿名内部类 3.通过实现onClickListener接口 三、ImageView控件 常见属性 src与background的区别 scaleType 1.ScaleType.FIT_CENTER默认 2.ScaleTy…

Java学习高级五

API 常用API Object类 Object类的常见方法

数据结构与算法 - 双指针

一、移动零 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12]输出: [1,3,12,0,0]示例 2: 输入: nums …

Linux文件传输命令介绍(非常详细)零基础入门到精通,收藏这一篇就够了

文件传输命令 1 ftp&#xff1a;传统的文件传输协议,使用广泛但安全性较低。 sftp&#xff1a;基于SSH的安全文件传输协议,提供交互式的文件管理功能。 tftp&#xff1a;简单的文件传输协议,主要用于网络设备的固件升级。 lftp&#xff1a;功能强大的FTP客户端,支持多种文件…

2024最新外卖霸王餐小程序系统源码|霸王餐美团/饿了么系统 粉丝裂变+私域运营+持续稳定

前言&#xff1a; 外卖霸王餐小程序是一种结合了外卖点餐和优惠返利功能的微信小程序&#xff0c;为用户提供了一种便捷的点餐体验和优惠福利。 一、霸王餐系统小程序是什么&#xff1f; 霸王餐系统是指一种特殊的营销活动平台&#xff0c;商家通过该平台提供免费或大幅度折…

【深度学习】嘿马深度学习笔记第3篇:TensorFlow介绍,学习目标【附代码文档】

本教程的知识点为&#xff1a;深度学习介绍 1.1 深度学习与机器学习的区别 TensorFlow介绍 2.4 张量 2.4.1 张量(Tensor) 2.4.1.1 张量的类型 TensorFlow介绍 1.2 神经网络基础 1.2.1 Logistic回归 1.2.1.1 Logistic回归 TensorFlow介绍 总结 每日作业 神经网络与tf.keras 1.3 …

Netty深度剖析(2)— 事件调度

我们在上一节已经简单的介绍了一下 Netty 的事件调度&#xff0c;可以说 Netty 高性能的奥秘主要就在于其核心的事件循环和任务处理引擎&#xff0c;那么它究竟是如何实现的呢&#xff1f;这一节我们来详细探讨一下 Reactor 线程模型 在解释 Netty 事件循环的实现原理前&#…