GD32F130之DMA
什么是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相关推荐
- GD32F130之Timer13定时器
简介 Timer13是一个L2级别定时器,它的功能简单,非常适合定时器入门学习.其特性如下: 通道数:1个,通道0 计数方向:向上计数 计数器宽度:16位 时钟源:只有一个来自RCU模块的内部时钟 D ...
- 基于uFUN开发板的心率计(一)DMA方式获取传感器数据
前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...
- linux dma拷贝数据到用户态,图解:零拷贝Zero-Copy技术大揭秘
1.前言 像大白这种调包侠,深知不懂底层技术点就如同空中楼阁,再这样下去面阿里p10是没希望了. 想到这里,我开始慌了,所以今天和大家一起学习个底层技术点-零拷贝Zero-Copy. Linux系统中 ...
- 单片机I/O控制方式(UART中断和DMA中断的区别)
目录 1.程序循环检测方式 2.中断驱动方式 3.直接内存访问方式 单片机I/O设备的控制方式主要有三种:程序循环检测.中断驱动和直接内存访问. 1.程序循环检测方式 程序循环检测方式的基本思路是:在 ...
- stm32的rxne和idle中断_HAL库的STM32F767的DMA通过IDLE中断接收数据但不能访问
仿真能看到接收缓冲区数组中的数据,但不能访问,如果不访问,再开启DMA接收能正常接收,并且在仿真状态下也能查看.只要访问一次这个数组,下一次开启DMA接收后就不能接收数据了. 原代码如下: if(rx ...
- dma工作时cpu工不工作_CPU如何工作?
dma工作时cpu工不工作 CPU, also known as the microprocessor is the heart and/or brain of a computer. Lets De ...
- 百度成立小度蓝牙联盟,DMA+小度App打造蓝牙语音风口
人机交互经历了三个阶段键鼠.触屏和语音交互.在国外,谷歌.亚马逊.苹果等巨头的竞争已经到达白热化状态:在国内,百度的DuerOS正是这方面的一位大玩家. 在技术发展的各个时代中,提前入局的厂商,必然能 ...
- linux zynq ps dma,Zynq PS侧DMA驱动
Linux中,驱动必然会有驱动对应的设备类型.在linux4.4版本中,其设备是以设备树的形式展现的. PS端设备树的devicetree表示如下 324 dmac_s: dmac@f8003000 ...
- dma接收双缓存 stm32_容易被大多数人忽视的STM32串口DMA问题
讨论三个问题: 1.什么叫串口DMA 请求: 2.串口简要复习: 3.串口DMA发送流程. 第一 什么叫串口DMA 请求(战舰STM32开发板) 说这个问题之前先简单回顾DMA的基本特性.先导出原子哥 ...
- linux音频驱动dma数据,Linux音频驱动简述
3.2 mixer接口 int register_sound_mixer(structfile_operations *fops, int dev); 上述函数用于注册1个混音器,第1个参数fops即 ...
最新文章
- 实战:手把手教你用朴素贝叶斯对文档进行分类
- 判断 小程序_社区团购小程序商城系统,可以从哪些方面判断?
- java json格式字符串转为map_json格式的字符串序列化和反序列化的一些高级用法...
- c语言学生成绩删除功能,c语言学生成绩管理系统程序设计,有添加,查找,删除,输出,修改,排序等功能!!!...
- scrapy立面parse_立面设计模式–设计观点
- C++细节系列(零):零散记录
- linux引导过程和服务控制
- 编译SNMP使用的MIB文件步骤
- 【Python笔记】Scipy.stats.norm函数解析
- Selenium认识与实战(学习版)
- 医药集采对药企有什么影响?
- OSPF多区域中必须有area0。非area0区域要与area0相连才能实现传播域间路由信息
- weblogic增大线程数
- Linux内核4.1在file_operations的read_iter和write_iter
- (LeetCode)数数关系——Non-decreasing Array(非递减数组)
- 运放的原理、应用、参数和命名规则
- 猫狗叫声模拟器小程序源码
- 天干地支(python)
- Ufs测试硬盘的软件,Ufs格式的硬盘在WINDOWS下的读取程序
- wma转mp3怎么弄_如何使用音频格式转换器将wma音频转换为MP3格式
热门文章
- 网络云存储技术Windows server 2012 (项目十七 配置iSCSI传输的安全性)
- Win10电脑不能读取U盘怎么办?不识别U盘怎么解决?
- sqlserver执行生成rpt文件怎么回事_使用生成脚本功能和数据导出工具实现SQL Server2014数据降级2008R2版本...
- 自学c语言要下载什么软件下载,你学c语言用的什么app?
- Ubuntu 14.04 引导修复(Boot Repair)(双系统修复一)
- 2021京东618活动脚本App和电脑版最新版(建议使用app版)
- Axure8.0基础教程
- 重装系统原来这么简单,最详细的win7安装教程
- android手机内存单位 吉字节,Android的尺寸单位
- vue调用手机浏览器打开pdf_在微信中调用外部浏览器实现文件下载之解决