【STM32】DMA数据转运(存储器到存储器)

本篇博客重点在于标准库函数的理解与使用,搭建一个框架便于快速开发

目录

DMA简介

DMA时钟使能

DMA初始化

转运起始和终止的地址

转运方向

 数据宽度

传输次数

转运触发方式

 转运模式

通道优先级 

DMA初始化框架 

选择开启DMA通道 

更改转运次数

DMA应用实例-存储器到存储器转运

DMA.h

DMA.c 

main.c 


DMA简介

  • DMA(Direct Memory Access)直接存储器存取 ,可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预。这使CPU可以去做其他复杂的事情。
  • 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
  • 每个通道都支持软件触发和特定的硬件触发

STM32F103C8T6 DMA资源:DMA1(7个通道),具体DMA资源可以查看参考手册

 DMA框图

DMA时钟使能

已知DMA在AHB总线(如图)

由RCC时钟树知,应使能外部时钟 

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

DMA初始化

外设和存储器只是个名字,我们要确定数据从哪里来到哪里去,配置数据来源和去向的地址即可

 实现外设存储器,存储器到存储器,存储器到外设的转运

转运起始和终止的地址

闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标

    //地址均为32位//外设存储器的基地址DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;//外设存储器的指针自增使能(转运后将地址指向下一个数据)  Enable-自增  Dissable-不自增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//存储器的基地址DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;//存储器的指针自增使能(转运后将地址指向下一个),与外设存储器一样来理解DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

一次转运次数结束后,自增的指针也会回到最初的位置,以便下一次转运。

转运并不会把原有数据拿走,是复制了一份

在传输过程中,当开启通道(DMA_CCRx的EN=1)时,不能写更改地址。需关闭通道后再更改地址。

转运方向

DMA_DIR_PeripheralDST

peripheral: source

存储器到外设

DMA_DIR_PeripheralSRC

 peripheral : destination

外设到存储器

外设和存储器只是个名字而已,重要的是你填的地址 

	//转运方向:外设到存储器还是存储器到外设DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

 数据宽度

外设和存储器的传输宽度

字节(Byte)8位
半字(HalfWord)16位
全字(Word)32位
	//外设存储器的数据宽度DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//储存器的数据宽度DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

传输次数

可编程的数据传输数目:0~65535

这个寄存器只能在通道不工作(DMA_CCRx的EN=0)时写入。通道开启后该寄存器变为只读,指示剩余的待传输字节数目。寄存器内容在每次DMA传输后递减。 数据传输结束后,寄存器的内容或者变为0;或者当该通道配置为自动重加载模式时,寄存器的内容将被自动重新加载为之前配置时的数值。 当寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输

 每转运一次就会减去一,为0停止

	//转运次数DMA_InitStructure.DMA_BufferSize = Size;

转运触发方式

每个通道都同样支持软件触发,每个通道都直接连接专用的硬件DMA请求。

软件触发

软件触发常用于存储器与存储器之间转运,其不能与循环模式同时使用

//软件触发使能  
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;

硬件触发

硬件生成DMA请求常用于非存储器与存储器之间转运,其能与循环模式同时使用


DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //软件触发不使能 (硬件触发,需调用对应外设请求)//然后再使能外设到DMA的请求的函数,比如ADC
ADC_DMACmd(ADC1, ENABLE);

如上面DMA框图所示,由外设发出DMA请求

硬件触发请求DMA转运前,需要初始化特定通道,如ADC1的外设请求信号需要DMA1的通道1.

 转运模式

DMA_Mode_Normal正常模式转运到传输次数为0,停止转运
DMA_Mode_Circular循环模式传输次数为0后变为设定值(自动重装)
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

 当通道配置为非循环模式(正常模式)时,传输结束后(即传输次数变为0)将不再产生DMA转运。要开始新的DMA转运,需要在关闭DMA通道的情况下,在DMA_CNDTRx寄存器中重新写入传输次数

在循环模式下,最后一次传输结束时,DMA_CNDTRx寄存器的内容会自动地被重新加载为其初始数值,内部的当前外设/存储器地址寄存器也被重新加载DMA_CPARx/DMA_CMARx寄存器设定的初始基地址。

通道优先级 

在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、 中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推)

仲裁器

每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

	//转运通道优先级:有四个DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;

DMA1的通道1 

//将上面参数配置到通道1
DMA_Init(DMA1_Channel1, &DMA_InitStructure);

DMA初始化框架 

	DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;DMA_InitStructure.DMA_MemoryBaseAddr = 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通道 

使能后将开始转运且不可更改转运地址和转运次数,这里先不使能

DMA_Cmd(DMA1_Channel1, DISABLE);//不开启通道

更改转运次数

