DMA(Direct Memory Acess),中文译为直接存储器访问。主要用来在不占用CPU的情况下提供在存储器和存储器之间、外设和存储器之间或者外设和外设之间的高速数据传输,可以节约CPU的资源做其它操作。

WB32F10xxx 有两个完全相同的 DMA 控制器 DMAC1 和 DMAC2。每个 DMA 控制器有 3 个通道(一共有 6 个通道),每个通道可以单独配置,管理各种类型 DMA 传输。每个 DMAC 内还有一个仲裁器来协调各个 DMA 请求的优先权。

本节将通过固件库DMA例程中的DMAC_MemoryToMemory工程,来讲解如何配置DMA完成存储器到存储器之间的数据传输。

13.1 DMA存储器到存储器间数据传输配置

本节代码主要功能为,定义一个字符数组,将此数组中存放的字符复制到一个新的地址中。代码从上到下按照结构依次分析与注释,请大家认真学习。

13.1.1 预处理代码及宏定义代码分析

#include "wb32f10x.h"
#include <stdio.h>                                     //使用printf函数时必须包含此头文件。
#include <string.h>                                    //字符串处理函数声明所在头文件,若想输出字符串,必须包含此头文件。
#include "bsp_uart1.h"                                 //用户自定义串口输出函数声明所在头文件。DMAC_Channel_InitTypeDef DMAC_Channel_InitStruct;      //将DMAC_Channel_InitTypeDef 宏定义为 DMAC_Channel_InitStruct。
NVIC_InitTypeDef NVIC_InitStructure;                   //将NVIC_InitTypeDef 宏定义为 NVIC_InitStructure。
char memSrc[] = "DMAC Memory to Memory Example\r\n";   //申请一个字符数组,并写入"DMAC Memory to Memory Example"
char memDst[sizeof(memSrc)] = {0};                     //另外申请一个字符数组,申请的空间与memSrc[]相同。
uint32_t flag;                                         //申请一个32位无符号的变量flag。

注意:
1)以#include <string.h>这句代码为例再复习一遍:string.h 是个“头文件”,其中包含了字符串处理函数的声明。

C语言中的变量和函数都需要先声明(定义)再使用。我们在使用自己编写的函数或变量之前也要先定义它们,定义本身就是声明。而对于使用系统函数或库函数,也需要先把含有它们声明的文件“包含”进来。这些文件通常在系统的指定目录中,你的编译器(预处理器)会自动找到它们。

#include 是一个预处理指示符,C源码在被编译器编译前会先交由预处理器处理,预处理器就会把 #include <string.h> 替换成string.h文件中的内容,这样这些字符串处理函数的声明就含在源代码中了,编译器才能顺利编译。没有这些声明的话,编译时通常会报“找不到strcmp函数定义…”这样的错误。

2)宏定义又称为宏替换,是C语言提供的三种预处理功能的一种,通过宏定义,可以提高程序的通用性和易读性。

13.1.2 主函数部分代码分析

