stm32的DMA学习笔记 串口空闲中断+dma 1.DMA结构框架2.DMA名称的详细解析通道数据的高速通道 每个通道可以链接指定的外设(DMA2)可以将通道链接到内存。流理解为货车DMA的使用单位就是流使能dma就是以流作为基本单位(让那个货车可以发车)判断优先级就是判断流的优先级。(可以使用多个流但是一个流只能绑定一个通道)FIFO:突发模式的前提条件将数据寄存起来当满足突发的数据量后发送。突发模式批量的装卸货物简单理解就是一次性给目标地址发送几个字节的数据比如一次给目标发送4个字节的数据如果不使用突发则就是一次发送一个字节。将它分化成三个部分第一部分: MSIZE内存数据宽度相当于“包裹的大小”。字节 小包8位半字 中包16位字 大包32位第二部分FIFO 容量STM32 的 DMA FIFO 总容量固定是4 个字即 16 个字节。第三部分MBURST内存突发相当于“卸货方式”。INCR4 一口气卸 4 次货。INCR8 一口气卸 8 次货。INCR16 一口气卸 16 次货。节拍其实就是字节四节拍就是四个字节举例1.将MSIZE设置成字节FIFO级别设置成1/4, MBURST设置成4个节拍突发1次。分析单位是字节级别是1/4 16/44字节MBURST 突发传输四个字节且只突发一次总结一次性传输4个字节到目标源。2.将MSIZE设置成字节FIFO级别设置成1/2, MBURST设置成4个节拍突发2次。分析单位字节级别1/216/28 MBURST突发传输四个字节且突发两次(发送8字节)总结分2次突发发送8个字节到目标源。虽然从宏观上看这 8 个字节是一起发出去的但在总线时序上它们是两个独立的 INCR4 事务3.编程实战//在usart中开启空闲中断USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);void dma_initconfig(void){DMA_InitTypeDef dma_inittypedef;// 1. 必须先开启 DMA2 的时钟(STM32F4中DMA2挂载在AHB1总线上)RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);DMA_DeInit(DMA2_Stream1);while(DMA_GetCmdStatus(DMA2_Stream1) DISABLE){//等待dma流初始化完成。}//选定流通道dma_inittypedef.DMA_Channel DMA_Channel_2;//外设到内存 传输放向dma_inittypedef.DMA_DIR DMA_DIR_PeripheralToMemory;//DMA缓冲区500dma_inittypedef.DMA_BufferSize 500;//单次传输dma_inittypedef.DMA_Mode DMA_Mode_Normal;//DMAL流的优先级 当总线冲突时高优先级的流会先传输dma_inittypedef.DMA_Priority DMA_Priority_Low ;//外部内存通道地址 内存目标地址dma_inittypedef.DMA_PeripheralBaseAddr (uint32_t)(USART1-DR);//外设传输单位大小dma_inittypedef.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte;//外设地址不递增dma_inittypedef.DMA_PeripheralInc DMA_PeripheralInc_Disable;//外设端突发传输 一次传输几个传输单元 配合FIFO寄存器dma_inittypedef.DMA_PeripheralBurst DMA_PeripheralBurst_Single;//内存源地址dma_inittypedef.DMA_Memory0BaseAddr (uint32_t)buf;//内存传输单位大小dma_inittypedef.DMA_MemoryDataSize DMA_MemoryDataSize_Byte;//内存地址递增dma_inittypedef.DMA_MemoryInc DMA_MemoryInc_Enable;//内存端突发传输 一次传输几个传输单元 可以提高传输量 配合FIFO寄存器dma_inittypedef.DMA_MemoryBurst DMA_MemoryBurst_Single;//FIFO配置 不开启dma_inittypedef.DMA_FIFOMode DMA_FIFOMode_Disable;//dma_inittypedef.DMA_FIFOThreshold DMA_Init(DMA2_Stream1, dma_inittypedef);//开启DMR 开启流1DMA_Cmd(DMA2_Stream1, ENABLE);}extern uint8_t buf[500];void USART1_IRQHandler(void) {// 判断是否是串口1触发了空闲中断即一帧数据接收完毕if (USART_GetITStatus(USART1, USART_IT_IDLE) ! RESET){// 【第一步】必须先读取 SR 和 DR 寄存器来清除 IDLE 标志位// 标准库中调用一次 USART_ReceiveData 即可自动完成这个硬件清除流程USART_ReceiveData(USART1);// 【第二步】暂停当前的 DMA 传输流防止后续数据继续覆盖 buf 数组DMA_Cmd(DMA2_Stream1, DISABLE);// 【第三步】计算这一帧到底接收了多少个字节的数据// 原理DMA 初始配置的总大小(500) - 当前剩余还没搬运的数量 实际已接收数量uint16_t data_len 500 - DMA_GetCurrDataCounter(DMA2_Stream1);// 【第四步】在这里处理你刚刚接收到的不定长数据// 例如解析协议、打印日志、转发数据等process_received_data(buf, data_len);// 【第五步】重新设置 DMA 下一次要搬运的总量恢复为500DMA_SetCurrDataCounter(DMA2_Stream1, 500);// 【第六步】再次使能 DMA 流准备接收下一帧数据DMA_Cmd(DMA2_Stream1, ENABLE); }}