什么是DMA

DMA,即Direct Memory Access,是一种在无需CPU参与的情况下,将数据块在内存(SRAM)和外设(一般是I/O设备)之间高效传输的硬件机制。实现这种功能的集成电路单元叫做DMA Controller,即DMA 控制器。

使用DMA的好处

一般情况下,为了实现内存(SRAM)和外设(一般是I/O设备)之间的数据传输,有三种常用的方法:轮循法(polling),中断法(interrupt)以及本文要介绍的DMA方法。

轮循法(polling):在主循环中,CPU不断检查外设的相关标志位,来判断其是否需要进行数据的传输,如果有,则CPU将数据在外设和内存之间搬运,实现数据传输。当数据传输服务请求频繁或者的数据量很大时,会影响其他任务的实时性。由于其他任务的存在,也会影响数据传输的实时性。

中断法(interrupt):当外设需要传输数据时,会触发中断,CPU会暂停正在处理的任务,转而去处理外设的数据传输任务。CPU无需反复检查外设的标志位,中断机制会指示CPU何时去处理外设数据,但是依然需要CPU去完成数据搬运和传输过程。当外设数据传输服务发生不频繁,且数据量不大时,中断法也是不错的选择。当中断连续不断且频繁发生时,中断法变得不再高效,因为在恢复主流程的执行和中断响应的上下文切换上会占用大量CPU时间。

DMA传输法:DMA控制器是一种单片机中的硬件单元,他的功能就是允许I/O外设和存储器之间高效传输数据,且传输过程中无需CPU参与。其工作原理和流程如下:

1、当一个IO设备需要发送数据给存储器或者从存储器读取数据时,它会给DMA 控制器发送一个DRQ请求。DMA控制器收到请求后,向CPU发送HLD请求,要求CPU放弃对总线的使用,因为DMA控制器传输数据时,要使用系统总线。

DMA控制器和Cortex™-M3内核共享系统总线。当DMA和CPU访问同样的地址空间时,DMA访问可能会阻挡CPU访问系统总线几个总线周期。总线矩阵中实现了循环仲裁算法来分配DMA与CPU的访问权,它可以确保CPU得到至少一半的系统总线带宽。——《GD32手册》

2、CPU收到DMA控制器的HLD请求后,让出总线使用权给DMA控制器,并向DMA控制器响应HLDA信号。

3、当DMA控制器收到CPU的HLDA信号后,DMA控制器会通知IO设备一个DACK信号,告知IO设备可以进行数据的传输。然后DMA控制器占用系统总线,完成IO设备和存储器之间的数据传输。

4、当数据传输完成后,DMA控制器向CPU申请中断,告知CPU数据传输完成。同时CPU恢复系统总线的使用权。

从上描述可以发现:①在数据传输过程中,CPU完全不需要参与,只有传输完成后才有必要参与进来,大大的节约了CPU时间。②DMA数据传输请求总是IO外设发起的。

GD32F130 DMA简介

GD32F130系列单片机有一个DMA控制器,它有7个DMA通道(CH0~CH6)。 每一个通道可以服务一个外设与存储器之间的DMA数据传输。通道之间相互独立。

下图是DMA通道外设请求表。表格中标记有颜色的请求通道是支持重映射的。

1. 当 SYSCFG_CFGR0 寄存器的相应重映射位被清零时,请求被映射到该通道;
2. 当 SYSCFG_CFGR0 寄存器的相应重映射位被置位时,请求被映射到该通道。

例如可以配置SYSCFG_CFGR0寄存器中的对应位清0,来将USART0_TX请求通道映射到通道1。也可以配置SYSCFG_CFGR0寄存器中的对应位置1,来将USART0_TX请求通道重新映射到通道3。通过使用库函数syscfg_dma_remap_enable()和syscfg_dma_remap_disable()来重映射外设DMA通道的配置。

注意,映射DMA通道前,要使用rcu_periph_clock_enable(RCU_CFGCMP)来打开系统配置时钟,因为SYSCFG_CFGR0寄存器属于系统配置部分的寄存器。

配置通道软件优先级