int main(void)
{/* 配置中断优先组为2 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                                             /* 初始化串口1,此处传入的72000000为WB32使用的主频,115200为波特率 */uart1_init(72000000, 115200);/* 打印"Enter any key to continue..." */printf("Enter any key to continue...\r\n");/* 接收串口调试软件发来的字符 */getchar();/* 若接收到字符,打印"GoGo!!!" */printf("GoGo!!!\r\n");/* 使能DMAC1时钟,同时还需使能AHB总线上的DMAC1Bridge的时钟 */RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMAC1Bridge, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BMX1 | RCC_APB1Periph_DMAC1, ENABLE);/* 重启DMAC1 模块 */DMAC_DeInit(DMAC1);/* 配置DMAC1 Channel 0 *//* 设置传输源地址为数组memSrc的首地址 */DMAC_Channel_InitStruct.DMAC_SourceBaseAddr = (uint32_t)memSrc;/* 设置传输目标地址为数组memDst的首地址 */DMAC_Channel_InitStruct.DMAC_DestinationBaseAddr = (uint32_t)memDst;/* 使能DMA传输全局中断 */DMAC_Channel_InitStruct.DMAC_Interrupt = DMAC_Interrupt_Enable;/* 指定传输字宽 */DMAC_Channel_InitStruct.DMAC_SourceTransferWidth = DMAC_SourceTransferWidth_8b;/* 指定接收字宽 */DMAC_Channel_InitStruct.DMAC_DestinationTransferWidth = DMAC_DestinationTransferWidth_8b;/* 指定源地址增量模式为Increment */DMAC_Channel_InitStruct.DMAC_SourceAddrInc = DMAC_SourceAddrInc_Increment;/* 指定目标地址增量模式为Increment */DMAC_Channel_InitStruct.DMAC_DestinationAddrInc = DMAC_DestinationAddrInc_Increment;/* 指定源快速传输字长,对于存储器操作无效 */DMAC_Channel_InitStruct.DMAC_SourceTransactionLength = DMAC_SourceTransactionLength_1;/* 指定目标快速传输字长,对于存储器操作无效 */DMAC_Channel_InitStruct.DMAC_DestinationTransactionLength = DMAC_DestinationTransactionLength_1;/* 指定传输类型为存储器到存储器 */DMAC_Channel_InitStruct.DMAC_TransferTypeAndFlowControl =DMAC_TransferTypeAndFlowControl_MemoryToMemory_DMAC;/* 指定源主接口为AHB */DMAC_Channel_InitStruct.DMAC_SourceMasterInterface = DMAC_SourceMasterInterface_AHB;/* 指定目标主接口为AHB */DMAC_Channel_InitStruct.DMAC_DestinationMasterInterface = DMAC_DestinationMasterInterface_AHB;/* 指定块传输长度为memSrc数组的长度 */DMAC_Channel_InitStruct.DMAC_BlockTransferSize = sizeof(memSrc);/* 源握手接口选择为硬件 */ DMAC_Channel_InitStruct.DMAC_SourceHandshakingInterfaceSelect =DMAC_SourceHandshakingInterfaceSelect_Hardware;/* 目标握手接口选择为硬件 */DMAC_Channel_InitStruct.DMAC_DestinationHandshakingInterfaceSelect = DMAC_DestinationHandshakingInterfaceSelect_Hardware;/* 指定源握手接口优先级 */DMAC_Channel_InitStruct.DMAC_SourceHandshakingInterfacePolarity = DMAC_SourceHandshakingInterfacePolarity_High;/* 指定目标握手接口优先级 */DMAC_Channel_InitStruct.DMAC_DestinationHandshakingInterfacePolarity = DMAC_DestinationHandshakingInterfacePolarity_High;/* 失能源自动重装载 */DMAC_Channel_InitStruct.DMAC_AutomaticSourceReload = DMAC_AutomaticSourceReload_Disable;/* 失能目标自动重装载 */DMAC_Channel_InitStruct.DMAC_AutomaticDestinationReload = DMAC_AutomaticDestinationReload_Disable;/* 指定流控制模式 */DMAC_Channel_InitStruct.DMAC_FlowControlMode = DMAC_FlowControlMode_0;/* 指定FIFO(先进先出)模式 */DMAC_Channel_InitStruct.DMAC_FIFOMode = DMAC_FIFOMode_0;/* 通道优先级选择,与中断相反,数字越大优先级越高 */DMAC_Channel_InitStruct.DMAC_ChannelPriority = 0;/* 指定保护控制 */DMAC_Channel_InitStruct.DMAC_ProtectionControl = 0x1;/* 源硬件握手接口分配 */DMAC_Channel_InitStruct.DMAC_SourceHardwareHandshakingInterfaceAssign = 0;/* 目标硬件握手接口分配 */DMAC_Channel_InitStruct.DMAC_DestinationHardwareHandshakingInterfaceAssign = 0;/* 指定最大的AMBA快速传输字长 */DMAC_Channel_InitStruct.DMAC_MaximumAMBABurstLength = 0;/* DMAC通道结构体配置初始化 */DMAC_Channel_Init(DMAC1, DMAC_Channel_0, &DMAC_Channel_InitStruct);/* DMAC通道中断配置 */DMAC_ITConfig(DMAC1, DMAC_Channel_0, DMAC_IT_BLOCK, ENABLE);DMAC_ITConfig(DMAC1, DMAC_Channel_0, DMAC_IT_TFR, ENABLE);DMAC_ITConfig(DMAC1, DMAC_Channel_0, DMAC_IT_ERR, ENABLE);/* 中断初始化结构体配置 *//* 中断通道选择为DMAC1 */NVIC_InitStructure.NVIC_IRQChannel = DMAC1_IRQn;/* 指定抢占优先级 */NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;/* 指定子优先级 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;/* 使能中断通道 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/* 初始化中断初始化结构体 */NVIC_Init(&NVIC_InitStructure);/* 当以上配置好后,打印"DMA transfer enable." */printf("DMA transfer enable.\r\n");/* 使能DMAC1 */DMAC_Cmd(DMAC1, ENABLE);/* 使能DMAC1通道0 */DMAC_ChannelCmd(DMAC1, DMAC_Channel_0, ENABLE);while (1){}
}

