我是靠谱客的博主 愉快大树,最近开发中收集的这篇文章主要介绍stm32串口dma加空闲中断 实现fifo接收数据 串口高效收发思路我做这个串口数据接收 dma+空闲中断 加fifo  实现串口的高效收发  ,主要是串口接收的数据长度不定长,时间超时也不好做,还要串口收发的效率要高,采用串口数据的接收 dma+空闲中断+fifo的方式  速度快和效率高,占用cpu的时间短对比了其他几种方式1:采用串口中断的话,每接收1byte就得中断一次。这样太消耗CPU资源! 频繁进中断,占用中断,特别是对时间和时序要求比较严格的时候 串口频繁进入中断导致其他中断时序有,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

我做这个串口数据接收 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库的方式实现

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方式来生成代码

  /* 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生成的代码是不支持空闲中断的,需要自己增加空闲中断和空闲中断的处理

    __HAL_UART_ENABLE_IT(uart->h, UART_IT_IDLE);//使能空闲中断HAL代码
    __HAL_UART_CLEAR_IDLEFLAG(uart->h);//使能空闲中断HAL代码

串口中断的处理

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的数据长度的处理

//假设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;

}

但是这种方式会2个问题:

1,串口一直有数据接收 不产生空闲中断 这个时候会有问题  解决的方法采用超时方法来解决  或者采用一个一个的数据处理方式采用每来一个数据产生一个中断方式

2, 一个数据包超过dma的最大buff长度  已经越界 导致了部分数据被覆盖啦   解决方法就是把DMA的接收buff定义更大一点 最好定义为2倍的一个最大数据包 考虑资源也可以考虑1.5或1倍的一个最大数据包 或者采用一个一个的数据处理方式  采用每来一个数据产生一个中断方式

最后

以上就是愉快大树为你收集整理的stm32串口dma加空闲中断 实现fifo接收数据 串口高效收发思路我做这个串口数据接收 dma+空闲中断 加fifo  实现串口的高效收发  ,主要是串口接收的数据长度不定长,时间超时也不好做,还要串口收发的效率要高,采用串口数据的接收 dma+空闲中断+fifo的方式  速度快和效率高,占用cpu的时间短对比了其他几种方式1:采用串口中断的话,每接收1byte就得中断一次。这样太消耗CPU资源! 频繁进中断,占用中断,特别是对时间和时序要求比较严格的时候 串口频繁进入中断导致其他中断时序有的全部内容,希望文章能够帮你解决stm32串口dma加空闲中断 实现fifo接收数据 串口高效收发思路我做这个串口数据接收 dma+空闲中断 加fifo  实现串口的高效收发  ,主要是串口接收的数据长度不定长,时间超时也不好做,还要串口收发的效率要高,采用串口数据的接收 dma+空闲中断+fifo的方式  速度快和效率高,占用cpu的时间短对比了其他几种方式1:采用串口中断的话,每接收1byte就得中断一次。这样太消耗CPU资源! 频繁进中断,占用中断,特别是对时间和时序要求比较严格的时候 串口频繁进入中断导致其他中断时序有所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(58)

评论列表共有 0 条评论

立即
投稿
返回
顶部