完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一、STM32 DMA简介与功能说明
1、STM32F4 DMA简介 DMA(Direct memory access),即直接存储器访问。用于在外设与存储器之间以及存储器与存储器之间提供一种高速数据传输的方式。它无需CPU参与,通过硬件方式为RAM与I/O设备提供一条直接传送数据的通道。 STM32F4有2个DMA控制器,每个DMA控制器有8个数据流,每个数据流有8个通道(或请求)。其中2个DMA的请求映射如下: 2、DMA主要特性
DMA事务有给定数目的数据传输序列组成。其数据传输宽度(8bit、16bit、32bit)可用软件编程。 每个DMA传输包含三项操作:
8个数据流都可以提供源和目标之间的单向传输链路。 每个数据流配置后都可以执行:
传输方向使用DAM_SxCR寄存器中的DIR[1:0]bit进行配置,有三种传输方向:存储器到外设、外设到存储器或存储器到存储器。相应的源和目标地址如下图: 当传输数据宽度(在DMA_SxCR寄存器的PSIZE或MSIZE位中编程)分别是半字或字时,写入DMA_SxPAR或DMA_SxM0AR/M1AR寄存器的外设或存储器地址必须分别在字或半字地址的边界对齐。 7、指针递增与循环模式 根据DMA_SxCR寄存器中PINC和MINC位的状态,外设和存储器指针在每次传输后可以自动向后递增或保持常量。通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用,一般使用的是禁止模式。 如果使能了递增模式,则根据在DMA_SxCR寄存器PSIZE或MSIZE位中编程的数据宽度,下一次传输的地址将是前一次传输的地址递增1(对应一个字节)、2(对应半字)或 4(对应字)。 如果将PINCOS位置 1,则不论PSIZE值是多少,下一次传输的地址总是前一次传输的地址递增 4(自动与 32 位地址对齐)。 循环模式可用于处理循环缓冲区和连续数据流(例如ADC扫描模式)。可以使用DMA_SxCR寄存器中的CIRC位使能此特性。当激活循环模式时,要传输的数据项的数目在数据流配置阶段自动用设置的初始值进行加载,并继续响应 DMA 请求。 8、双缓冲模式 通过将DMA_SxCR寄存器中的DBM位置1,即可使能双缓冲区模式。此模式在处理数据量较大的情况下,十分有用,比如开启双缓冲采集摄像头数据。双缓冲模式除了有两个存储器指针之外,数据流的工作方式与常规(单缓冲区)数据流的一样。使能双缓冲区模式时,将自动使能循环模式(DMA_SxCR中的CIRC位的状态是“无关”),并在每次事务结束时交换存储器指针。在此模式下,每次事务结束时,DMA控制器都从一个存储器目标交换为另一个存储器目标。 这样,软件在处理一个存储器区域的同时,DMA传输还可以填充/使用第二个存储器区域。其库函数代码如下: /* * @param Memory1BaseAddr: the base address of the second buffer (Memory 1) * @param DMA_CurrentMemory: specifies which memory will be first buffer for * the transactions when the Stream will be enabled. * This parameter can be one of the following values: * @arg DMA_Memory_0: Memory 0 is the current buffer. * @arg DMA_Memory_1: Memory 1 is the current buffer. */ void DMA_DoubleBufferModeConfig(DMA_Stream_TypeDef* DMAy_Streamx, uint32_t Memory1BaseAddr, uint32_t DMA_CurrentMemory) { /* Check the parameters */ assert_param(IS_DMA_ALL_PERIPH(DMAy_Streamx)); assert_param(IS_DMA_CURRENT_MEM(DMA_CurrentMemory)); if (DMA_CurrentMemory != DMA_Memory_0) { /* Set Memory 1 as current memory address */ DMAy_Streamx->CR |= (uint32_t)(DMA_SxCR_CT); } else { /* Set Memory 0 as current memory address */ DMAy_Streamx->CR &= ~(uint32_t)(DMA_SxCR_CT); } /* Write to DMAy Streamx M1AR */ DMAy_Streamx->M1AR = Memory1BaseAddr; } 注意:在双缓冲区模式下使能数据流时,可遵循下列条件,实时更新 AHB 存储器的基址(DMA_SxM0AR或 DMA_SxM1AR): ● 当DMA_SxCR 寄存器中的CT位为“0”时,可以写入DMA_SxM1AR寄存器。当CT=“1”时,试图写入此寄存器会将错误标志位 (TEIF) 置 1,并自动禁止数据流。 ● 当DMA_SxCR 寄存器中的CT位为“1”时,可以写入DMA_SxM0AR寄存器。当CT=“0”时,试图写入此寄存器会将错误标志位 (TEIF) 置 1,并自动禁止数据流。 因此目标存储器依据DMA_SxCR 寄存器中CT值的情况,决定是从存储器 0 更改为 存储器 1(或从存储器 1 更改为存储器 0)。对于所有其它模式(双缓冲区模式除外),一旦使能数据流,存储器地址寄存器即被写保护。 二、代码分析 void DMA_UART_TX(void) { DMA_InitTypeDef DMA_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //开启DMA时钟 DMA_DeInit(DMA2_Stream7); while(DMA_GetCmdStatus(DMA2_Stream7) != DISABLE){} //等待stream可配置,即DMAy_SxCR.EN变为0 //配置Stream DMA_InitStructure.DMA_Channel = DMA_Channel_4; //从8个channel中选择一个 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //外设地址 DMA_InitStructure.DMA_Memory0BaseAddr = (u32)SendBuff; //存储器0地址,双缓存模式还要使用M1AR DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //存储器到外设模式 DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE; //数据传输量,以外设数据项为单位 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; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //单次传输 DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //单次传输 DMA_Init(DMA2_Stream7, &DMA_InitStructure); } 由最前面的DMA请求映射图可知,串口1对应DMA2数据流7通道4,即相应的配置DMA2_Stream7,DMA_Channel_4。因为是配置的DMA串口发送,所以外设地址对应(u32)&USART1->DR或(0x40011004,查存储器映射表即可);串口发送模式对应的是存储器到外设,故设置为DMA_DIR_MemoryToPeripheral。在配置其他外设(如SPI,ADC,DAC等)的DMA模式,改变上述地方即可,若为接收端,则需改为DMA_DIR_PeripheralToMemory,即外设到存储器,其他基本不变。 void DMA_TX(uint8_t *tx_buffer,uint16_t length) { DMA_InitTypeDef DMA_InitStructure; DMA_Cmd(DMA2_Stream7, DISABLE); //关闭DMA通道 DMA_SetCurrDataCounter(DMA2_Stream7, (uint16_t)length); //设置传输字节数 DMA2_Stream7->CR |= (1 << 10); //发送DMA流的地址不自增 DMA2_Stream7->M0AR = (uint32_t)tx_buffer; //设置接收和发送的内存地址 DMA_Cmd(DMA2_Stream7, ENABLE); //打开DMA通道 USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口1的DMA发送 while( DMA_GetFlagStatus(DMA2_Stream7, DMA_FLAG_TCIF7) == RESET); //等待传输完成 DMA_Cmd(DMA2_Stream7, DISABLE); //关闭DMA通道 DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7); //清除DMA传输完成标志 } 此处配置的是可变长变地址发送,即每次可发送不同的变量。当然也可以用初始化当中的配置buffer缓冲区和大小,此时若开启了DMA_LISR中TCIF7中断(数据流7传输完成中断标志),达到条件后就会跳到DMA2_Stream7中断服务函数里面,此时查询TCIF7的状态来获得当前DMA的传输状态,传输完成后清除DMA传输完成标志DMA_FLAG_TCIF7即可,代码如下: void DMA2_Channel7_IRQHandler(void) { //判断是否为DMA发送完成中断 if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)==SET) { DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7); } } 至此串口DMA发送数据介绍完毕,下一篇将介绍串口DMA接收数据。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1537 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1490 浏览 1 评论
910 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
654 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1538 浏览 2 评论
1845浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
596浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
499浏览 3评论
499浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
483浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-5 14:25 , Processed in 1.018853 second(s), Total 77, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号