在这部分代码中,主要配置了DMAC_Channel_InitTypeDef结构体中的各个结构体成员。

注意:
1)传入变量的地址与传入数组的地址方式不同,若是传入变量,需在变量前加上“&”;传入数组变量时,直接传数组名即可。

2)可以发现在传源地址与目标地址时,数组名前有“(uint32_t)”,此意为强制类型转换。

3)DMAC_Channel_InitTypeDef结构体中的结构体成员很多,初学者可以结合注释和DMA的其他传输方式一起学习,多看多想。

4)uart1_init(72000000, 115200);此部分代码用来配置串口,我们在下面的内容讲解。

13.1.3 DMAC1中断服务函数代码分析

/* 配置DMAC1中断服务函数 */
void DMAC1_IRQHandler(void)
{if(DMAC_GetITStatus(DMAC1, DMAC_Channel_0, DMAC_IT_BLOCK) != RESET)    //判断DMAC_IT_BLOCK中断标志位{DMAC_ClearITPendingBit(DMAC1, DMAC_Channel_0, DMAC_IT_BLOCK);        //若产生DMAC_IT_BLOCK中断标志位,则清除DMAC_IT_BLOCK中断标志位printf("DMA block transfer complete.\r\n");                          //打印“DMA block transfer complete.”}if(DMAC_GetITStatus(DMAC1, DMAC_Channel_0, DMAC_IT_TFR) != RESET)      //判断DMAC_IT_TFR中断标志位{DMAC_ClearITPendingBit(DMAC1, DMAC_Channel_0, DMAC_IT_TFR);          //若产生DMAC_IT_TFR中断标志位,则清除DMAC_IT_TFR中断标志位printf("DMA transfer complete.\r\n");                                 //打印“DMA transfer complete.”if(memcmp(memDst, memSrc, sizeof(memSrc)) == 0) {                 //通过“memcmp”比较函数,判断memDst数组与memSrc数组是否相等printf("DMA transfer success!!!\r\n");                             //若相等,打印“DMA transfer success!!!”}else {printf("DMA transfer failed!!!\r\n");                              //若不相等,打印"DMA transfer failed!!!”}}if(DMAC_GetITStatus(DMAC1, DMAC_Channel_0, DMAC_IT_ERR) != RESET)       //判断DMAC_IT_ERR中断标志位{DMAC_ClearITPendingBit(DMAC1, DMAC_Channel_0, DMAC_IT_ERR);          //若产生DMAC_IT_ERR中断标志位,则清除DMAC_IT_ERR中断标志位printf("DMA transfer error!!!\r\n");                                //打印“DMA transfer error!!!”}}

此中断服务函数的主要功能为判断DMA是否完成数据传输,以及完成的情况。

注意:
1)请注意这部分代码中出现的中断标识符的名称,“DMAC_IT_BLOCK”,“DMAC_IT_TFR”,“DMAC_IT_ERR”。

