完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
**DMA涉及概念讲解:
** ①:DMA即Direct Memory Access(直接存储器存取),是STM32特有的外设。大容量STM32产品集成了两个DMA,分别是DMA1和DMA2,。其中DMA1有7个通道,DMA2有5个通道,具体每个通道连接的外设可以参考STM32芯片的数据手册。 ②:通过DMA可以将数据在两个不同的地址之间进行传递,如存储器到外设寄存器,外设寄存器到存储器,也可以从存储器到存储器之间。 ③:当两个数据在不同的地址之间传递时,需要在程序配置中确定每次传输的字节数,确定是字节、半字还是字。 ④:DMA的每个通道优先级是可变的。以DMA1为例,它有7个通道,可以配置每个通道的优先级为很高、高、中,低四种中的一种。若两个通道的优先级一样,当两个通道同时有DMA请求时,通道号小的优先级则高。 ⑤:DMA每次传输的数据量是可变的,DMA中有一个专门的寄存器用于存储这个数据量值。这个寄存器是32位的,但高16位全部保留为0,实际上起作用的是低16位,所以每次传输的最大数据量值是65536。 ⑥:⑤中所示,比如设置数据量值为100,若DMA传输设置为循环模式,则100个数据传输完成后,将自动进行下一轮传输。若设置成非循环模式,则需要先关闭DMA,再设置数据量值,再开启DMA,才能进行下一轮传输。 ⑦:DMA在传输过程中,常用的有3种标志位–传输完成一半、传输全部完成,传输过程发生错误。可以在程序中设置开启对应标志位的中断,当标志位到来时,会执行中断服务程序。也可不开启相应标志位的中断。 ⑧:确定好要传输的外设和存储器地址之后,需要在程序中设置传输方向,即传输方向是从外设到寄存器,还是从寄存器到外设。 ⑨:DMA一般用来在外设和存储器之间进行数据传输,所以还要设置外设地址及存储器地址是否递增。例如定义一个数组,char data[100],外设地址为&UART->TX,若将数组中的100个数据传输到UART->TX中,则存储器地址需要每次递增,而外设地址不需要递增。 DMA配置过程: ①:确定传输数据的外设和寄存器地址 ②:确定传输方向 ③:确定每次传输的数据量值 ④:确定传输数据的字节数 ⑤:配置通道优先级 ⑥:确定传输是循环模式还是非循环模式 ⑦:如若需要开启中断,则开启响应位中断 注意:DMA也可以从存储器到存储器,但存储器到存储器过程只能为非循环模式。 一、如何理解DMA 对于DMA,打个比方就很好理解: 角色预设: 淘宝店主 —- STM32 MCU 快递员 —- 外设(如UART,SPI) 发货室 —- DMA 1、首先你是一个淘宝店主,如果每次发货收货都要跟快递沟通交涉会很浪费时间和精力。 2、然后你就自己建了一个发货室,发货室里有好多个货柜箱子,每个箱子上都写着快递名字(如果申通快递,顺丰快递等)。 3、每次发什么快递,你就找到对应的货柜箱子,把货物放进去即可,然后跟快递通知一声。 4、快递取走快件。 5、如果是收货,快递直接把快件放到对应的柜子,然后通知你一下。 6、你过来提取货物。 通过上面的方式,你可以不需要直接跟快递打交道,就可以轻松发货成功,DMA处理方式跟上面例子是一样的。如下图: 那么DMA在STM32上是具体怎么实现的呢? 我们先了解一下STM32关于DMA的相关配置。 1、两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道) ps:对应我们例子,就是有两个大的发货室,一个有7个货柜,另个有5个货柜。 2、在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) ps: 店主可以跟每个快递公司签订协议,可以在货柜前贴上加急(很高),很急(高),急(中),一般(低), 如果同时有几个快递员过来取货,优先根据上面的优先级先取件。 3、独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。 ps: 指的是货件大小 4、支持循环的缓冲器管理(会把原来的数据覆盖) 5、每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。 ps: 送快递出现的异常情况(送到了一半,送完,快递出错) 解释到这里,不知道大家能不能理解呢。后面是具体的配置。 1、DMA 对应通道如下图 DMA1: DMA2: 2、DMA配置 1)数据传输的目的地和来源 对应我的例子,就是送快递还是取快递。 2)定义DMA通道的DMA缓存的大小 ps: 即货柜大小,能存多少个快件 3)外设地址寄存器递增与否 4)内存地址寄存器递增与否 5)设定了外设数据宽度 6)设定了内存数据宽度 7)设置了DMA的工作模式 8)DMA通道的软件优先级 9)使能或关闭DMA通道的内存到内存传输 2)初始化DMA u8 sendbuf[1024]; u8 receivebuf[1024]; static void _uart1_dma_configuration() { DMA_InitTypeDef DMA_InitStructure; /* DMA1 Channel6 (triggered by USART1 Rx event) Config */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE); /* DMA1 Channel5 (triggered by USART1 Rx event) Config */ DMA_DeInit(DMA1_Channel5); DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;// 初始化外设地址,相当于“哪家快递” DMA_InitStructure.DMA_MemoryBaseAddr =(u32)receivebuf;// 内存地址,相当于几号柜 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设作为数据来源,即为收快递 DMA_InitStructure.DMA_BufferSize = DMASIZE ;// 缓存容量,即柜子大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不递增,即柜子对应的快递不变 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;// 内存递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设字节宽度,即快递运输快件大小度量(按重量算,还是按体积算) DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;// 内存字节宽度,即店主封装快递的度量(按重量,还是按体质进行封装) DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 正常模式,即满了就不在接收了,而不是循环存储 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;// 优先级很高,对应快递就是加急 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 内存与外设通信,而非内存到内存 DMA_Init(DMA1_Channel5, &DMA_InitStructure);// 把参数初始化,即拟好与快递公司的协议 DMA_Cmd(DMA1_Channel5, ENABLE);// 启动DMA,即与快递公司签订合同,正式生效 /* DMA1 Channel4 (triggered by USART1 Tx event) Config */ DMA_DeInit(DMA1_Channel4); DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base; // 外设地址,串口1, 即发件的快递 DMA_InitStructure.DMA_MemoryBaseAddr =(u32)sendbuf;// 发送内存地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;// 外设为传送数据目的地,即发送数据,即快递是发件 DMA_InitStructure.DMA_BufferSize = 0; //发送长度为0,即未有快递需要发送 DMA_Init(DMA1_Channel4, &DMA_InitStructure);//初始化 USART_ITConfig(USART1, USART_IT_TC, ENABLE);// 使能串口发送完成中断 USART_DMACmd(USART1, USART_DMAReq_Tx|USART_DMAReq_Rx, ENABLE);// 使能DMA串口发送和接受请求 下面代码是一个标准DMA设置,当然实际应用中可根据实际情况进行裁减,来自: ## https://blog.csdn.net/faihung/article/details/78748033 ``` DMA_DeInit(DMA_Channel1); ``` 上面这句是给DMA配置通道,根据ST提供的资料,STM3210Fx中DMA包含7个通道(CH1~CH7),也就是说可以为外设或memory提供7座“桥梁” ``` DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; ``` 上面语句中的DMA_InitStructure是一个DMA结构体,在库中有声明了,当然使用时就要先定义 了;DMA_PeripheralBaseAddr是该结构体中一个数据成员,给DMA一个起始地址,好比是一个buffer起始地址,数据流程是:外设 寄存器à DMA_PeripheralBaseAddàmemory中变量空间(或flash中数据空间等),ADC1_DR_Address是我定义的一个地址 变量; ``` DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue; ``` 上面这句很显然是DMA要连接在Memory中变量的地址,ADC_ConvertedValue是我自己在memory中定义的一个变量; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 上面的这句是设置DMA的传输方向,就如前面我所说的,DMA可以双向传输,也可以单向传输,这里设置的是单向传输,如果需要双向传输:把DMA_DIR_PeripheralSRC改成DMA_DIR_PeripheralDST即可。 DMA_InitStructure.DMA_BufferSize = 2; 上面的这句是设置DMA在传输时缓冲区的长度,前面有定义过了buffer的起始地址:ADC1_DR_Address ,为了安全性和可靠性,一般需要给buffer定义一个储存片区,这个参数的单位有三种类型:Byte、HalfWord、word,我设置的2个 half-word(见下面的设置);32位的MCU中1个half-word占16 bits。 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 上面的这句是设置DMA的外设递增模式,如果DMA选用的通道(CHx)有多个外设连接,需要使用外设递增模式:DMA_PeripheralInc_Enable;我的例子里DMA只与ADC1建立了联系,所以选用DMA_PeripheralInc_Disable DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 上面的这句是设置DMA的内存递增模式,DMA访问多个内存参数时,需要使用DMA_MemoryInc_Enable,当DMA只访问一个内存参数时,可设置成:DMA_MemoryInc_Disable。 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 上面的这句是设置DMA在访问时每次操作的数据长度。有三种数据长度类型,前面已经讲过了,这里不在叙述。 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 与上面雷同。在此不再说明。 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 上面的这句是设置DMA的传输模式:连续不断的循环模式,若只想访问一次后就不要访问了(或按指令操作来反问,也就是想要它访问的时候就访问,不要它访问的时候就停止),可以设置成通用模式:DMA_Mode_Normal DMA_InitStructure.DMA_Priority = DMA_Priority_High; 上面的这句是设置DMA的优先级别:可以分为4级:VeryHigh,High,Medium,Low. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; 上面的这句是设置DMA的2个memory中的变量互相访问的 DMA_Init(DMA_Channel1,&DMA_InitStructure); 前面那些都是对DMA结构体成员的设置,在次再统一对DMA整个模块做一次初始化,使得DMA各成员与上面的参数一致。 DMA_Cmd(DMA_Channel1,ENABLE); 哈哈哈!这一句我想我就不罗嗦了,大家一看就明白。 接下来,我们去STM32的程序中来分析下DMA配置的详细过程: 我们主要详细的讲解下两个配置函数:DMA_Configuration()和DMA_Init()这两个函数,来自: ## https://blog.csdn.net/u010280307/article/details/53334985 ``` void DMA_Configuration(void) { DMA_InitTypeDef DMA_InitStructure; /* DMA channel1 configuration */ DMA_DeInit(DMA1_Channel1);//重置DMA的寄存器的值,配置为缺省值 DMA_InitStructure.DMA_PeripheralBaseAddr =(u32)&ADC1->DR; /*设置 DMA 外设基地址,即为转换结果的寄存器*/ DMA_InitStructure.DMA_MemoryBaseAddr =(u32)&AD_Value;/*定义内存基地址*/ DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; /*定义AD外设作为数据传输的来源*/ DMA_InitStructure.DMA_BufferSize = N*M;/*指定DMA通道DMA缓存的大小,即需要开辟几个内存空间*/ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /*寄存器地址国定*/ DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /*设定内存地址递增,即每次DMA都是将*/ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;/* 定义外设和内存的数据宽度*/ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;/*设定DMA工作再循环缓存模式*/ DMA_InitStructure.DMA_Priority = DMA_Priority_High;/*设定DMA选定的通道的软件优先级*/ DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//关闭内存到内存的传输 DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE);/* Enable DMA channel1 */ } ``` ``` void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct) { uint32_t tmpreg = 0; /*--------------------------- DMAy Channelx CCR Configuration -----------------*/ /* Get the DMAy_Channelx CCR value */ tmpreg = DMAy_Channelx->CCR; /* Clear MEM2MEM, PL, MSIZE, PSIZE, MINC, PINC, CIRC and DIR bits */ tmpreg &= CCR_CLEAR_Mask; /* Configure DMAy Channelx: data transfer, data size, priority level and mode */ /* Set DIR bit according to DMA_DIR value */ /* Set CIRC bit according to DMA_Mode value */ /* Set PINC bit according to DMA_PeripheralInc value */ /* Set MINC bit according to DMA_MemoryInc value */ /* Set PSIZE bits according to DMA_PeripheralDataSize value */ /* Set MSIZE bits according to DMA_MemoryDataSize value */ /* Set PL bits according to DMA_Priority value */ /* Set the MEM2MEM bit according to DMA_M2M value */ tmpreg |= DMA_InitStruct->DMA_DIR | DMA_InitStruct->DMA_Mode | DMA_InitStruct->DMA_PeripheralInc | DMA_InitStruct->DMA_MemoryInc | DMA_InitStruct->DMA_PeripheralDataSize | DMA_InitStruct->DMA_MemoryDataSize | DMA_InitStruct->DMA_Priority | DMA_InitStruct->DMA_M2M; /* Write to DMAy Channelx CCR */ DMAy_Channelx->CCR = tmpreg; /*--------------------------- DMAy Channelx CNDTR Configuration ---------------*/ /* Write to DMAy Channelx CNDTR */ DMAy_Channelx->CNDTR = DMA_InitStruct->DMA_BufferSize; /*--------------------------- DMAy Channelx CPAR Configuration ----------------*/ /* Write to DMAy Channelx CPAR */ DMAy_Channelx->CPAR = DMA_InitStruct->DMA_PeripheralBaseAddr; /*--------------------------- DMAy Channelx CMAR Configuration ----------------*/ /* Write to DMAy Channelx CMAR */ DMAy_Channelx->CMAR = DMA_InitStruct->DMA_MemoryBaseAddr; } ``` 将上面两个函数比较一下就可以知道,前者函数对于后者来说就相当于是一个中间量的过程,暂时的将需要的配置参数写入一个结构体DMA_InitTypeDef中,后面调用DMA_Init这个函数之后,重新配置物理地址中DMA的寄存器相应的位。下面附录上两个函数中的结构体参数组成 ``` typedef struct { __IO uint32_t CCR; __IO uint32_t CNDTR; __IO uint32_t CPAR; __IO uint32_t CMAR; } DMA_Channel_TypeDef; ``` ``` DMA_InitTypeDef DMA_InitStructure; 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; |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1975 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1760 浏览 1 评论
1232 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
819 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1776 浏览 2 评论
2015浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
892浏览 4评论
stm32f4下spi+dma读取数据不对是什么原因导致的?
318浏览 3评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
671浏览 3评论
662浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-2-24 04:58 , Processed in 0.726105 second(s), Total 42, Slave 37 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191