【正点原子探索者STM32F407开发板例程连载+教学】第28章 DMA实验

2019-07-20 15:02发布

第二十八章 DMA实验

       [mw_shl_code=c,true]1.硬件平台:正点原子探索者STM32F407开发板 2.软件平台:MDK5.1 3.固件库版本:V1.4.0 [/mw_shl_code]

本章我们将向大家介绍STM32F4DMA。在本章中,我们将利用STM32F4DMA来实现串口数据传送,并在TFTLCD模块上显示当前的传送进度。本章分为如下几个部分: 28.1 STM32F4 DMA简介 28.2 硬件设计 28.3 软件设计 28.4 下载验证  

28.1 STM32F4 DMA简介

DMA,全称为:Direct Memory Access,即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAMI/O设备开辟一条直接传送数据的通路,能使CPU的效率大为提高。 STM32F4最多有2DMA控制器(DMA1DMA2),共16个数据流(每个控制器8个),每一个DMA控制器都用于管理一个或多个外设的存储器访问请求。每个数据流总共可以有多达8个通道(或称请求)。每个数据流通道都有一个仲裁器,用于处理DMA请求间的优先级。 STM32F4的DMA有以下一些特性: ● 双AHB主总线架构,一个用于存储器访问,另一个用于外设访问 ● 仅支持32位访问的AHB从编程接口 ● 每个DMA控制器有8个数据流,每个数据流有多达8个通道(或称请求) ● 每个数据流有单独的四级32位先进先出存储器缓冲区(FIFO),可用于FIFO模式或直接模式。 ● 通过硬件可以将每个数据流配置为:  1,支持外设到存储器、存储器到外设和存储器到存储器传输的常规通道  2,支持在存储器方双缓冲的双缓冲区通道 ● 8个数据流中的每一个都连接到专用硬件DMA通道(请求) ● DMA 数据流请求之间的优先级可用软件编程(4个级别:非常高、高、中、低),在软件优先级相同的情况下可以通过硬件决定优先级(例如,请求0的优先级高于请求1) ● 每个数据流也支持通过软件触发存储器到存储器的传输(仅限DMA2控制器) ● 可供每个数据流选择的通道请求多达8个。此选择可由软件配置,允许几个外设启动DMA请求 ● 要传输的数据项的数目可以由DMA控制器或外设管理: 1,DMA 流控制器:要传输的数据项的数目是1到65535,可用软件编程 2,外设流控制器:要传输的数据项的数目未知并由源或目标外设控制,这些外设通过硬件发出传输结束的信号 ● 独立的源和目标传输宽度(字节、半字、字):源和目标的数据宽度不相等时,DMA自动封装/解封必要的传输数据来优化带宽。这个特性仅在FIFO模式下可用。 ● 对源和目标的增量或非增量寻址 ● 支持4个、8个和16个节拍的增量突发传输。突发增量的大小可由软件配置,通常等于外设FIFO大小的一半 ● 每个数据流都支持循环缓冲区管理 ● 5个事件标志(DMA 半传输、DMA 传输完成、DMA 传输错误、DMA FIFO 错误、直接模式错误),进行逻辑或运算,从而产生每个数据流的单个中断请求 STM32F4有两个DMA控制器,DMA1DMA2,本章,我们仅针对DMA2进行介绍。STM32F4DMA控制器框图如图28.1.1所示:
28.1.1 DMA控制器框图 DMA控制器执行直接存储器传输:因为采用AHB主总线,它可以控制AHB总线矩阵来启动AHB事务。它可以执行下列事务: 1,外设到存储器的传输 1,  存储器到外设的传输 3,存储器到存储器的传输     这里特别注意一下,存储器到存储器需要外设接口可以访问存储器,而仅DMA2的外设接口可以访问存储器,所以仅DMA2控制器支持存储器到存储器的传输,DMA1不支持。28.1.1中数据流的多通道选择,是通过DMA_SxCR寄存器控制的,如图28.1.2所示:  28.1.2 DMA数据流通道选择        从上图可以看出,DMA_SxCR控制数据流到底使用哪一个通道,每个数据流有8个通道可供选择,每次只能选择其中一个通道进行DMA传输。接下来,我们看看DMA2的各数据流通道映射表,如表28.1.1所示:  28.1.1 DMA2各数据流通道映射表 上表就列出了DMA2所有可能的选择情况,来总共64种组合,比如本章我们要实现串口1DMA发送,即USART1_TX,就必须选择DMA2的数据流7,通道4,来进行DMA传输。这里注意一下,有的外设(比如USART1_RX)可能有多个通道可以选择,大家随意选择一个就可以了。 接下来,我们介绍一下DMA设置相关的几个寄存器。 第一个是DMA中断状态寄存器,该寄存器总共有2个:DMA_LISRDMA_HISR,每个寄存器管理4数据流(总共8个),DMA_LISR寄存器用于管理数据流0~3,而DMA_HISR用于管理数据流4~7。这两个寄存器各位描述都完全一模一样,只是管理的数据流不一样。 这里,我们仅以DMA_LISR寄存器为例进行介绍,DMA_LISR各位描述如图28.1.3所示:      28.1.3 DMA_LISR寄存器各位描述 如果开启了DMA_LISR中这些位对应的中断,则在达到条件后就会跳到中断服务函数里面去,即使没开启,我们也可以通过查询这些位来获得当前DMA传输的状态。这里我们常用的是TCIFx位,即数据流xDMA传输完成与否标志。注意此寄存器为只读寄存器,所以在这些位被置位之后,只能通过其他的操作来清除。DMA_HISR寄存器各位描述通DMA_LISR寄存器各位描述完全一样,只是对应数据流4~7,这里我们就不列出来了。 第二个是DMA中断标志清除寄存器, 该寄存器同样有2个:DMA_LIFCRDMA_HIFCR,同样是每个寄存器控制4个数据流,DMA_LIFCR寄存器用于管理数据流0~3,而DMA_ HIFCR用于管理数据流4~7。这两个寄存器各位描述都完全一模一样,只是管理的数据流不一样。 这里,我们仅以DMA_LIFCR寄存器为例进行介绍,DMA_LIFCR各位描述如图28.1.4所示:  28.1.4 DMA_LIFCR寄存器各位描述 DMA_LIFCR的各位就是用来清除DMA_LISR的对应位的,通过写1清除。在DMA_LISR被置位后,我们必须通过向该位寄存器对应的位写入1来清除。DMA_HIFCR的使用同DMA_LIFCR类似,这里就不做介绍了。 第三个是DMA数据流x配置寄存器(DMA_SxCR)(x=0~7,下同)。该寄存器的我们在这里就不贴出来了,见《STM32F4xx中文参考手册》第2239.5.5一节。该寄存器控制着DMA的很多相关信息,包括数据宽度、外设及存储器的宽度、优先级、增量模式、传输方向、中断允许、使能等都是通过该寄存器来设置的。所以DMA_ SxCRDMA传输的核心控制寄存器。 第四个是DMA数据流x数据项数寄存器(DMA_SxNDTR)。这个寄存器控制DMA数据流x的每次传输所要传输的数据量。其设置范围为0~65535。并且该寄存器的值会随着传输的进行而减少,当该寄存器的值为0的时候就代表此次数据传输已经全部发送完成了。所以可以通过这个寄存器的值来知道当前DMA传输的进度。特别注意,这里是数据项数目,而不是指的字节数。比如设置数据位宽为16位,那么传输一次(一个项)就是2个字节。 第五个是DMA数据流x的外设地址寄存器(DMA_SxPAR)。该寄存器用来存储STM32F4外设的地址,比如我们使用串口1,那么该寄存器必须写入0x40011004(其实就是&USART1_DR)。如果使用其他外设,就修改成相应外设的地址就行了。 最后一个是DMA数据流x的存储器地址寄存器,由于STM32F4DMA支持双缓存,所以存储器地址寄存器有两个:DMA_SxM0ARDMA_SxM1AR,其中DMA_SxM1AR仅在双缓冲模式下,才有效。本章我们没用到双缓冲模式,所以存储器地址寄存器就是:DMA_SxM0AR,该寄存器和DMA_CPARx差不多,但是是用来放存储器的地址的。比如我们使用SendBuf[8200]数组来做存储器,那么我们在DMA_SxM0AR中写入&SendBuff就可以了。 DMA相关寄存器就为大家介绍到这里,关于这些寄存器的详细描述,请参考《STM32F4xx中文参考手册》第9.5节。本章我们要用到串口1的发送,属于DMA2的数据流7,通道4,接下来我们就介绍下使用库函数的配置步骤和方法。首先这里我们需要指出的是,DMA相关的库函数支持在文件stm32f4xx_dma.c以及对应的头文件stm32f4xx_dac.h中。具体步骤如下: 1)使能DMA2时钟,并等待数据流可配置。 DMA的时钟使能是通过AHB1ENR寄存器来控制的,这里我们要先使能时钟,才可以配置DMA相关寄存器。所以先要使能DMA2的时钟。另外,要对配置寄存器(DMA_SxCR)进行设置,必须先等待其最低位为0(也就是DMA传输禁止了),才可以进行配置。 库函数使能DMA2时钟的方法为: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能 等待DMA可配置,也就是等待DMA_SxCR寄存器最低位为0的方法为: while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待DMA可配置 2) 初始化DMA2数据流7,包括配置通道,外设地址,存储器地址,传输数据量等。 DMA的某个数据流各种配置参数初始化是通过DMA_Init函数实现的: void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);  函数的第一个参数是指定初始化的DMA的数据流编号,这个很容易理解。入口参数范围为:DMAx_Stream0~ DMAx_Stream7(x=1,2)。下面我们主要看看第二个参数。跟其他外设一样,同样是通过初始化结构体成员变量值来达到初始化的目的,下面我们来看看DMA_InitTypeDef结构体的定义: typedef struct {   uint32_t DMA_Channel;             uint32_t DMA_PeripheralBaseAddr;   uint32_t DMA_Memory0BaseAddr;      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_FIFOMode;           uint32_t DMA_FIFOThreshold;        uint32_t DMA_MemoryBurst;         uint32_t DMA_PeripheralBurst;    }DMA_InitTypeDef; 这个结构体的成员比较多,但是每个成员变量的意义我们在前面基本都已经讲解过,这里我们一一做个简要的介绍。 第一个参数DMA_Channel用来设置DMA数据流对应的通道。前面我们已经讲解过,可供每个数据流选择的通道请求多达8个,取值范围为:DMA_Channel_0~ DMA_Channel_7。 第二个参数DMA_PeripheralBaseAddr用来设置DMA传输的外设基地址,比如要进行串口 DMA传输,那么外设基地址为串口接受发送数据存储器USART1->DR的地址,表示方法为&USART1->DR 第三个参数DMA_Memory0BaseAddr为内存基地址,也就是我们存放DMA传输数据的内存地址。 第四个参数DMA_DIR设置数据传输方向,决定是从外设读取数据到内存还送从内存读取数据发送到外设,也就是外设是源地还是目的地,这里我们设置为从内存读取数据发送到串口,所以外设自然就是目的地了,所以选择值为DMA_DIR_PeripheralDST 第五个参数DMA_BufferSize设置一次传输数据量的大小,这个很容易理解。 第六个参数DMA_PeripheralInc设置传输数据的时候外设地址是不变还是递增。如果设置为递增,那么下一次传输的时候地址加1,这里因为我们是一直往固定外设地址&USART1->DR发送数据,所以地址不递增,值为DMA_PeripheralInc_Disable 第七个参数DMA_MemoryInc设置传输数据时候内存地址是否递增。这个参数和DMA_PeripheralInc意思接近,只不过针对的是内存。这里我们的场景是将内存中连续存储单元的数据发送到串口,毫无疑问内存地址是需要递增的,所以值为DMA_MemoryInc_Enable 第八个参数DMA_PeripheralDataSize用来设置外设的数据长度是为字节传输(8bits),半字传输(16bits)还是字传输(32bits),这里我们是8位字节传输,所以值设置为DMA_PeripheralDataSize_Byte 第九个参数DMA_MemoryDataSize是用来设置内存的数据长度,和第七个参数意思接近,这里我们同样设置为字节传输DMA_MemoryDataSize_Byte 第十个参数DMA_Mode用来设置DMA模式是否循环采集,也就是说,比如我们要从内存中采集64个字节发送到串口,如果设置为重复采集,那么它会在64个字节采集完成之后继续从内存的第一个地址采集,如此循环。这里我们设置为一次连续采集完成之后不循环。所以设置值为DMA_Mode_Normal。在我们下面的实验中,如果设置此参数为循环采集,那么你会看到串口不停的打印数据,不会中断,大家在实验中可以修改这个参数测试一下。 第十一个参数DMA_Priority是用来设置DMA通道的优先级,有低,中,高,超高三种模式,这个在前面讲解过,这里我们设置优先级别为中级,所以值为DMA_Priority_Medium。优先级可以随便设置,因为我们只有一个数据流被开启了。假设有多个数据流开启(最多8个),那么就要设置优先级了,DMA仲裁器将根据这些优先级的设置来决定先执行那个数据流的DMA。优先级越高的,越早执行,当优先级相同的时候,根据硬件上的编号来决定哪个先执行(编号越小越优先)。 第十二个参数DMA_FIFOMode用来设置是否开启FIFO模式。这里我们不开启所以选择DMA_FIFOMode_Disable 第十三个参数DMA_FIFOThreshold用来选择FIFO阈值。根据前面讲解可以为FIFO容量的1/4,1/2,3/4以及1倍。这里我们实际并没有开启FIFO模式,所以可以不关心。 第十四个参数DMA_MemoryBurst用来配置存储器突发传输配置。可以选择为4个节拍的增量突发传输DMA_MemoryBurst_INC48个节拍的增量突发传输DMA_MemoryBurst_INC816个街拍的增量突发传输DMA_MemoryBurst_INC16以及单次传输DMA_MemoryBurst_Single 第十五个参数DMA_PeripheralBurst用来配置外设突发传输配置。跟前面一个参数DMA_MemoryBurst作用类似,只不过一个针对的是存储器,一个是外设。这里我们选择单次传输DMA_PeripheralBurst_Single 参数含义我们就给大家讲解到这里,具体详细配置,大家可以参考中文参考手册相关寄存器配置可以更加详细的了解含义。接下来我们给出上面场景的实例代码:   /* 配置 DMA Stream */   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)使能串口1DMA发送 进行DMA配置之后,我们就要开启串口的DMA发送功能,使用的函数是:
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);  //使能串口1DMA发送   如果是要使能串口DMA接受,那么第二个参数修改为USART_DMAReq_Rx即可。 4)使能DMA2数据流7,启动传输。   使能DMA数据流的函数为:  void DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState)   使能DMA2_Stream7,启动传输的方法为:  DMA_Cmd (DMA2_Stream7ENABLE; 通过以上4步设置,我们就可以启动一次USART1DMA传输了。 5)查询DMA传输状态DMA传输过程中,我们要查询DMA传输通道的状态,使用的函数是: FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG) 比如我们要查询DMA数据流7传输是否完成,方法是: DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7); 这里还有一个比较重要的函数就是获取当前剩余数据量大小的函数: uint16_t DMA_GetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx); 比如我们要获取DMA数据流7还有多少个数据没有传输,方法是: DMA_GetCurrDataCounter(DMA1_Channel4); 同样,我们也可以设置对应的DMA数据流传输的数据量大小,函数为: void DMA_SetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx, uint16_t Counter); DMA相关的库函数我们就讲解到这里,大家可以查看固件库中文手册详细了解。  