我们要学会通过名称(或工程中相关名称的定义)来猜测该中断标识符的作用。

例:
“DMAC_IT_TFR”,当DMA传输(Transfer)完成后,该位由硬件自动置1。我们就可以通过“DMAC_GetITStatus”函数检测该中断标识符的值来判断DMA是否完成了传输。

“DMAC_IT_ERR”,当DMA传输出错后,该位由硬件自动置1。我们就可以通过“DMAC_GetITStatus”函数检测该中断标识符的值来判断DMA的传输是否出错。

2)中断标识位置1后,必须由用户自己清除,这一点在代码中有体现。

13.1.4 串口配置代码分析

在主函数中我们使用到了uart1_init(72000000, 115200);这个函数,鼠标右击进入定义,可以在该文件中看到函数原型:

此处与我们第八章中所讲串口配置有所不同,这里采用了寄存器操作方式来编写此部分代码,初学者可以忽略此部分,只需要知道void uart1_init(uint32_t apbclk, uint32_t baud)这个函数是用来初始化UART的时钟频率和波特率即可。

13.2 实验现象

将代码编译完成烧录到WB32中,使用串口将开发板与电脑连接好(PA9连串口的RXD、PA10连串口的TXD),打开串口调试软件,复位开发板并使用调试软件发送任意字符给开发板(此例中我发送“1”):

DMA存储器到存储器数据传输成功。

若将代码中DMAC_Channel_InitTypeDef结构体中的结构体成员DMAC_DestinationAddrInc设置为DMAC_DestinationAddrInc_NoChange,编译烧录后,复位开发板并使用调试软件发送任意字符给开发板(此例中我发送“1”),实验结果如下:


DMA存储器到存储器数据传输失败。

注意:
1)请结合数据在计算机中的存储方法,想一想为何将代码中DMAC_Channel_InitTypeDef结构体中的结构体成员DMAC_DestinationAddrInc设置为DMAC_DestinationAddrInc_NoChange后DMA数据传输失败?