当DMA控制器在同一时间接收到多个外设请求时,仲裁器将根据外设请求的优先级来决定响
应哪一个外设请求。优先级包括软件优先级和硬件优先级,优先级规则如下:

  • 软件优先级:分为4级,低,中,高和极高。可以通过寄存器DMA_CHxCTL的PRIO位域来配置;
  • 硬件优先级:当通道具有相同的软件优先级时,编号低的通道优先级高。例:通道0和通道2配置为相同的软件优先级时,通道0的优先级高于通道2。

设置软件优先级

  • DMA_CHxCTL.PRIO[1:0] = 00:低
  • DMA_CHxCTL.PRIO[1:0] = 01:中
  • DMA_CHxCTL.PRIO[1:0] = 10:高
  • DMA_CHxCTL.PRIO[1:0] = 11:极高

配置数据传输方向

DMA主要用于外设和存储器之间的数据传输,所有数据传输方向主要有两种:

  • 存储器到外设:例如USART使用DMA方式发送数据,这个数据可能是一个字符串或者一个字节数组。当使用DMA发送时,这些数据从存储器传输到USART外设的TDATA寄存器
  • 外设到存储器:例如USART使用DMA方式接收数据,使用一个字节数组来缓存收到的数据。当使用DMA接收时,收到的数据从USART外设的RDATA寄存器传输到存储器

虽然DMA也支持存储器到存储器,但是几乎用不到。

禁用存储器到存储器模式

  • DMA_CHxCTL.M2M=0:禁用存储器到存储器模式
  • DMA_CHxCTL.M2M=1:启用存储器到存储器模式

配置传输方向

  • DMA_CHxCTL.DIR=0:外设到存储器
  • DMA_CHxCTL.DIR=1:存储器到外设

配置数据宽度

DMA传输数据的时候,只要传输没结束,每次外设发起传输请求时,需要从外设读数据,或者写一个数据到外设,那么每次读/写的单个数据多大呢?这就是通过设置外设数据宽度来指定的。同理也要设置存储器的数据宽度。总之这里的数据宽度指的是单次传输的数据大小,也就是占用的字节数。

如果设置的存储器和外设的数据宽度不一样,则在数据存储到目的地时会发生截断或者高位补0 的情况。

  • 截断的情况:例如数据源的数据宽度是32bit,而目标数据宽度是16bit,从数据源传输一个0xB3B2B1B0到目标,其结果是目标得到的数据是0xB1B0,即丢掉了高位部分。
  • 补零的情况:例如数据源的数据宽度是16bit,而目标数据宽度是32bit,从数据源传输一个0xB1B0到目标,其结果是目标得到的数据是0x0000B1B0,即高位部分用0填充。

设置存储器数据宽度

  • DMA_CHxCTL.MWIDTH[1:0] = 00 :8bit
  • DMA_CHxCTL.MWIDTH[1:0] = 01 :16bit
  • DMA_CHxCTL.MWIDTH[1:0] = 10 :32bit
  • DMA_CHxCTL.MWIDTH[1:0] = 11 :保留

设置外设数据宽度

  • DMA_CHxCTL.PWIDTH[1:0] = 00 :8bit
  • DMA_CHxCTL.PWIDTH[1:0] = 01 :16bit
  • DMA_CHxCTL.PWIDTH[1:0] = 10 :32bit
  • DMA_CHxCTL.PWIDTH[1:0] = 11 :保留

配置外设地址和存储器地址以及地址生成算法

外设地址就是外设寄存器映射的地址,而存储器地址就是内存缓冲数组的起始地址。由于外设寄存器地址是固定的,所以外设地址生成算法应该选择固定地址模式。而存储器对应的缓冲数组是包含多个数据元素的矢量,所以存储器的地址生成算法应该选择增量地址模式。

当使用固定地址模式时,每次存取数据的地址都不变化。当使用增量地址模式时,每次传输一次数据后,地址就会自动增加一个数据宽度,假设数据宽度是8bit,则地址值会增1;如果数据宽度是2,则地址值增2,;如果数据宽度是32bit,则地址值增4。和C语言中指针的表现一致。