28.2 硬件设计

所以本章用到的硬件资源有: 1)  指示灯DS0 2)  KEY0按键 3)  串口 4) TFTLCD模块 5) DMA 本章我们将利用外部按键KEY0来控制DMA的传送,每按一次KEY0DMA就传送一次数据到USART1,然后在TFTLCD模块上显示进度等信息。DS0还是用来做为程序运行的指示灯。 本章实验需要注意P6口的RXDTXD是否和PA9PA10连接上,如果没有,请先连接。

28.3 软件设计

打开本章的实验工程可以看到,我们在FWLIB分组下面增加了DMA支持文件stm32f4xx_dma.c,同时引入了stm32f4xx_dma.h头文件支持。在HARDWARE分组下面我们新增了dma.c以及对应头文件dma.h用来存放dma相关的函数和定义。 打开dma.c文件,代码如下: //DMAx的各通道配置 //这里的传输形式是固定的,这点要根据不同的情况来修改 //从存储器->外设模式/8位数据宽度/存储器增量模式 //DMA_StreamxMA数据流,DMA1_Stream0~7/DMA2_Stream0~7 //chxMA通道选择, @ref DMA_channel 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) {     DMA_InitTypeDef  DMA_InitStructure;     if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1     {       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可配置   /* 配置 DMA Stream */   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;//FIFO模式禁止           DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;//FIFO阈值   DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输   DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输   DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化DMA Stream } //开启一次DMA传输 //DMA_StreamxMA数据流,DMA1_Stream0~7/DMA2_Stream0~7 //ndtr:数据传输量  void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr) {     DMA_Cmd(DMA_Streamx, DISABLE);                      //关闭DMA传输     while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}  //确保DMA可以被设置      DMA_SetCurrDataCounter(DMA_Streamx,ndtr);          //数据传输量      DMA_Cmd(DMA_Streamx, ENABLE);                      //开启DMA传输 }     该部分代码仅仅2个函数,MYDMA_Config函数,基本上就是按照我们上面介绍的步骤来初始化DMA的,该函数是一个通用的DMA配置函数,DMA1/DMA2的所有通道,都可以利用该函数配置,不过有些固定参数可能要适当修改(比如位宽,传输方向等)。该函数在外部只能修改DMA及数据流编号、通道号、外设地址、存储器地址(SxM0AR)传输数据量等几个参数,更多的其他设置只能在该函数内部修改。MYDMA_Enable函数就是设置DMA缓存大小并且使能DMA数据流。对照前面的配置步骤的详细讲解看看这部分代码即可。 dma.h头文件内容比较简单,主要是函数申明,这里我们不细说。 接下来我们看看那main函数如下: /*发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.*/ #define SEND_BUF_SIZE 8200  u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区 const u8 TEXT_TO_SEND[]={"ALIENTEK Explorer STM32F4 DMA 串口实验"};   int main(void) {     u16 i;     u8 t=0,j,mask=0;     float pro=0;//进度     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2     delay_init(168);     //初始化延时函数     uart_init(115200);  //初始化串口波特率为115200         LED_Init();                 //初始化LED     LCD_Init();                 //LCD初始化     KEY_Init();                 //按键初始化 /*DMA2,STEAM7,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,"2014/5/6");        LCD_ShowString(30,130,200,16,16,"KEY0:Start");      POINT_COLOR=BLUE;//设置字体为蓝 {MOD}           //显示提示信息       j=sizeof(TEXT_TO_SEND);        for(i=0;i<SEND_BUF_SIZE;i++)//填充ASCII字符集数据     {         if(t>=j)//加入换行符         {             if(mask)             {                 SendBuff=0x0a;t=0;             }else             {                 SendBuff=0x0d;mask++;             }           }else//复制TEXT_TO_SEND语句         {             mask=0;             SendBuff=TEXT_TO_SEND[t];t++;         }               }           POINT_COLOR=BLUE;//设置字体为蓝 {MOD}         i=0;     while(1)     {         t=KEY_Scan(0);         if(t==KEY0_PRES)  //KEY0按下         {             printf(" DMA DATA: ");                     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_Enable(DMA2_Stream7,SEND_BUF_SIZE);   //开始一次DMA传输!             //等待DMA传输完成,此时我们来做另外一些事,点灯             //实际应用中,传输数据期间,可以执行另外的任务             while(1)             {                 if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET) //等待DMA2_Steam7传输完成                 {                  DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);//清传输完成标志                  break;                 }                 pro=DMA_GetCurrDataCounter(DMA2_Stream7);//得到当前剩余数据数                 pro=1-pro/SEND_BUF_SIZE;//得到百分比                      pro*=100;                   //扩大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;         }              }           } main函数的流程大致是:先初始化内存SendBuff的值,然后通过KEY0开启串口DMA发送,在发送过程中,通过DMA_GetCurrDataCounter()函数获取当前还剩余的数据量来计算传输百分比,最后在传输结束之后清除相应标志位,提示已经传输完成。这里还有一点要注意,因为是使用的串口1 DMA发送,所以代码中使用USART_DMACmd函数开启串口的DMA发送: USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口1DMA发送 至此,DMA串口传输的软件设计就完成了。