进入函数之前,DMA未启动或者已经启动转运了但转运次数为0,如果DMA使能后要更改转运次数,应先失能,更改转运次数后再使能,最后启动转运

调用该函数一次,设置的转运次数变为0,即转运完成

void MyDMA_Transfer(void)
{DMA_Cmd(DMA1_Channel1, DISABLE);DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);//设置DMAx通道y的转运次数DMA_Cmd(DMA1_Channel1, ENABLE);while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);//等待转运完成DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位
}

DMA应用实例-存储器到存储器转运

DMA.h

#ifndef __MYDMA_H
#define __MYDMA_Hvoid MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size);
void MyDMA_Transfer(void);#endif

DMA.c 

#include "stm32f10x.h"                  // Device headeruint16_t MyDMA_Size;void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{MyDMA_Size = Size;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;DMA_InitStructure.DMA_MemoryBaseAddr = 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_Cmd(DMA1_Channel1, DISABLE);
}void MyDMA_Transfer(void)
{DMA_Cmd(DMA1_Channel1, DISABLE);DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);DMA_Cmd(DMA1_Channel1, ENABLE);while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);DMA_ClearFlag(DMA1_FLAG_TC1);
}

main.c 

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};
uint8_t DataB[] = {0, 0, 0, 0};int main(void)
{OLED_Init();MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);OLED_ShowString(1, 1, "DataA");OLED_ShowString(3, 1, "DataB");OLED_ShowHexNum(1, 8, (uint32_t)DataA, 8);OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);while (1){DataA[0] ++;DataA[1] ++;DataA[2] ++;DataA[3] ++;OLED_ShowHexNum(2, 1, DataA[0], 2);OLED_ShowHexNum(2, 4, DataA[1], 2);OLED_ShowHexNum(2, 7, DataA[2], 2);OLED_ShowHexNum(2, 10, DataA[3], 2);OLED_ShowHexNum(4, 1, DataB[0], 2);OLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2);Delay_ms(1000);MyDMA_Transfer();OLED_ShowHexNum(2, 1, DataA[0], 2);OLED_ShowHexNum(2, 4, DataA[1], 2);OLED_ShowHexNum(2, 7, DataA[2], 2);OLED_ShowHexNum(2, 10, DataA[3], 2);OLED_ShowHexNum(4, 1, DataB[0], 2);OLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2);Delay_ms(1000);}
}

代码参考:

STM32入门教程-2023版 细致讲解 中文字幕_哔哩哔哩_bilibili

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

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

相关文章

【第二节】80x86汇编-寄存器和标志位

目录 前言 一、汇编相关概念 1.1 数据表示与类型 1.2 汇编语言的构成 1.3 存储器及指令、数据 1.4 存储单元 1.5 CPU对存储器的读写操作 1.6 CPU读写内存单元的过程 1.7 intel CPU发展 1.8 8086 内部结构 二、寄存器 2.1 寄存器概览 2.2 32位寄存器 2.3 16位寄存器…

三维建模软件:地理信息与遥感领域的智慧构建者

在地理信息与遥感技术的广阔舞台中,建模软件如同一位卓越的建筑师,以数据为砖瓦,智慧为水泥,构建出一个又一个又一个逼真、动态的虚拟世界。本文将深入探究其技术核心、应用实例、未来趋势,揭示建模软件如何在地理信息…

《爱情,到此为止》票房大卖 贾斯汀巴尔多尼与布莱克莱弗利的矛盾升级 是真的还是炒作

布蕾克莱弗利,贾斯汀巴尔多尼 布莱克莱弗利凭借电影《我们的末日》在周末取得了票房成功,首映票房收入达 5000 万美元。在电影院困难时期,这是一个了不起的成就,但没有人谈论这一胜利——粉丝们对她与导演兼联合主演贾斯汀巴尔多…

排序(基数,堆,归并)

基数排序 定义0-9十个桶,先排序个数,在排序十位,依次向下(桶就是二维数组) 按照个位先排一次 个位已经有序了,桶内遵循先进先出 没有十位放到0里 取出 百位 这样排序就完成了。放进取出几次,取…

Flink Checkpoint expired before completing解决方法

在Flink消费Kafka日志的时候出现了这样的一则报错, JobManager报错如下: 2024-03-07 15:21:12,500 [Checkpoint Timer] WARN org.apache.flink.runtime.checkpoint.CheckpointFailureManager [] - Failed to trigger or complete checkpoint 181 for …

Python酷库之旅-第三方库Pandas(082)

目录 一、用法精讲 341、pandas.Series.str.startswith方法 341-1、语法 341-2、参数 341-3、功能 341-4、返回值 341-5、说明 341-6、用法 341-6-1、数据准备 341-6-2、代码示例 341-6-3、结果输出 342、pandas.Series.str.strip方法 342-1、语法 342-2、参数 …

