当前位置: 首页 > news >正文

STM32学习笔记(七) —— DMA传输(MTM)

DMA,全称是Direct Memory Access(直接内存访问)。可以在存储器和存储器之间或者外设和存储器之间传输数据,而不需要CPU的干预,这样可以节省CPU的资源,提高工作效率。

1.功能框图

在这里插入图片描述

STM32F103RCT6有两个DMA控制器,需要DMA传输的时候,外设会向DMA发送请求,DMA也会进行应答,随后会进行DMA传输。

2.DMA通道

在这里插入图片描述

在这里插入图片描述

两个DMA控制器一共有12个通道(DMA1有7个,DMA2有5个),上面图中列出了每个外设的请求与DMA通道的对应关系。当有多个DMA通道请求时,由于每次只能响应一个DMA通道请求,所以DMA仲裁器会根据软件优先级以及硬件优先级来确定先响应哪个通道。

3.优先级
1° 软件优先级可以在DMA_CCRx寄存器中设置,一共有最高优先级、高优先级、中等优先级、低优先级这4个等级。
2° 硬件优先级由通道号决定,通道号小的优先级高于通道号大的,比如通道2优先级高于通道4.
DMA仲裁器会优先对比软件优先级,软件优先级相同时再对比硬件优先级。

4.DMA传输模式
DMA传输的数据量可以通过DMA_CNDTRx寄存器最大编程为65535,每次传输后会递减,在传输了一半的数据和数据传输完成后都会有相应的标志位标识,并且可以使能相应的中断。
1° 当通道配置为非循环模式时,完成数据传输后将不再产生DMA操作,如果要开始新的DMA传输,需要在关闭DMA通道的情况下,重新配置要传输的数据个数(重新配置DMA_CNDTRx寄存器);
2° 在循环模式下,最后一次传输结束时,DMA_CNDTRx寄存器会被自动重载为初始数值。
3° 存储器到存储器模式。DMA的传输方向可以是存储器到外设、外设到存储器,也可以是存储器到存储器。这种情况下需要借用外设端口,当使用外设通道代表存储器时,通道可以随意选择,注意不能与循环模式同时使用。

5.软件编程
1° 在内存中开辟两个数组空间 mData_Buffer and pData_Buffer
2° 向 mData_Buffer 中存入数据
3° 使用 DMA 将 mData_Buffer 中数据移动到 pData_Buffer 中

/* 全局变量 */
char mData_Buffer[7] = "eckard";
char pData_Buffer[7] = "before";/* 在while循环之前添加 *///开启 DMA1 时钟, 使用外设之前一定要开启时钟RCC->AHBENR |= (uint16_t)0x01 << 0;//清除 CCR 寄存器DMA1_Channel1->CCR &= (uint16_t)0xffff8000;//数据从存储器读, 因为我们这里是要验证存储器到存储器模式DMA1_Channel1->CCR |= (uint16_t)0x01 << 4;//不执行循环操作, 注意存储器到存储器模式不能与循环模式同时使用DMA1_Channel1->CCR |= (uint16_t)0x00 << 5;//外设地址增量操作,地址递增的意思是传输完当前地址中的数据后,地址变为下一个要传输的数据的地址DMA1_Channel1->CCR |= (uint16_t)0x01 << 6;//存储器地址增量操作DMA1_Channel1->CCR |= (uint16_t)0x01 << 7;//外设数据宽度8位DMA1_Channel1->CCR |= (uint16_t)0x00 << 8;//存储器数据宽度8位DMA1_Channel1->CCR |= (uint16_t)0x00 << 10;//通道优先级低,这里的优先级是软件设置的优先级,硬件优先级由通道号决定的DMA1_Channel1->CCR |= (uint16_t)0x00 << 12;//启动存储器到存储器模式DMA1_Channel1->CCR |= (uint16_t)0x01 << 14;//数据传输数量,每次传输后会递减,如果在循环模式下,最后一次传输结束时,此寄存器会被自动重载为初始数值,比如7DMA1_Channel1->CNDTR = (uint16_t)7;//外设地址,我们上述设置的是从存储器读,所以数据传输的方向是mData_Buffer到pData_Buffer, 此地址为目标地址DMA1_Channel1->CPAR  = (uint32_t)pData_Buffer;//存储器地址, 此地址是源地址DMA1_Channel1->CMAR  = (uint32_t)mData_Buffer;printf("transfer start.\r\n");printf("mData_Buffer = %s\r\n", mData_Buffer);printf("pData_Buffer = %s\r\n", pData_Buffer);printf("\r\n");//开启通道DMA1_Channel1->CCR |= (uint16_t)0x01 << 0;//等待传输完成while(DMA1->ISR & ((uint32_t)0x01 << 1));/* 清除传输完成标志 */DMA1->IFCR |= (uint32_t)0x01 << 1;printf("transfer completed.\r\n");printf("mData_Buffer = %s\r\n", mData_Buffer);printf("pData_Buffer = %s\r\n", pData_Buffer);