28.4 下载验证

在代码编译成功之后,我们通过串口下载代码到ALIENTEK探索者STM32F4开发板上,可以看到LCD显示如图28.4.1所示:  28.4.1 DMA实验测试图 伴随DS0的不停闪烁,提示程序在运行。我们打开串口调试助手,然后按KEY0,可以看到串口显示如图28.4.2所示的内容:  28.4.2 串口收到的数据内容 可以看到串口收到了探索者STM32F4开发板发送过来的数据,同时可以看到TFTLCD上显示了进度等信息,如图28.4.3所示:  28.4.3 DMA串口数据传输中 至此,我们整个DMA实验就结束了,希望大家通过本章的学习,掌握STM32F4DMA使用。DMA是个非常好的功能,它不但能减轻CPU负担,还能提高数据传输速度,合理的应用DMA,往往能让你的程序设计变得简单。  
 实验详细手册和源码下载地址:http://www.openedv.com/posts/list/41586.htm  正点原子探索者STM32F407开发板购买地址http://item.taobao.com/item.htm?id=41855882779
  

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
4条回答
sucore
1楼-- · 2019-07-20 19:08
 精彩回答 2  元偷偷看……
电子玩霸
2楼-- · 2019-07-20 21:18
回复【2楼】sucore:
---------------------------------
对,我的F411也发现这个错误,为什么呢?
笨笨的小马驹爱奔跑
3楼-- · 2019-07-20 22:59
我遇到了 一直进入中断的错误,标志清0了,用的USART3的接收DMA,没有数据也是一直进入中断
洋洋阳阳
4楼-- · 2019-07-21 04:34
 精彩回答 2  元偷偷看……

一周热门 更多>