bug的常见排查和分析思路以及相关的原因分类

作为开发人员,经常会收到来自用户和QA,领导反馈的各种问题。 为了快速问题,我们有时需要站在更高的角度,更全面的看待问题。才能更快锁定问题。 具体的bug还需要结合企业实际业务情况,相关的框架,依赖库&…

PHP项目任务系统小程序源码

🚀解锁高效新境界!我的项目任务系统大揭秘🔍 🌟 段落一:引言 - 为什么需要项目任务系统? Hey小伙伴们!你是否曾为了杂乱的待办事项焦头烂额?🤯 或是项目截止日逼近&…

QT、C++简单界面设计

#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {---------------------窗口设置----------------------this->setWindowTitle("南城贤子摄影工作室");//设置窗口标题this->setWindowIcon(QIcon("d:\\Pictures\\C…

PUMA论文阅读

PUMA: Efficient Continual Graph Learning with Graph Condensation PUMA:通过图压缩进行高效的连续图学习 ABSTRACT 在处理流图时,现有的图表示学习模型会遇到灾难性的遗忘问题,当使用新传入的图进行学习时,先前学习的这些模…

c语言中比较特殊的输入格式

目录 一.%[ ] 格式说明符 1.基本用法 (1)读取字母字符: (2)读取数字字符: (3)读取所有字符直到遇到空格: (4)读取直到换行符: 2.使用范围和组合: 3.^ 取反操作 4.注意事项 (1). 字符范围的正确表示 (2). 避免字符集中的特殊字符冲突 (3).避免空字符集 (4). 输入长…

构建高效外贸电商系统的技术探索与源码开发

在当今全球化的经济浪潮中,外贸电商作为连接国内外市场的桥梁,其重要性日益凸显。一个高效、稳定、功能全面的外贸电商系统,不仅能够助力企业突破地域限制,拓宽销售渠道,还能提升客户体验,增强品牌竞争力。…

Wireshark过滤规则

一、按IP地址过滤 1、查看源IP为 xx 的包 ip.srcIP地址 例如:ip.src172.18.10.56 2、查看目标IP为 xx 的包 ip.dstIP地址 例如:ip.dst172.16.76.251 3、查看源或目标IP为 xx 的包 ip.addrIP地址 例如:ip.addr172.18.10.56 二、按MAC地…

数学建模--浅谈多波束测线问题

目录 1.问题说明 2.问题分析 3.代码分析 1.问题说明 这个是国赛的真题,我们这个里面只是浅谈,就是对于这个里面运用的过程仿真的思路进行说明,这个探测的波束问题实际上也是一个简单的过程仿真问题,也是需要去进行作图的&#…

【中等】 猿人学web第一届 第5题 js混淆-乱码增强

文章目录 请求流程请求参数cookie信息 加密参数定位Hook CookieAST 还原混淆代码解密函数还原字符串还原数组引用还原浏览器内置对象 / 变量值引用还原逗号表达式还原 unicode, 16进制数值字符串相加AST 解混淆完整代码 加密参数还原cookie m字段m字段坑点 cookie RM4hZBv0dDon…

什么是云原生?(二)

1. 云原生的定义 云原生指构建和运行应用以充分利用通过云技术交付模式交付的分布式计算。云原生应用旨在充分利用云技术平台特有的可扩展性、弹性和灵活性优势。 根据云原生计算基金会 (CNCF) 的定义,云原生技术可帮助企业在公有云、私有云和混合云环境中构建和…

Unity Render Streaming项目实践经验

UnityRenderStreaming项目 项目github地址见上,我使用项目的3.1.0-exp.7版本、Unity 2023.1.0版本、windows11运行。 1下载项目包 2在Unity Hub中打开RenderStreaming~文件夹 3在package manager中导入com.unity.renderstreaming package 因为已经下载过了就选择install pa…

Word中加载Mathtype后粘贴复制快捷键(Ctrl+C/V)不能使用

操作环境 windows 11操作系统 word版本2021 mathtype版本7.4 这个问题只出现在word中,在excel和ppt中都不存在这个问题,而且之前在另一台电脑中使用word2016版本并没有这种问题的,然后网上搜了一下有不少人有这种问题,word直接取…

Docker Containerd初体验

Docker Containerd概述 ​ Containerd是一个开源的容器运行时,它提供了一种标准化的方式来管理容器的生命周期。该项目最初是由Docker开发团队创建的,并在后来成为了一个独立的项目,被纳入了Cloud Native Computing Foundation(C…

Taos 常用命令工作笔记(二)

最近测试创建一个涛思的数据库和一堆表进行测试,通过json配置文件配置字段的类型、名称等,程序通过解析json文件的配置,动态创建数据库的表。 其中表字段为驼峰结构的规则命名,创建表也是成功的,插入的测试数据也是成功…