设置存储器基地址

使用DMA_CHxMADDR寄存器来设置存储器基地址。

设置外设基地址

使用DMA_CHxPADDR寄存器来设置存储器基地址。

设置存储器地址生成算法

  • DMA_CHxCTL.MNAGA=0:固定地址模式
  • DMA_CHxCTL.MNAGA=1:增量地址模式

设置外设地址生成算法

  • DMA_CHxCTL.PNAGA=0:固定地址模式
  • DMA_CHxCTL.PNAGA=1:增量地址模式

配置数据传输长度

外设每请求一次DMA传输,只会传输一次数据。通过设置DMA_CHxCNT来指定一共需要DMA传输多少次数据。

一旦通道使能,DMA_CHxCNT寄存器为只读的,并在每个 DMA 传输之后值减 1。如果该寄存器的值为 0,无论通道开启与否,都不会有数据传输。如果该通道工作在循环模式下,一旦通道的传输任务完成,该寄存器会被自动重装载为初始设置值。

在传输过程中,DMA_CHxCNT用于指示本次传输周期,还有多少数据没有传输。

配置DMA传输模式

有两种传输模式。

  • 单次模式:当DMA_CHxCNT寄存器指定的传输数据长度传输完成后,DMA_CHxCNT寄存器为0,使得DMA停止传输。
  • 循环模式:当DMA_CHxCNT寄存器指定的传输数据长度传输完成后,DMA_CHxCNT寄存器会重载恢复为初始值,且DMA通道会再次从存储器的基地址处继续进行后续的数据传输。这样可以一直响应外设的传输请求,直到DMA通道被禁止。

通过DMA_CHxCTL寄存器的CMEN 位来选择是否开启循环模式。

  • DMA_CHxCTL.CMEN =0:禁止循环模式
  • DMA_CHxCTL.CMEN =1:使能循环模式

DMA中断

DMA传输过程中会触发三种中断:

  • 传输完成中断:随着传输进行,当DMA_CHxCNT递减为0时,触发传输完成中断。
  • 传输半完成中断:假设本次传输周期,设置的传输长度DMA_CHxCNT的值为N,则当传输完成一半(整数除法,N/2,例如7/2=3)后,触发传输半完成中断。
  • 传输错误中断:传输过程中发生错误时触发此中断。

DMA配置代码模板

这里给出USART0使用DMA接收和发送的DMA部分配置代码