【WB32库开发】第13章(上)DMA直接存储器访问——存储器到存储器相关推荐

  1. GD32F303固件库开发(13)----定时器TIM捕获PWM测量频率与占空比

    GD32F303固件库开发.13----定时器TIM捕获PWM测量频率与占空比 概述 视频教学 csdn课程 样品申请 生成例程 keil配置 使能串口 串口重定向 占空比与频率计算 GPIO初始化 ...

  2. 第13章 统一的数据访问异常层次体系

    第13章 统一的数据访问异常层次体系 本章内容 DAO模式的背景 梦想照进现实 发现问题,解决问题 不重新发明轮子 要了解Spring为什么要提供统一的数据访问异常层次体系,我们得先从DAO模式说起. ...

  3. Unix/Linux下的Curses库开发指南——第一章 Curses库开发简介

    1.1什么是curses curses实际上是一个函数开发包,专门用来进行UNIX下终端环境下的屏幕界面处理以及I/O处理.通过这些函数库,C和C++程序就可以控制终端的视频显示以及输入输出.使用cu ...

  4. 【WB32库开发】第12章(上)TIM1高级定时器——PWM输入捕获

    本章要学习的PWM输入捕获是定时器又一重要应用,使用PWM输入捕获可以测量输入PWM的频率和占空比. PWM输入只能使用定时器的两个通道:通道1和通道2,且一路PWM输入要占用两个捕获寄存器,一个用于 ...

  5. 【WB32库开发】第9章 TIM1高级定时器——输出多路PWM

    PWM(Pulse Width Modulation)意为脉冲宽度调制,简称脉宽调制 .在工控行业,PWM信号可以用来调节电机转速.调节变频器以及BLDC电机驱动等:在LED照明行业,可以通过PWM来 ...

  6. 【WB32库开发】第10章 TIM1高级定时器——PWM互补输出

    在上一章的输出多路PWM的基础上,本章主要讲述如何配置WB32上的高级定时器TIM1完成PWM互补输出. 对PWM互补输出的概括放到10.3节中,请大家先跟随10.1节了解PWM互补输出如何配置,再结 ...

  7. Unix/Linux下的Curses库开发指南——第二章 curses库I/O处理

    第2章 curses库I/O处理 2.1 curses库简介 curses库是curses开发包中最重要的一个库,其中提供了一些基本的屏幕操作函数,包括输入/输出,屏幕初始化,屏幕处理中断以及窗口的创 ...

  8. 外设驱动库开发笔记13:MLX90614红外温度传感器驱动

    红外温度传感器一般用于非接触式的温度检测.在我们的系统中经常会有这样的需求.所以我们将其设计为通用的驱动库以备复用.这一篇我们将讲述MLX90614红外温度传感器驱动的设计与实现. 1.功能概述 ML ...

  9. Visual C# 2008+SQL Server 2005 数据库与网络开发--第13章 使用菜单和对话框

    在Windows应用程序中,除了Windows窗体之外,还有两个重要的组成部分,即菜单和对话框.本章将通过几个示例来介绍如何在Windows程序中使用菜单和对话框. 转载于:https://www.c ...

  10. 第13章 使用ADO.NET访问数据库

    Program using System; using System.Collections.Generic; using System.Linq; using System.Text; using ...

最新文章

  1. 大家都收藏了的最新开源项目Top12!CV、NLP、机器学习一应俱全
  2. 面试官:给我一个避免消息重复消费的解决方案?
  3. 在WP7下自定义RelativeSource 的Binding
  4. leetcode344. 反转字符串 史上最简单力扣题
  5. Spring MVC 接收POST表单请求,获取参数总结
  6. 访问json接口浏览器提示下载文件
  7. 时间和天数相加并格式化
  8. 【2014年计划】IT之路
  9. 全文检索——Lucene
  10. (三)SGE 部署 SGE
  11. [转]UserData使用总结 - lanyu
  12. 用矿卡P106升级tensorflow深度学习服务器
  13. 20200903-03-Hadoop运行模式之本地运行模式伪分布式运行模式
  14. 紫荆花开之say love to the girl you love
  15. PHP算法-快速排序
  16. 让word从第二页开始标记页脚
  17. 【解析】心田上的百合花开——心田花开
  18. 盘古开源:加速建设算力网络,全面开启算力时代
  19. Python编程入门教程(以在线评测平台为载体)
  20. 【面试题4】谈谈以前端角度出发做好SEO需要考虑什么?

热门文章

  1. Android性能优化系列-监听View inflate周期并动态替换
  2. opencc对文档的繁体中文转换为简体
  3. 单片机复位电路的可靠性设计及精典实用复位电路
  4. 山城虽寒、但物联却热,探访中移物联小记一笔……
  5. 【ESP32之旅】ESP32C3 Arduino库使用方法
  6. 分布式时序数据库作为工业物联网数据后台的7大优势
  7. GPS的Heading, Course, and Crab Angle不同与区别
  8. 文通电脑版车牌识别软件,让违章驾车无处可躲
  9. RoadMap:面向自动驾驶视觉定位的轻量级语义地图(ICRA2021)
  10. 【记忆化搜索/数位DP】zznu2175(长度为n的含有ACM的字符串)