<div style="padding-bottom: 5px; line-height: 1.5; background-color: rgb(255,255,255); margin: 0px; padding-left: 5px; padding-right: 5px; color: rgb(0,0,0); font-size: 12px; padding-top: 5px"> <a name="_Toc342394245">26.1 STM32 DMA简介 DMA,全称为:Direct Memory Access,即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,能使CPU的效率大为提高。 STM32最多有2个DMA控制器(DMA2仅存在大容量产品中),DMA1有7个通道。DMA2有5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁起来协调各个DMA请求的优先权。 STM32的DMA有以下一些特性: ●每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。 ●在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),假如在相等优先权时由硬件决定(请求0优先于请求1,依此类推) 。 ●独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。 ●支持循环的缓冲器管理 ●每个通道都有3个事件标志(DMA 半传输,DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。 ●存储器和存储器间的传输 ●外设和存储器,存储器和外设的传输 ●闪存、SRAM、外设的SRAM、APB1 APB2和AHB外设均可作为访问的源和目标。 ●可编程的数据传输数目:最大为65536 STM32F103ZET6有两个DMA控制器,DMA1和DMA2,本章,我们仅针对DMA1进行介绍。 从外设(TIMx、ADC、SPIx、I2Cx和USARTx)产生的DMA请求,通过逻辑或输入到DMA控制器,这就意味着同时只能有一个请求有效。外设的DMA请求,可以通过设置相应的外设寄存器中的控制位,被独立地开启或关闭。 表26.1.1是DMA1各通道一览表: 表26.1.1 DMA1个通道一览表 这里解释一下上面说的逻辑或,例如通道1的几个DMA1请求(ADC1、TIM2_CH3、TIM4_CH1),这几个是通过逻辑或到通道1的,这样我们在同一时间,就只能使用其中的一个。其他通道也是类似的。 这里我们要使用的是串口1的DMA传送,也就是要用到通道4。接下来,我们介绍一下DMA设置相关的几个寄存器。 第一个是DMA中断状态寄存器(DMA_ISR)。该寄存器的各位描述如图26.1.1所示: 图26.1.1 DMA_ISR寄存器各位描述 我们如果开启了DMA_ISR中这些中断,在达到条件后就会跳到中断服务函数里面去,即使没开启,我们也可以通过查询这些位来获得当前DMA传输的状态。这里我们常用的是TCIFx,即通道DMA传输完成与否的标志。注意此寄存器为只读寄存器,所以在这些位被置位之后,只能通过其他的操作来清除。 第二个是DMA中断标志清除寄存器(DMA_IFCR)。该寄存器的各位描述如图26.1.2所示: 图26.1.2 DMA_IFCR寄存器各位描述DMA_IFCR的各位就是用来清除DMA_ISR的对应位的,通过写0清除。在DMA_ISR被置位后,我们必须通过向该位寄存器对应的位写入0来清除。 第三个是DMA通道x配置寄存器(DMA_CCRx)(x=1~7,下同)。该寄存器的我们在这里就不贴出来了,见《STM32参考手册》第150页10.4.3一节。该寄存器控制着DMA的很多相关信息,包括数据宽度、外设及存储器的宽度、通道优先级、增量模式、传输方向、中断允许、使能等都是通过该寄存器来设置的。所以DMA_CCRx是DMA传输的核心控制寄存器。 第四个是DMA通道x传输数据量寄存器(DMA_CNDTRx)。这个寄存器控制DMA通道x的每次传输所要传输的数据量。其设置范围为0~65535。并且该寄存器的值会随着传输的进行而减少,当该寄存器的值为0的时候就代表此次数据传输已经全部发送完成了。所以可以通过这个寄存器的值来知道当前DMA传输的进度。 第五个是DMA通道x的外设地址寄存器(DMA_CPARx)。该寄存器用来存储STM32外设的地址,比如我们使用串口1,那么该寄存器必须写入0x40013804(其实就是&USART1_DR)。如果使用其他外设,就修改成相应外设的地址就行了。 最后一个是DMA通道x的存储器地址寄存器(DMA_CMARx),该寄存器和DMA_CPARx差不多,但是是用来放存储器的地址的。比如我们使用SendBuf[5200]数组来做存储器,那么我们在DMA_CMARx中写入&SendBuff就可以了。 DMA相关寄存器就为大家介绍到这里,此节我们要用到串口1的发送,属于DMA1的通道4,接下来我们就介绍下DMA1通道4的配置步骤: 1)设置外设地址。 设置外设地址通过DMA1_CPAR4来设置,我们只要在这个寄存器里面写入&USART1_DR的值就可以了。该地址将作为DMA传输的目标地址。 2)设置存储器地址。 设置存储器地址,我们通过DMA1_CMAR4来设置,假设我们要把数组SendBuf作为存储器,那么我们在该寄存器写入&SendBuf就可以了。该地址将作为DMA传输的源地址。 3)设置传输数据量。 通过DMA1_CNDTR4来设置DMA1通道4的数据传输量,这里面写入此次你要传输的数据量就可以了,也就是SendBuf的大小。该寄存器的数值将在DMA启动后自减,每次新的DMA传输,都重新向该寄存器写入要传输的数据量。 4)设置通道4的配置信息。 配置信息通过DMA1_CCR4来设置。这里我们设置存储器和外设的数据位宽均为8,且模式是存储器到外设的存储器增量模式。优先级可以随便设置,因为我们只有一个通道被开启了。假设有多个通道开启(最多7个),那么就要设置优先级了,DMA仲裁器将根据这些优先级的设置来决定先执行那个通道的DMA。优先级越高的,越早执行,当优先级相同的时候,根据硬件上的编号来决定哪个先执行(编号越小越优先)。 5)使能DMA1通道4,启动传输。 在以上配置都完成了之后,我们就使能DMA1_CCR4的最低位开启DMA传输,这里注意要设置USART1的使能DMA传输位,通过USART1->CR3的第七位设置。 通过以上5步设置,我们就可以启动一次USART1的DMA传输了。 <a name="_Toc342394247">26.3 软件设计 打开上一章的工程,首先在HARDWARE文件夹下新建一个DMA的文件夹。然后新建一个dma.c的文件和dma.h的头文件,保存在DMA文件夹下,并将DMA文件夹加入头文件包含路径。 打开dma.c文件,输入如下代码: #include "dma.h" #include "delay.h" u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度 //DMA1的各通道配置 //这里的传输形式是固定的,这点要根据不同的情况来修改 //从存储器->外设模式/8位数据宽度/存储器增量模式 //DMA_CHxMA通道CHx //cpar:外设地址 //cmar:存储器地址 //cndtr:数据传输量 void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr) { RCC->AHBENR|=1CMAR=(u32)cmar; //DMA1,存储器地址 DMA1_MEM_LEN=cndtr; //保存DMA传输数据量 DMA_CHx->CNDTR=cndtr; //DMA1,传输数据量 DMA_CHx->CCR=0X00000000; //复位 <div style="margin: 5px 0px"> DMA_CHx->CCR|=1 |