目录
6.2 DMA中断状态清除寄存器:DMA_LIFCR和DMA_HIFCR
6.6 DMA数据流x的存储器地址寄存器:DMA_SxMOAR和DMA_SxM1AR
1. 什么是DMA
DMA全称为:Direct Memory Access;即直接存储器访问。
DMA传输数据就是从地址A复制到地址B,地址A和地址B可以是外设和存储器之间,也可以是存储器和存储器之间。
单片机的核心是CPU,我们知道CPU无时无刻不在处理着大量的事务,其中包括转移数据、计算数据、控制程序转移等等。正如中断一样,CPU处理的事务也有轻重缓急之分,有些事情对于CPU来说并不是那么重要,比方说转移数据、存储数据、复制数据,如果通过其他功能去处理这些事,而让原本应该处理这些事件的CPU转而去处理更加重要、复杂的事务,是不是可以更好的利用CPU资源呢?
DMA就是基于这种设想而设计的,转移数据(特别是大量的数据费时费力)是可以不用CPU参与的,这部分CPU转而去处理更加复杂重要的事务,可以大大的增强CPU资源利用率。比方说我们希望资源 A 的数据复制到资源 B,是可以不用CPU参与的,只需要在资源 A 和资源 B 之间开辟一个DMA通道来复制数据即可。CPU可以转而去处理更加重要的事务。
DMA的作用就是为了解决大量数据转移过度消耗CPU资源的问题。有了DMA使得CPU更专注于更加重要实用的操作、计算、控制等。
DMA控制器基于复杂的总线矩阵架构,将功能强大的双 AHB 主总线架构与独立的 FIFO 结合在一起,优化了系统带宽。两个DMA控制器总共有16个数据流(每个控制器8个),每一个DMA控制器都用于管理一个或多个外设的存储器访问请求。每个数据流总共可以有多达8个通道(或称请求)。每个通道都有一个仲裁器,用于处理DMA请求间的优先级(类似于中断中的NVIC)。
2. DMA的主要特性
- 双 AHB 主总线架构,一个用于存储器访问,另一个用于外设访问
- 仅支持 32 位访问的 AHB 从编程接口
- 每个DMA控制器有8个数据流,每个数据流有多达8个通道(或称请求)
- 每个数据流有单独的四级 32 位先进先出存储器缓冲区 (FIFO),可用于 FIFO 模式或直接模式
- 通过硬件可以将每个数据流配置为:
- —支持外设到存储器、存储器到外设和存储器到存储器传输的常规通道
- —也支持在存储器方双缓冲的双缓冲区通道
- 8 个数据流中的每一个都连接到专用硬件 DMA 通道(请求)
- DMA 数据流请求之间的优先级可用软件编程(4 个级别:非常高、高、中、低),在软件优先级相同的情况下可以通过硬件决定优先级(例如,请求 0 的优先级高于请求 1)
- 每个数据流也支持通过软件触发存储器到存储器的传输
3. DMA功能
3.1 DMA功能框图
DMA控制器执行直接存储器传输:采用AHB主总线,它可以控制AHB总线矩阵来启动AHB事务;
直接存储器执行的事务包括:外设到存储器的传输、存储器到外设的传输、存储器到存储器的传输。
DMA控制器提供两个AHB主端口:AHB存储器端口(用于连接存储器)和 AHB外设端口(用于连接外设)。但是需要注意:当执行存储器与存储器之间的传输时,AHB外设端口必须也可以访问存储器。
3.2 DMA事务
DMA事务由给定数目的数据传输序列组成。要传输的数据项的数目及其宽度(8位、16位 或 32位)可由软件编程。
每个DMA传输包含三项操作:
- 通过 DMA_SxPAR 或 DMA_SxM0AR 寄存器寻址,从外设数据寄存器或存储器单元中加载数据。
- 通过 DMA_SxPAR 或 DMA_SxM0AR 寄存器寻址,将加载的数据存储到外设数据寄存器或存储器单元。
- DMA_SxNDTR 计数器在数据存储结束后递减,该计数器中包含仍需执行的事务数。
在产生事件后,外设会向DMA控制器发送请求信号。DMA控制器根据通道优先级处理该请求。只要DMA控制器访问外设,DMA控制器就会向外设发送确认信号。外设获得DMA控制器的确认信号后,便会立即释放其请求。一旦外设使请求变得失效,那么DMA控制器就会释放确认信号。如果有更多的请求,外设可以启动下一个事务。
3.3 通道选择
每一个数据流都与一个DMA请求相关联,此DMA请求可以从8个可能的通道请求中选出。此选择由DMA_SxCR寄存器中的CHSEL[2:0]位控制。
3.4 仲裁器
仲裁器为两个AHB主端口(存储器和外设端口)提供基于请求优先级的 8 个DMA数据流请求管理,并启动 外设/存储器 访问序列。
优先级管理分为两个阶段:
- 软件:每个数据流优先级都可以在DMA_SxCR寄存器中配置。分为:非常高优先级、高优先级、中优先级、低优先级。
- 硬件:如果两个请求具有相同的软件优先级,则编号低的数据流优先于编号高的数据流。例如:数据流 2 的优先级高于数据流 4 的优先级。
3.5 DMA数据流
8个DMA控制器数据流都能提供源和目标(传输的双方)之间的单向传输链路。
每个数据流配置后都可以执行:
- 常规类型事务:存储器到外设、外设到存储器或存储器到存储器的传输。
- 双缓冲区类型事务:使用存储器的两个存储器指针的双缓冲区传输。
3.6 源、目标和传输模式
源传输和目标传输在整个 4GB 区域(也就是地址在0x0000 0000和0xFFFF FFFF之间)都可以寻址外设和存储器。
传输方向使用的是DMA_SxCR寄存器中的DIR[1:0]位进行配置,有三种可能的传输方向:存储器到外设、外设到存储器或存储器到存储器。
3.6.1 外设到存储器模式
使能了这个模式(将DMA_SxCR寄存器中的位EN置1)时,每次产生外设请求,数据流都会启动数据源到FIFO的传输。
FIFO简介:
FIFO用于在源数据传输到目标之前临时存储这些数据。
每个数据流都有一个独立的4字FIFO,阈值级别可由软件配置为1/4、1/2、3/4或满。为了使能FIFO阈值级别,必须通过将DMA_SxFCR寄存器中的DMDIS位置1来禁止直接模式。FIFO的结构随源与目标数据宽度而不同。
当达到了FIFO(源数据传输到目标之前临时存储)的阈值级别时,FIFO的内容移出并存储到目标中。
如果 DMA_SxNDTR 寄存器达到零、外设请求传输终止(在使用外设流控制器的情况下)或 DMA_SxCR 寄存器中的 EN 位由软件清零,传输即会停止。
在直接模式下(当 DMA_SxFCR 寄存器中的 DMDIS 值为“0”时),不使用 FIFO 的阈值级别控制:每完成一次从外设到 FIFO 的数据传输后,相应的数据立即就会移出并存储到目标中。
3.6.2 存储器到外设模式
使能了这种模式(将DMA_SxCR寄存器中的EN位置1)时,数据流会立即启动传输,从源完全填充FIFO。
每次发生外设请求,FIFO的内容都会移出并存储到目标中。当FIFO的级别小于或等于预定义的阈值级别时,将使用存储器中的数据完全重载FIFO。
如果 DMA_SxNDTR 寄存器达到零、外设请求传输终止(在使用外设流控制器的情况下)或 DMA_SxCR 寄存器中的 EN 位由软件清零,传输即会停止。
3.6.3 存储器到存储器模式
DMA通道在没有外设请求触发的情况下同样可以工作。
通过将DMA_SxCR寄存器中的使能位(EN)置1来使能数据流时,数据流会立即开始填充FIFO,直至达到阈值级别。达到阈值级别后,FIFO的内容便会移出,并存储到目标中。
如果DMA_SxNDTR寄存器达到零或DMA_SxCR寄存器中的EN位由软件清零,传输即会停止。
注意:
- 使用存储器到存储器模式时,不允许循环模式和直接模式。
- 只有DMA2控制器能够执行存储器到存储器的传输。
3.7 指针递增
根据DMA_SxCR寄存器中的PINC和MINC位的状态,外设和存储器指针在每次传输后可以自动向后递增或保持常量。
通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用。
如果使能了递增模式,则根据在DMA_SxCR寄存器PSIZE和MSIZE位中编程的数据宽度,下一次传输的地址将是前一次传输的地址递增1(对于字节)、2(对于半字)或4(对于字)。
为了优化封装操作,可以不管AHB外设端口上传输的数据的大小,将外设地址的增量偏移大小固定下来。DMA_SxCR寄存器中的PINCOS位用于将增量偏移大小与外设AHB端口或32位地址上的数据大小对齐。PINCOS位仅对AHB外设端口有影响。
如果将PINCOS位置1,则不论PSIZE值是多少,下一次传输的地址总比前一次传输的地址递增4(自动与32位地址对齐)。但是,AHB存储器端口不受此操作影响。
如果AHB外设端口或AHB存储器端口分别请求突发事务,为了满足AMBA协议,则需要将 PINC 或 MINC 位置1。
3.8 DMA内存占用
在STM32控制器中,芯片采用Cortex-MX架构,总线结构有了很大的优化,DMA占用另外的地址总线,并不会与CPU的系统总线发生冲突。也就是说,DMA的使用不会影响CPU的运行速度。
3.9 存储器映像
我们知道计算机系统的5大组成部分是运算器、控制器、存储器、输入设备和输出设备。其中运算器和控制器通常合在一起,称为CPU。所以计算机的关键部分就是CPU和存储器。
4. DMA中断
对于每个DMA数据流,可在发生以下事件时产生中断:
- 达到半传输
- 传输完成
- 传输错误
- FIFO错误(上溢、下溢或FIFO级别错误)
- 直接模式错误
注意:在将使能控制位置 “1” 前,应将相应的事件标志清零,否则会立即产生中断。
总结:
数据的传输主要需要 1. 数据的源地址(数据从哪里发出的)2. 数据传输位置的目标地址(需要将数据传输到哪里)3. 传递数据多少的数据传输量 4. 进行多少次传输的传输模式
用户在使用DMA直接存储器传输时,去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及:源地址、目标地址、数据传输量这三个参数,需要用户设置好这三个参数,DMA控制器就会启动数据传输,当剩余传输数据量为0时,就会达到传输终点,结束DMA传输。也可以说只要剩余传输量不是0,并且DMA还处于使能状态,那么就会发生数据传输。
特殊的:DMA还存在循环传输模式,当到达传输终点时会重新启动DMA传输。
DMA的应用主要是单纯的DMA转运(不依靠CPU的情况)和ADC搭配DMA使用,其中ADC搭配DMA使用是非常普遍的。
首先理解为什么ADC需要搭配DMA使用? 单纯的ADC是可以独立进行工作的,并且通过中断服务函数获取每次采样的值,每次采样完毕后存储到数据寄存器中,但是ADC有一个致命的缺陷,也可以是不足,就是ADC每次转换得到的结果会覆盖上一次的值。而配合DMA进行传输,不仅仅可以利用DMA进行多路信号的采集,实现一次性处理,最重要的是不会出现转换结果覆盖的情况。
5. 初始化结构体
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; //外设地址
uint32_t DMA_MemoryBaseAddr; //存储器地址
uint32_t DMA_DIR; //传输方向
uint32_t DMA_BufferSize; //传输数目
uint32_t DMA_PeripheralInc; //外设地址增量模式
uint32_t DMA_MemoryInc; //存储器地址增量模式
uint32_t DMA_PeripheralDataSize; //外设数据宽度
uint32_t DMA_MemoryDataSize; //存储器数据宽度
uint32_t DMA_Mode; //模式选择
uint32_t DMA_Priority; //通道优先级
uint32_t DMA_M2M; //存储器到存储器模式
}DMA_InitTypeDef;
1. 数据从哪里来,要到哪里去?
uint32_t DMA_PeripheralBaseAddr; //外设地址
uint32_t DMA_MemoryBaseAddr; //存储器地址
前两个定义数据从哪里来:M->P P->M M->M
uint32_t DMA_DIR; //传输方向
传输方向定义要到哪里去。
外设地址:DMA_CPAR 存储器地址:DMA_CMAR 传输方向:DMA_CCR:DIR
2. 数据要传多少,传的单位是什么?
uint32_t DMA_BufferSize; //传输数目
uint32_t DMA_PeripheralInc; //外设地址增量模式
uint32_t DMA_MemoryInc; //存储器地址增量模式
uint32_t DMA_PeripheralDataSize; //外设数据宽度
uint32_t DMA_MemoryDataSize; //存储器数据宽度
3. 什么时候传输结束
uint32_t DMA_Mode; //模式选择
模式选择:DMA_CCRx:CIRC
传输过半、传输完成、传输出错,DMA_ISR;
6. DMA相关寄存器
6.1 中断状态寄存器:DMA_LISR和DMA_HISR
中断状态寄存器:DMA_LISR和DMA_HISR,每个寄存器管理四个数据流(总共8个),DMA_LISR寄存器用于管理数据流0~3,而DMA_HISR用于管理数据流4~7。这两个寄存器各位的描述一模一样,只是管理的数据流不同。
6.1.1 DMA低中断状态寄存器:DMA_LISR
DMA低中断状态寄存器:DMA_LISR(DMA low interrupt status register)
位27、21、11、5 TCIFx:数据流x传输完成中断标志
0:数据流x上无传输完成事件
1:数据流x上发生传输完成事件
位26、20、10、4 HTIFx:数据流x半传输中断标志
0:数据流x上无半传输事件
1:数据流x上发生半传输事件
位25、19、9、3 TEIFx:数据流x传输错误中断标志
0:数据流x上无传输错误
1:数据流x上发生传输错误
位24、18、8、2 DMEIFx:数据流x直接模式错误中断标志
0:数据流x上无直接模式错误
1:数据流x上发生直接模式错误
位22、16、6、0 FEIFx:数据流x FIFO错误中断标志
0:数据流x上无FIFO错误事件
1:数据流x上发生FIFO错误事件
6.1.2 DMA高中断状态寄存器:DMA_HISR
DMA高中断状态寄存器:DMA_HISR(DMA high interrupt status register)
位27、21、11、5 TCIFx:数据流x传输完成中断标志
0:数据流x上无传输完成事件
1:数据流x上发生传输完成事件
位26、20、10、4 HTIFx:数据流x半传输中断标志
0:数据流x上无半传输事件
1:数据流x上发生半传输事件
位25、19、9、3 TEIFx:数据流x传输错误中断标志
0:数据流x上无传输错误
1:数据流x上发生传输错误
位24、18、8、2 DMEIFx:数据流x直接模式错误中断标志
0:数据流x上无直接模式错误
1:数据流x上发生直接模式错误
位22、16、6、0 FEIFx:数据流x FIFO错误中断标志
0:数据流x上无FIFO错误事件
1:数据流x上发生FIFO错误事件
总结:如果开启了中断状态寄存器中对应的中断,则在达成条件后就会跳到中断服务函数里面去,即使没有开启,我们也可以通过查询这些位来获得当前DMA传输的状态。我们常用的是该寄存器的TCIFx位,也就是数据流x的DMA传输完成与否标志。
需要注意的是:此寄存器为只读寄存器,所以在这些位被置位以后,只能通过其他的操作来清除。
6.2 DMA中断状态清除寄存器:DMA_LIFCR和DMA_HIFCR
DMA中断状态清除寄存器:DMA_LIFCR和DMA_HIFCR,每个寄存器控制4个数据流,DMA_LIFCR寄存器管理数据流0~3,DMA_HIFCR管理数据流4~7。同样地,每个寄存器各位的描述完全相同,只是管理的数据流不同
DMA中断状态清除寄存器:DMA_LIFCR
6.3 DMA数据流x配置寄存器:DMA_SxCR
DMA数据流x配置寄存器:DMA_SxCR(DMA stream x configuration register)
注:该寄存器是DMA传输的核心控制寄存器。该寄存器控制着DMA的很多信息,包括数据宽度、外设及存储器的宽度、优先级、增量模式、传输方向、中断允许、使能都是通过该寄存器来设置的。该寄存器具体每一位的功能可自行查看中文参考手册。
6.4 DMA数据流x数据项数寄存器:DMA_SxNDTR
DMA数据流x数据项数寄存器:DMA_SxNDTR(DMA stream x number of data register)
该寄存器控制着DMA数据流x的每次传输所要传输的数据量。其设置范围是0~65535。并且该寄存器的值会随着传输的进行而减少,当该寄存器的值为0的时候就代表此次数据传输已经全部发送完成了。所以可以通过这个寄存器的值来知道当前DMA传输的进度。
6.5 DMA数据流x的外设地址寄存器:DMA_SxPAR
DMA数据流x的外设地址寄存器:DMA_SxPAR(DMA stream x peripheral address register)
该寄存器用来存储STM32F4外设的地址,比如使用的是串口1,那么对应的该寄存器必须写入(&USART1_DR)0x4001 1004。如果使用其他外设,就修改成相应外设的地址就行。
6.6 DMA数据流x的存储器地址寄存器:DMA_SxMOAR和DMA_SxM1AR
其中DMA_SxM1AR仅在双缓冲模式下,才有效。DMA_SxM0AR,该寄存器和DMA_CPARx差不多,但是是用来放存储器的地址的。比如我们使用SendBuff[8200]数组来做存储器,那么我们在DMA_SxM0AR中写入&SendBuff就可以了。
7. 库函数配置DMA
本次实验用到串口1的发送,属于DMA2的数据流7,通道4。
1. 使能DMA2时钟,等待数据流可配置
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE); //DMA2时钟使能
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){} //等待DMA可配置
对配置寄存器DMA_SxCR进行设置,等待其最低位为0,也就是DMA禁止传输了,才对DMA进行配置,while函数中获取寄存器状态不等于DISABLE
一旦离开while循环,那么就意味着状态位等于DISABLE了,也就是DMA禁止传输了。
2. 初始化DMA2数据流7,包括配置通道,外设地址,存储器地址,传输数据量等
void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct); //初始化DMA
typedef struct { uint32_t DMA_Channel; //设置DMA数据流对应通道,可供选择的通道请求多达8个,取值为DMA_Channel_0~DMA_Channel_0; uint32_t DMA_PeripheralBaseAddr; //设置DMA传输的外设地址 uint32_t DMA_Memory0BaseAddr; //设置DMA内存地址,存放DMA传输数据的地址 uint32_t DMA_DIR; //设置数据传输方向 uint32_t DMA_BufferSize; //设置一次传输数据量的大小 uint32_t DMA_PeripheralInc; //设置传输数据的时候外设地址是不变还是递增 uint32_t DMA_MemoryInc; //设置传输数据的时候内存地址是否递增 uint32_t DMA_PeripheralDataSize; //设置外设的数据长度是字节传输,半字节传输还是字传输 uint32_t DMA_MemoryDataSize; //设置内存的数据长度 uint32_t DMA_Mode; //设置DMA模式是否循环采集 uint32_t DMA_Priority; //设置DMA通道优先级 低 中 高 超高 uint32_t DMA_FIFOMode; //设置是否开启FIFO模式 uint32_t DMA_FIFOThreshold; //选择FIFO阈值 uint32_t DMA_MemoryBurst; //配置存储器突发传输配置 uint32_t DMA_PeripheralBurst; //配置外设突发传输设置 }DMA_InitTypeDef; DMA_InitStructure.DMA_Channel = chx; //通道选择 DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外设地址 DMA_InitStructure.DMA_Memory0BaseAddr = mar;//DMA 存储器0地址 DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//存储器到外设模式 DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据长度:8位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//单次传输 DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输 DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化DMA Stream
3. 使能串口1的DMA发送
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口1的DMA发送
4. 使能DMA2数据流7,启动传输
DMA_Cmd (DMA2_Stream7,ENABLE); //使能DMA数据流
5. 查询DMA传输状态
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG) //查询DMA传输状态
ag. DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7); //查询DMA数据流7传输状态
uint16_t DMA_GetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx); //获取当前剩余数据量大小
ag. DMA_GetCurrDataCounter(DMA1_Channel4); //获取DMA数据流7还有多少个数据没有传输
void DMA_SetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx, uint16_t Counter); //设置DMA数据流传输的数据流大小
8. 实验程序
DMA通常情况下是配合ADC一起进行使用的,可以很直接的解决ADC的短板。
在初始化的ADC代码的基础之上添加DMA初始化,修改器结构体变量即可。
本次实验实现串口1的DMA发送,即USART1_TX,就必须选择DMA2的数据流7,通道4来进行DMA传输。
因为:
8.1 回车换行符
if(t>=j)//加入换行符
{
if(mask)
{
SendBuff[i]=0x0a;
t=0;
}
else
{
SendBuff[i]=0x0d;
mask++;
}
}
//回车换行符一般用于windows的TXT文件,包含两个字符“\r\n”,即先回车,光标回到首行,然后换行,光标另起一行
0x0a和0x0d都是回车键的ASCII码值,是window系统中定义的控制字符。
当我们按键盘上的回车键时,系统接收到其实是0x0d 0x0a两个控制字符,所以显示出来具有换行功能。
8.2 代码
8.2.1 main.c
#include "stm32f4xx.h"
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "lcd.h"
#include "usmart.h"
#include "KEY.h"
#include "DMA.h"
//LCD状态设置函数
void led_set(u8 sta)//只要工程目录下有usmart调试函数,主函数就必须调用这两个函数
{
LED1=sta;
}
//函数参数调用测试函数
void test_fun(void(*ledset)(u8),u8 sta)
{
led_set(sta);
}
//发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍
#define SEND_BUF_SIZE 8200
u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区
const u8 TEXT_TO_SEND[]={"ALIENTEK Explorer STM32F4 DMA 串口实验"}; //const修饰的地址存在了Flash中
int main(void)
{
u16 i;
u8 t=0,j,mask=0;
float pro=0; //进度
delay_init(168);
uart_init(115200);
LED_Init();
LCD_Init();
Key_Init();
//DMA2 数据流7,CH4 外设串口1 存储器为SendBuff,长度为:SEND_BUF_SIZE
MyDMA_Config(DMA2_Stream7,DMA_Channel_4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
LCD_ShowString(30,70,200,16,16,"DMA Test");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2023/20/23");
LCD_ShowString(30,130,200,16,16,"KEY0:START");
POINT_COLOR=BLUE;
//显示提示信息
j=sizeof(TEXT_TO_SEND);
for(i=0;i<SEND_BUF_SIZE;i++) //填充ASCII字符集数据
{
if(t>=j)//加入换行符
{
if(mask)
{
SendBuff[i]=0x0a;
t=0;
}
else
{
SendBuff[i]=0x0d;
mask++;
}
}
else //复制TEXT_TO_SEND语句
{
mask=0;
SendBuff[i]=TEXT_TO_SEND[t];
t++;
}
}
POINT_COLOR=BLUE;
i=0;
while(1)
{
t=KEY_Scan(0);
if(t==1)
{
printf("\r\nDMA DATA:\r\n");
LCD_ShowString(30,150,200,16,16,"Start Transimit……");
LCD_ShowString(30,170,200,16,16," %");//显示百分号
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口1DMA发送
MyDMA_Eable(DMA2_Stream7,SEND_BUF_SIZE); //开始一次DMA传输
while(1)
{
if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET) //等待DMA2_Stream7传输完成
{
DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7); //清除传输完成标志
break;
}
pro=DMA_GetCurrDataCounter(DMA2_Stream7); //得到当前剩余数据数
pro=1-pro/SEND_BUF_SIZE; //得到百分比
pro=pro*100;
LCD_ShowNum(30,170,pro,3,16);
}
LCD_ShowNum(30,170,100,3,16); //显示100%
LCD_ShowString(30,150,200,16,16,"Transimit Finished");
}
i++;
delay_ms(10);
if(i==20)
{
LED0=!LED0;
i=0;
}
}
}
8.2.2 DMA.c
#include "stm32f4xx.h"
#include "DMA.h"
//DMA各通道配置
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_Streamx:DMA数据流 DMA1_Stream0~7/DMA2_Stream0~7
//chx:DMA通道选择 DMA_Channel_0~DMA_Channel_7
//par:外设地址
//mar:寄存器地址
//ndtr:数据传输量
void MyDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr)
{
if((u32)DMA_Streamx>(u32)DMA2) // 得到当前Stream是属于DMA1还是DMA2
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能
else
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能
DMA_DeInit(DMA_Streamx);
while(DMA_GetCmdStatus(DMA_Streamx)!=DISABLE); //等待DMA可配置,当离开while循环后,DMA就已经使能了
//配置DMA结构体
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_BufferSize=ndtr; //数据传输量
DMA_InitStructure.DMA_Channel=chx; //DMA通道选择
DMA_InitStructure.DMA_DIR=DMA_DIR_MemoryToPeripheral; //存储器到外设模式
DMA_InitStructure.DMA_FIFOMode=DMA_FIFOMode_Disable; //FIFO模式禁止
DMA_InitStructure.DMA_FIFOThreshold=DMA_FIFOThreshold_Full; //FIFO阈值
DMA_InitStructure.DMA_Memory0BaseAddr=mar;
DMA_InitStructure.DMA_MemoryBurst=DMA_MemoryBurst_Single; //存储器突发单次传输
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//存储器数据长度,8位
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//存储器增量模式,也就是存储器依次向后递增
DMA_InitStructure.DMA_Mode=DMA_Mode_Normal; //使用普通模式
DMA_InitStructure.DMA_PeripheralBaseAddr=par;
DMA_InitStructure.DMA_PeripheralBurst=DMA_PeripheralBurst_Single; //外设突发单次传输
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;//外设数据长度,8个字节
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable; //外设非增量模式,也就是外设的指针不是依次向后递增的
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium; //中等优先级
DMA_Init(DMA_Streamx,&DMA_InitStructure);
}
//开启一次DMA传输
//DMA_Streamx:DMA数据流 DMA1_Stream0~7/DMA2_Stream0~7
//ndtr:数据传输量
void MyDMA_Eable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)
{
DMA_Cmd(DMA_Streamx,DISABLE); //关闭DMA传输,只有DMA传输被关闭,才能获取DMA传输状态位
while(DMA_GetCmdStatus(DMA_Streamx)!=DISABLE); //确保DMA可以被设置
DMA_SetCurrDataCounter(DMA_Streamx,ndtr); //数据传输量
DMA_Cmd(DMA_Streamx,ENABLE); //开启DMA传输
}
8.2.3 DMA.h
#ifndef _DMA__H_
#define _DMA__H_
void MyDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr);
void MyDMA_Eable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr);
#endif