将程序下载后,打开串口调试助手,可以查看打印的信息。

在这里插入图片描述

上述使用的是寄存器编程,下面使用STM32CubeMX来初始化DMA

在这里插入图片描述

在这里插入图片描述

生成代码后,发现新增了dma.c文件,已经完成了DMA的初始化

在这里插入图片描述

在main函数中也有调用

在这里插入图片描述

STM32CubeMX只能帮助我们初始化外设的配置,应用程序需要我们自己编写,我们这里需要调用HAL_DMA_Start函数来开启DMA传输

/* 全局变量 */
char mData_Buffer[7] = "eckard";
char pData_Buffer[7] = "before";/* 在while循环之前添加 */printf("transfer start.\r\n");printf("mData_Buffer = %s\r\n", mData_Buffer);printf("pData_Buffer = %s\r\n", pData_Buffer);printf("\r\n");/* 开启DMA传输 */HAL_DMA_Start (&hdma_memtomem_dma1_channel1, (uint32_t)mData_Buffer, (uint32_t)pData_Buffer, 7);/* 等待传输完成 */while(__HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_memtomem_dma1_channel1)) == RESET);/* 清除传输完成标志 */__HAL_DMA_CLEAR_FLAG(&hdma_memtomem_dma1_channel1, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_memtomem_dma1_channel1));printf("transfer completed.\r\n");printf("mData_Buffer = %s\r\n", mData_Buffer);printf("pData_Buffer = %s\r\n", pData_Buffer);

将程序下载后,打开串口调试助手,可以查看到打印的信息与上述采用寄存器编程时是一样的。

本例程代码可以在HAL库工程模板这一章节的最后,百度网盘链接分享处获取

以上是通过开发板进行实际验证的,下面使用软件仿真,

我们进入调试界面,打开串口窗口,然后点击运行( 前面章节有提到,所以本篇以及后续章节都不再重复提及 )

运行结果如下,与在开发板上验证的结果一致。

在这里插入图片描述

相关文章:

  • MySQL数据库⑪_C/C++连接MySQL_发送请求
  • 初识数据库:探索数据的世界
  • 统计平台项目总体架构设计方案
  • 51单片机 温度传感器得数据,传到上位机
  • [ai笔记9] openAI Sora技术文档引用文献汇总
  • Rust 学习笔记 - 流程控制 与 Range 类型
  • ZYNQ:PL-CAN总线功能应用
  • CSS之重绘与回流
  • StringJoiner
  • GEE:关于在GEE平台上进行回归计算的若干问题
  • 3秒实现无痛基于Stable Diffusion WebUI安装ComfyUI!无需重复安装环境!无需重复下载模型!安装教程
  • 通过 Prometheus 编写 TiDB 巡检脚本(脚本已开源,内附链接)
  • 【Unity】【VR开发】针对VR项目的优化版Unity Build Settings
  • 人工智能学习与实训笔记(四):神经网络之NLP基础—词向量
  • [力扣 Hot100]Day28 两数相加
  • 【知识碎片】第三方登录弹窗效果
  • 10个确保微服务与容器安全的最佳实践
  • Bytom交易说明(账户管理模式)
  • ES6语法详解(一)
  • jdbc就是这么简单
  • leetcode讲解--894. All Possible Full Binary Trees
  • maven工程打包jar以及java jar命令的classpath使用
  • MySQL QA
  • MySQL数据库运维之数据恢复
  • MySQL用户中的%到底包不包括localhost?
  • node入门
  • redis学习笔记(三):列表、集合、有序集合
  • Synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比...
  • 彻底搞懂浏览器Event-loop
  • 对象管理器(defineProperty)学习笔记
  • 记录一下第一次使用npm
  • 区块链将重新定义世界
  • 如何设计一个比特币钱包服务
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 文本多行溢出显示...之最后一行不到行尾的解决
  •  一套莫尔斯电报听写、翻译系统
  • elasticsearch-head插件安装
  • mysql面试题分组并合并列
  • 哈罗单车融资几十亿元,蚂蚁金服与春华资本加持 ...
  • ###C语言程序设计-----C语言学习(3)#
  • (免费领源码)python#django#mysql公交线路查询系统85021- 计算机毕业设计项目选题推荐
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (转)人的集合论——移山之道
  • .NET Core Web APi类库如何内嵌运行?
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .Net6 Api Swagger配置
  • .Net程序猿乐Android发展---(10)框架布局FrameLayout
  • .NET导入Excel数据
  • .net反混淆脱壳工具de4dot的使用
  • @TableId注解详细介绍 mybaits 实体类主键注解
  • [bzoj1038][ZJOI2008]瞭望塔
  • [DevEpxress]GridControl 显示Gif动画
  • [FxCop.设计规则]8. 也许参数类型应该是基类型