//=====================[USART0_TX = DMA_CH1]=====================
dma_deinit(USART0_TX_DMA_CH);  //复位DMA通道的相关寄存器
dma_memory_to_memory_disable(USART0_TX_DMA_CH);   //CTL.M2M=0,禁用存储器到存储器模式
dma_transfer_direction_config(USART0_TX_DMA_CH,DMA_MEMORY_TO_PERIPHERAL);  //CTL.DIR,传输方向为存储器到外设
dma_circulation_disable(USART0_TX_DMA_CH);     //CTL.CMEN=0禁用循环模式
dma_priority_config(USART0_TX_DMA_CH,DMA_PRIORITY_HIGH); //CTL.PRIO[1:0]通道的软件优先级
dma_memory_width_config(USART0_TX_DMA_CH,DMA_MEMORY_WIDTH_8BIT);      //CTL.MWIDTH[1:0]配置存储器单个数据宽度
dma_periph_width_config(USART0_TX_DMA_CH,DMA_PERIPHERAL_WIDTH_16BIT); //CTL.PWIDTH[1:0]配置外设单个数据宽度
dma_memory_increase_enable(USART0_TX_DMA_CH);  //CTL.MNAGA存储器地址生成算法为递增
dma_periph_increase_disable(USART0_TX_DMA_CH); //CTL.PNAGA外设地址生成算法为固定不变
//dma_interrupt_enable(USART0_TX_DMA_CH,DMA_INT_FTF);  //CTL.FTFIE 使能传输完成中断
//dma_interrupt_enable(USART0_TX_DMA_CH,DMA_INT_HTF);  //CTL.HTFIE 使能半传输完成中断
//dma_interrupt_enable(USART0_TX_DMA_CH,DMA_INT_ERR);  //CTL.ERRIE 使能传输错误中断
dma_memory_address_config(USART0_TX_DMA_CH, (uint32_t)USART0_tx_dma_buf);   //配置存储器基地址
dma_periph_address_config(USART0_TX_DMA_CH, (uint32_t)&USART_TDATA(USART0));  //配置外设基地址为USART0的TDATA寄存器地址
dma_transfer_number_config(USART0_TX_DMA_CH,0);  //CNT寄存器,设置传输长度
//dma_channel_enable(USART0_TX_DMA_CH);//使能通道//=====================[USART0_RX = DMA_CH2]=====================
dma_deinit(USART0_RX_DMA_CH);  //复位DMA通道的相关寄存器
dma_memory_to_memory_disable(USART0_RX_DMA_CH);   //CTL.M2M=0,禁用存储器到存储器模式
dma_transfer_direction_config(USART0_RX_DMA_CH,DMA_PERIPHERAL_TO_MEMORY);  //CTL.DIR,传输方向为外设到存储器
dma_circulation_enable(USART0_RX_DMA_CH);     //CTL.CMEN=1  打开循环模式
dma_priority_config(USART0_RX_DMA_CH,DMA_PRIORITY_HIGH); //CTL.PRIO[1:0]通道的软件优先级
dma_memory_width_config(USART0_RX_DMA_CH,DMA_MEMORY_WIDTH_8BIT);      //CTL.MWIDTH[1:0]配置存储器单个数据宽度
dma_periph_width_config(USART0_RX_DMA_CH,DMA_PERIPHERAL_WIDTH_16BIT); //CTL.PWIDTH[1:0]配置外设单个数据宽度
dma_memory_increase_enable(USART0_RX_DMA_CH);  //CTL.MNAGA存储器地址生成算法为递增
dma_periph_increase_disable(USART0_RX_DMA_CH); //CTL.PNAGA外设地址生成算法为固定不变
//dma_interrupt_enable(USART0_RX_DMA_CH,DMA_INT_FTF);  //CTL.FTFIE 使能传输完成中断
//dma_interrupt_enable(USART0_RX_DMA_CH,DMA_INT_HTF);  //CTL.HTFIE 使能半传输完成中断
//dma_interrupt_enable(USART0_RX_DMA_CH,DMA_INT_ERR);  //CTL.ERRIE 使能传输错误中断
dma_memory_address_config(USART0_RX_DMA_CH, (uint32_t)USART0_rx_dma_buf);   //配置存储器基地址
dma_periph_address_config(USART0_RX_DMA_CH, (uint32_t)&USART_RDATA(USART0));  //配置外设基地址为USART0的RDATA寄存器地址
dma_transfer_number_config(USART0_RX_DMA_CH,USART0_RX_DMA_BUF_SIZE);  //CNT寄存器,设置传输长度
//dma_channel_enable(USART0_RX_DMA_CH);//使能通道

