stm32串口dma加空闲中断 实现fifo接收数据 串口高效收发思路
2020-06-03 05:14
1691 查看
我做这个串口数据接收 dma+空闲中断 加fifo 实现串口的高效收发 ,主要是串口接收的数据长度不定长,时间超时也不好做,还要串口收发的效率要高,采用串口数据的接收 dma+空闲中断+fifo的方式 速度快和效率高,占用cpu的时间短
对比了其他几种方式
1:采用串口中断的话,每接收1byte就得中断一次。这样太消耗CPU资源! 频繁进中断,占用中断,特别是对时间和时序要求比较严格的时候 串口频繁进入中断导致其他中断时序有影响
2:采用DMA方式接收数据,接收的数据长度必须是固定的 对于接收数据长度不固定就不怎么好弄了,特别像gprs通信,接收长度不固定,这些都是困扰我
3:采用dma方式接收数据+定时器超时中断,这样来确定一帧数据完成,需要开关定时器,操作比较复杂,超时时间还不太好设置,stm32f1和f4 没有超时中断还只能采用定时器或者把rxd引脚接到stm32定时器触发引脚上来实现超时,
4:stm32串口dma方式接收数据+空闲中断或者超时中断+fifo 这种分内事来实现不定长的数据和高效的串口数据接收 效率比其他的方式要快,消耗cpu的时间比较少,这样应用可以做数据超时
所以我采用了stm32串口dma方式接收数据+空闲中断或者超时中断+fifo方式来实现
像stm32f103和stm32f407芯片没有时间超时中断 需要定时器来做超时 比较麻烦,nxp的部分芯片串口有超时中断,atmel的部分芯片串口有超时中断,stm32的h7和f7系列才有超时中断
要实现stm32串口dma方式接收数据+空闲中断或者超时中断+fifo方式来实现
第一步 stm32的串口dma配置 ,串口的初始化,还有串口的空闲中断
1,通过stm32的cubemx软件来生成串口+dma配置的初始化
cubemx生成代码都是HAL库的模式,我就以stm32f4的HAL库的方式实现
[code]UART_HandleTypeDef huart1={0}; huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { }
dma的配置可以通过cubemx软件串口+dma方式来生成代码
[code] /* USER CODE BEGIN USART1_MspInit 0 */ /* USER CODE END USART1_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USART1 DMA Init */ /* USART1_RX Init */ hdma_usart1_rx.Instance = DMA2_Stream2; hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4; hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart1_rx.Init.Mode = DMA_CIRCULAR; hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(huart,hdmarx,hdma_usart1_rx); /* USART1 interrupt Init */ HAL_NVIC_SetPriority(USART1_IRQn, 5, 0); HAL_NVIC_EnableIRQ(USART1_IRQn);
2,stm32生成的代码是不支持空闲中断的,需要自己增加空闲中断和空闲中断的处理
[code] __HAL_UART_ENABLE_IT(uart->h, UART_IT_IDLE);//使能空闲中断HAL代码 __HAL_UART_CLEAR_IDLEFLAG(uart->h);//使能空闲中断HAL代码
串口中断的处理
[code]void USART1_IRQHandler(void) { HAL_UART_IRQHandler(&huart1); if((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) ? SET : RESET) == SET) { //串口空闲中断数据处理 } }
第二步 了解环形fifo的buff特点,通过分析环形buff的特点,其实串口的dma接收 dma模式设置循环模式 就是环形buff
环形buff的说明讲网站:https://blog.csdn.net/jiejiemcu/article/details/80563422
转载环形fifo说明出处: STM32进阶之串口环形缓冲区实现
以下内容是引用了原文的内容
转载原文链接:https://blog.csdn.net/jiejiemcu/article/details/80563422
实现的原理:初始化的时候,列队头与列队尾都指向0,当有数据存储的时候,数据存储在‘0’的地址空间,列队尾指向下一个可以存储数据的地方‘1’,再有数据来的时候,存储数据到地址‘1’,然后队列尾指向下一个地址‘2’。当数据要进行处理的时候,肯定是先处理‘0’空间的数据,也就是列队头的数据,处理完了数据,‘0’地址空间的数据进行释放掉,列队头指向下一个可以处理数据的地址‘1’。从而实现整个环形缓冲区的数据读写。
看图,队列头就是指向已经存储的数据,并且这个数据是待处理的。下一个CPU处理的数据就是1;而队列尾则指向可以进行写数据的地址。当1处理了,就会把1释放掉。并且把队列头指向2。当写入了一个数据6,那么队列尾的指针就会指向下一个可以写的地址。
如果你懂了环形队列,那就跟着一步步用代码实现吧:
是不是跟dma的circular的模式跟这个环形队列非常相似呢
第3步就开始写串口dma+空闲中断或超时中断+fifo的代码啦
重要的代码 串口dma的fifo的数据长度的处理
[code]//假设dma的接收缓存大小为512 环形buff的位置为 500 //接收到数据长度为48 这个时候的环形buff的位置为548 // 548-500=48 但是dma环形buff的特性, //dma实际的长度为36 这个时候如何计算长度 // 这个算法如512-500 12 548-512 36 rev_dma_lens = DMA接收的数据长度 //buff_index-----串口接收和app的buff的序列号 //环形数组 if (puart->ndtr_last != rev_dma_lens) //上次与这次不同,表示有新数据 { start_addr = start_addr + len;//环形数据地址偏移量 // if (start_addr >= 512) { puart->start_addr = start_addr - 512; } // if (rev_dma_lens > ndtr_last) { len = rev_dma_lens - ndtr_last; //接收数据长度=上次长度-这次长度 } else { len = 512 - ndtr_last + rev_dma_lens; //环形数据到头后,总数-这次剩余+上次剩余 } // ndtr_last = rev_dma_lens; }
相关文章推荐
- STM32利用串口空闲中断在串口DMA配置下接收变长数据
- STM32使用DMA加串口空闲中断接收数据
- 【STM32学习】 串口接收数据 使用串口空闲中断与DMA
- STM32 DMA加串口空闲中断接收数据
- STM32CubeMX 串口空闲中断加DMA 实现不定长度收发数据
- STM32—无需中断来实现使用DMA接收串口数据(原创)
- STM32—无需中断来实现使用DMA接收串口数据
- STM32 利用DMA和串口空闲中断实现不定长收发数据包
- 【STM32】DMA+串口空闲中断接收定长数据(解决接收错位问题)
- Stm32——串口空闲中断+DMA接收不定长数据
- STM32—无需中断来实现使用DMA接收串口数据(原创)
- STM32空闲中断+DMA解决接收不定长数据问题
- STM32 HAL 库, 配置串口DMA接收及空闲中断
- STM32F207运用串口空闲中断+DMA接收不定长数据
- STM32F207运用串口空闲中断+DMA接收不定长数据
- stm32的串口空闲中断接收数据
- stm32 实现串口中断接收浮点型、整型数据
- STM32空闲中断+DMA解决接收不定长数据问题
- STM32 串口 空闲中断接收不定长数据
- 串口1配合DMA接收不定长数据(空闲中断+DMA接收)