GD32F130之DMA相关推荐

  1. GD32F130之Timer13定时器

    简介 Timer13是一个L2级别定时器,它的功能简单,非常适合定时器入门学习.其特性如下: 通道数:1个,通道0 计数方向:向上计数 计数器宽度:16位 时钟源:只有一个来自RCU模块的内部时钟 D ...

  2. 基于uFUN开发板的心率计(一)DMA方式获取传感器数据

    前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...

  3. linux dma拷贝数据到用户态,图解:零拷贝Zero-Copy技术大揭秘

    1.前言 像大白这种调包侠,深知不懂底层技术点就如同空中楼阁,再这样下去面阿里p10是没希望了. 想到这里,我开始慌了,所以今天和大家一起学习个底层技术点-零拷贝Zero-Copy. Linux系统中 ...

  4. 单片机I/O控制方式(UART中断和DMA中断的区别)

    目录 1.程序循环检测方式 2.中断驱动方式 3.直接内存访问方式 单片机I/O设备的控制方式主要有三种:程序循环检测.中断驱动和直接内存访问. 1.程序循环检测方式 程序循环检测方式的基本思路是:在 ...

  5. stm32的rxne和idle中断_HAL库的STM32F767的DMA通过IDLE中断接收数据但不能访问

    仿真能看到接收缓冲区数组中的数据,但不能访问,如果不访问,再开启DMA接收能正常接收,并且在仿真状态下也能查看.只要访问一次这个数组,下一次开启DMA接收后就不能接收数据了. 原代码如下: if(rx ...

  6. dma工作时cpu工不工作_CPU如何工作?

    dma工作时cpu工不工作 CPU, also known as the microprocessor is the heart and/or brain of a computer. Lets De ...

  7. 百度成立小度蓝牙联盟,DMA+小度App打造蓝牙语音风口

    人机交互经历了三个阶段键鼠.触屏和语音交互.在国外,谷歌.亚马逊.苹果等巨头的竞争已经到达白热化状态:在国内,百度的DuerOS正是这方面的一位大玩家. 在技术发展的各个时代中,提前入局的厂商,必然能 ...

  8. linux zynq ps dma,Zynq PS侧DMA驱动

    Linux中,驱动必然会有驱动对应的设备类型.在linux4.4版本中,其设备是以设备树的形式展现的. PS端设备树的devicetree表示如下 324 dmac_s: dmac@f8003000 ...

  9. dma接收双缓存 stm32_容易被大多数人忽视的STM32串口DMA问题

    讨论三个问题: 1.什么叫串口DMA 请求: 2.串口简要复习: 3.串口DMA发送流程. 第一 什么叫串口DMA 请求(战舰STM32开发板) 说这个问题之前先简单回顾DMA的基本特性.先导出原子哥 ...

  10. linux音频驱动dma数据,Linux音频驱动简述

    3.2 mixer接口 int register_sound_mixer(structfile_operations *fops, int dev); 上述函数用于注册1个混音器,第1个参数fops即 ...

最新文章

  1. 实战:手把手教你用朴素贝叶斯对文档进行分类
  2. 判断 小程序_社区团购小程序商城系统,可以从哪些方面判断?
  3. java json格式字符串转为map_json格式的字符串序列化和反序列化的一些高级用法...
  4. c语言学生成绩删除功能,c语言学生成绩管理系统程序设计,有添加,查找,删除,输出,修改,排序等功能!!!...
  5. scrapy立面parse_立面设计模式–设计观点
  6. C++细节系列(零):零散记录
  7. linux引导过程和服务控制
  8. 编译SNMP使用的MIB文件步骤
  9. 【Python笔记】Scipy.stats.norm函数解析
  10. Selenium认识与实战(学习版)
  11. 医药集采对药企有什么影响?
  12. OSPF多区域中必须有area0。非area0区域要与area0相连才能实现传播域间路由信息
  13. weblogic增大线程数
  14. Linux内核4.1在file_operations的read_iter和write_iter
  15. (LeetCode)数数关系——Non-decreasing Array(非递减数组)
  16. 运放的原理、应用、参数和命名规则
  17. 猫狗叫声模拟器小程序源码
  18. 天干地支(python)
  19. Ufs测试硬盘的软件,Ufs格式的硬盘在WINDOWS下的读取程序
  20. wma转mp3怎么弄_如何使用音频格式转换器将wma音频转换为MP3格式

热门文章

  1. 网络云存储技术Windows server 2012 (项目十七 配置iSCSI传输的安全性)
  2. Win10电脑不能读取U盘怎么办?不识别U盘怎么解决?
  3. sqlserver执行生成rpt文件怎么回事_使用生成脚本功能和数据导出工具实现SQL Server2014数据降级2008R2版本...
  4. 自学c语言要下载什么软件下载,你学c语言用的什么app?
  5. Ubuntu 14.04 引导修复(Boot Repair)(双系统修复一)
  6. 2021京东618活动脚本App和电脑版最新版(建议使用app版)
  7. Axure8.0基础教程
  8. 重装系统原来这么简单,最详细的win7安装教程
  9. android手机内存单位 吉字节,Android的尺寸单位
  10. vue调用手机浏览器打开pdf_在微信中调用外部浏览器实现文件下载之解决