美文网首页
STM32F103串口DMA+空闲中断+多级缓冲实现不定长接收

STM32F103串口DMA+空闲中断+多级缓冲实现不定长接收

作者: xEndLess | 来源:发表于2018-10-18 22:08 被阅读0次

    文本提供的代码是基于STM32CubeMX生成的HAL库的。

    STM32串口接收大体分为3种方式:

    1、阻塞接收---HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);

    2、中断接收---HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

    3、DMA接收---HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

    本文只谈论DMA接收方式,同时增加空闲中断和多级缓冲。单纯的DMA接收适合固定长度的数据接收,局限性太大,很难适用实际项目需要。增加空闲中断,可以做到不定长接收。多级缓冲在一定程度上可以缓解裸机代码实时性差的问题。

    具体步骤如下:

    1.STM32CubeMX生成工程,这里介绍串口部分的设置:

    2.新建usart_ex.c文件,主要关注两个函数:void HW_UART_Modem_IRQHandler(UART_HandleTypeDef *huart)和void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)。

    /**

      ******************************************************************************

      * @file    : usart_ex.c

      * @author  : xEndLess

      * @version : V1.0.0

      * @date    : 2018-4-3

      * @brief  : 本文件是对usart.c的扩展,避免每次用Cube建立工程时,需要对usart.c做出修改.

      *            串口发送函数,回调函数,DMA接收完成函数,都放在这里。

      *

      ******************************************************************************

      */

    /* Includes ------------------------------------------------------------------*/

    #include "bsp.h"

    #include "stdarg.h"

    /* Private typedef -----------------------------------------------------------*/

    /* Private define ------------------------------------------------------------*/

    /* Private macro -------------------------------------------------------------*/

    /* Private variables ---------------------------------------------------------*/

    Usart1_BufTypeDef Usart1Buf;

    Usart2_BufTypeDef Usart2Buf;

    Usart3_BufTypeDef Usart3Buf;

    Usart4_BufTypeDef Usart4Buf;

    Usart5_BufTypeDef Usart5Buf;

    /* 以下4个变量是在uasrt.c中定义的 ,Cube自动生成 */

    extern DMA_HandleTypeDef hdma_uart4_rx;

    extern DMA_HandleTypeDef hdma_usart1_rx;

    extern DMA_HandleTypeDef hdma_usart2_rx;

    extern DMA_HandleTypeDef hdma_usart3_rx;

    /* Private function prototypes -----------------------------------------------*/

    /* Private functions ---------------------------------------------------------*/

    /****************** UART CallBack *******************/

    /**

      * @brief  Rx Transfer completed callback

      * @param  UartHandle: UART handle

      * @note  This example shows a simple way to report end of DMA Rx transfer, and

      *        you can add your own implementation.

      * @retval None

      */

    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

    {

      if (huart->Instance == USART1)

      {

        Usart1Buf.RxEndFlag[Usart1Buf.RxDimension] = SET;

        Usart1Buf.RxEndIndex[Usart1Buf.RxDimension] = USART1_BUF_LENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);

        Usart1Buf.RxDimension++;

        Usart1Buf.RxDimension %= USART1_BUF_DIMENSION;

        HAL_UART_DMAStop(huart);

        HAL_UART_Receive_DMA(huart, Usart1Buf.RxBuffer[Usart1Buf.RxDimension], USART1_BUF_LENGTH);

      }

      else if (huart->Instance == USART2)

      {

        Usart2Buf.RxEndFlag[Usart2Buf.RxDimension] = SET;

        Usart2Buf.RxEndIndex[Usart2Buf.RxDimension] = USART2_BUF_LENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);

        Usart2Buf.RxDimension++;

        Usart2Buf.RxDimension %= USART2_BUF_DIMENSION;

        HAL_UART_DMAStop(huart);

        HAL_UART_Receive_DMA(huart, Usart2Buf.RxBuffer[Usart2Buf.RxDimension], USART2_BUF_LENGTH);

      }

      else if (huart->Instance == USART3)

      {

        Usart3Buf.RxEndFlag[Usart3Buf.RxDimension] = SET;

        Usart3Buf.RxEndIndex[Usart3Buf.RxDimension] = USART3_BUF_LENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);

        Usart3Buf.RxDimension++;

        Usart3Buf.RxDimension %= USART3_BUF_DIMENSION;

        HAL_UART_DMAStop(huart);

        HAL_UART_Receive_DMA(huart, Usart3Buf.RxBuffer[Usart3Buf.RxDimension], USART3_BUF_LENGTH);

      }

      else if (huart->Instance == UART4)

      {

        Usart4Buf.RxEndIndex[Usart4Buf.RxDimension] = USART4_BUF_LENGTH - __HAL_DMA_GET_COUNTER(&hdma_uart4_rx);

        Usart4Buf.RxEndFlag[Usart4Buf.RxDimension] = SET;

        Usart4Buf.RxDimension++;

        Usart4Buf.RxDimension %= USART4_BUF_DIMENSION;

        HAL_UART_DMAStop(huart);

        HAL_UART_Receive_DMA(huart, Usart4Buf.RxBuffer[Usart4Buf.RxDimension], USART4_BUF_LENGTH);

      }

      else if (huart->Instance == UART5)

      {

        Usart5Buf.RxEndIndex[Usart5Buf.RxDimension] = USART5_BUF_LENGTH;

        Usart5Buf.RxEndFlag[Usart5Buf.RxDimension] = SET;

        Usart5Buf.RxDimension++;

        Usart5Buf.RxDimension %= USART5_BUF_DIMENSION;

        HAL_UART_Receive_IT(huart, Usart5Buf.RxBuffer[Usart5Buf.RxDimension], USART5_BUF_LENGTH);

      }

    }

    /**

      * @brief DMA IDLE接收串口数据,该函数刚到串口中断中

      * @param handle to the UART

      * @retval void

      **/

    void HW_UART_Modem_IRQHandler(UART_HandleTypeDef *huart)

    {

      uint32_t tmp_flag = 0, tmp_it_source = 0;

      if (huart->Instance == USART1)

      {

        tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE);

        tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_IDLE); 

        /* UART parity error interrupt occurred ------------------------------------*/

        if ((tmp_flag != RESET) && (tmp_it_source != RESET))

        {

          Usart1Buf.RxEndIndex[Usart1Buf.RxDimension] = USART1_BUF_LENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);

          /* 在485通信时,有时会出现未接收到数据,空闲中断响应的情况 */

          if (Usart1Buf.RxEndIndex[Usart1Buf.RxDimension] != 0)

          {

            Usart1Buf.RxEndFlag[Usart1Buf.RxDimension] = SET;

            Usart1Buf.RxDimension++;

            Usart1Buf.RxDimension %= USART1_BUF_DIMENSION;

            HAL_UART_DMAStop(huart);

            HAL_UART_Receive_DMA(huart, Usart1Buf.RxBuffer[Usart1Buf.RxDimension], USART1_BUF_LENGTH);

          }

        }

        __HAL_UART_CLEAR_IDLEFLAG(huart);

      }

      else if (huart->Instance == USART2)

      {

        tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE);

        tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_IDLE); 

        /* UART parity error interrupt occurred ------------------------------------*/

        if ((tmp_flag != RESET) && (tmp_it_source != RESET))

        {

          Usart2Buf.RxEndIndex[Usart2Buf.RxDimension] = USART2_BUF_LENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);

          /* 在485通信时,有时会出现未接收到数据,空闲中断响应的情况 */

          if (Usart2Buf.RxEndIndex[Usart2Buf.RxDimension] != 0)

          {

            Usart2Buf.RxEndFlag[Usart2Buf.RxDimension] = SET;

            Usart2Buf.RxDimension++;

            Usart2Buf.RxDimension %= USART2_BUF_DIMENSION;

            HAL_UART_DMAStop(huart);

            HAL_UART_Receive_DMA(huart, Usart2Buf.RxBuffer[Usart2Buf.RxDimension], USART2_BUF_LENGTH);

          }

        }

        __HAL_UART_CLEAR_IDLEFLAG(huart);

      }

      else if (huart->Instance == USART3)

      {

        tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE);

        tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_IDLE); 

        /* UART parity error interrupt occurred ------------------------------------*/

        if ((tmp_flag != RESET) && (tmp_it_source != RESET))

        {

          Usart3Buf.RxEndIndex[Usart3Buf.RxDimension] = USART3_BUF_LENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);

          /* 在485通信时,有时会出现未接收到数据,空闲中断响应的情况 */

          if (Usart3Buf.RxEndIndex[Usart3Buf.RxDimension] != 0)

          {

            Usart3Buf.RxEndFlag[Usart3Buf.RxDimension] = SET;

            Usart3Buf.RxDimension++;

            Usart3Buf.RxDimension %= USART3_BUF_DIMENSION;

            HAL_UART_DMAStop(huart);

            HAL_UART_Receive_DMA(huart, Usart3Buf.RxBuffer[Usart3Buf.RxDimension], USART3_BUF_LENGTH);

          }

        }

        __HAL_UART_CLEAR_IDLEFLAG(huart);

      }

      else if (huart->Instance == UART4)

      {

        tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE);

        tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_IDLE); 

        /* UART parity error interrupt occurred ------------------------------------*/

        if ((tmp_flag != RESET) && (tmp_it_source != RESET))

        {

          Usart4Buf.RxEndIndex[Usart4Buf.RxDimension] = USART4_BUF_LENGTH - __HAL_DMA_GET_COUNTER(&hdma_uart4_rx);

          /* 在485通信时,有时会出现未接收到数据,空闲中断响应的情况 */

          if (Usart4Buf.RxEndIndex[Usart4Buf.RxDimension] != 0)

          {

            Usart4Buf.RxEndFlag[Usart4Buf.RxDimension] = SET;

            Usart4Buf.RxDimension++;

            Usart4Buf.RxDimension %= USART4_BUF_DIMENSION;

            HAL_UART_DMAStop(huart);

            HAL_UART_Receive_DMA(huart, Usart4Buf.RxBuffer[Usart4Buf.RxDimension], USART4_BUF_LENGTH);

          }

        }

        __HAL_UART_CLEAR_IDLEFLAG(huart);

      }

      else if (huart->Instance == UART5)

      {

        tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE);

        tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_IDLE); 

        /* UART parity error interrupt occurred ------------------------------------*/

        if ((tmp_flag != RESET) && (tmp_it_source != RESET))

        {

          Usart5Buf.RxEndIndex[Usart5Buf.RxDimension] = USART5_BUF_LENGTH - huart5.RxXferCount;

          /* 在485通信时,有时会出现未接收到数据,空闲中断响应的情况 */

          if (Usart5Buf.RxEndIndex[Usart5Buf.RxDimension] != 0)

          {

            Usart5Buf.RxEndFlag[Usart5Buf.RxDimension] = SET;

            Usart5Buf.RxDimension++;

            Usart5Buf.RxDimension %= USART5_BUF_DIMENSION;

            HAL_UART_AbortReceive_IT(huart); /* 暂停接收中断 */

            HAL_UART_Receive_IT(huart, Usart5Buf.RxBuffer[Usart5Buf.RxDimension], USART5_BUF_LENGTH);

          }

        }

        __HAL_UART_CLEAR_IDLEFLAG(huart);

      }

    }

    /************************ (C) COPYRIGHT 2016-2022,xEndLess *****END OF FILE****/

    3.增加usart_ex.h文件,并增加如下代码:

    /**

      ******************************************************************************

      * @file    : usart_ex.c

      * @author  : xEndLess

      * @version : V1.0.0

      * @date    : 2018-4-3

      * @brief  : 本文件是对usart.c的扩展,避免每次用Cube建立工程时,需要对usart.c做出修改.

      *            串口发送函数,回调函数,DMA接收完成函数,都放在这里。

      *

      ******************************************************************************

      */

    #ifndef __USART_EX_H

    #define __USART_EX_H

    /* Includes ------------------------------------------------------------------*/

    #include "usart.h"

    #include "stm32f1xx_hal.h"

    /**

    * @brief Usart

    */

    #define TX_TIMEOUT          ((uint32_t)3000)

    #define RX_TIMEOUT          ((uint32_t)3000)

    #define USART1_BUF_LENGTH      255

    #define USART1_BUF_DIMENSION  2

    #define USART2_BUF_LENGTH      255

    #define USART2_BUF_DIMENSION  10

    #define USART3_BUF_LENGTH      255

    #define USART3_BUF_DIMENSION  5

    #define USART4_BUF_LENGTH      255

    #define USART4_BUF_DIMENSION  5

    #define USART5_BUF_LENGTH      255

    #define USART5_BUF_DIMENSION  10

    /**

    * @brief Usart1_BufTypeDef

    */

    typedef struct

    {

    uint8_t RxBuffer[USART1_BUF_DIMENSION][USART1_BUF_LENGTH];    /* 接收缓冲区 */

    uint16_t RxEndIndex[USART1_BUF_DIMENSION];                    /* 尾索引值 */

    FlagStatus RxEndFlag[USART1_BUF_DIMENSION];                  /* 接收结束标识 */

    uint8_t RxDimension;                                          /* 接收维度 */

    }Usart1_BufTypeDef;

    /**

    * @brief Usart2_BufTypeDef

    */

    typedef struct

    {

      uint8_t RxBuffer[USART2_BUF_DIMENSION][USART2_BUF_LENGTH];    /* 接收缓冲区 */

      uint16_t RxEndIndex[USART2_BUF_DIMENSION];                    /* 尾索引值 */

      FlagStatus RxEndFlag[USART2_BUF_DIMENSION];                  /* 接收结束标识 */

      uint8_t RxDimension;                                          /* 接收维度 */

    }Usart2_BufTypeDef;

    /**

    * @brief Usart3_BufTypeDef

    */

    typedef struct

    {

      uint8_t RxBuffer[USART3_BUF_DIMENSION][USART3_BUF_LENGTH];    /* 接收缓冲区 */

      uint16_t RxEndIndex[USART3_BUF_DIMENSION];                    /* 尾索引值 */

      FlagStatus RxEndFlag[USART3_BUF_DIMENSION];                  /* 接收结束标识 */

      uint8_t RxDimension;                                          /* 接收维度 */

    }Usart3_BufTypeDef;

    /**

    * @brief Usart4_BufTypeDef

    */

    typedef struct

    {

      uint8_t RxBuffer[USART4_BUF_DIMENSION][USART4_BUF_LENGTH];    /* 接收缓冲区 */

      uint16_t RxEndIndex[USART4_BUF_DIMENSION];                    /* 尾索引值 */

      FlagStatus RxEndFlag[USART4_BUF_DIMENSION];                  /* 接收结束标识 */

      uint8_t RxDimension;                                          /* 接收维度 */

    }Usart4_BufTypeDef;

    /**

    * @brief Usart5_BufTypeDef

    */

    typedef struct

    {

      uint8_t RxBuffer[USART5_BUF_DIMENSION][USART5_BUF_LENGTH];    /* 接收缓冲区 */

      uint16_t RxEndIndex[USART5_BUF_DIMENSION];                    /* 尾索引值 */

      FlagStatus RxEndFlag[USART5_BUF_DIMENSION];                  /* 接收结束标识 */

      uint8_t RxDimension;                                          /* 接收维度 */

    }Usart5_BufTypeDef;

    extern Usart1_BufTypeDef Usart1Buf;

    extern Usart2_BufTypeDef Usart2Buf;

    extern Usart3_BufTypeDef Usart3Buf;

    extern Usart4_BufTypeDef Usart4Buf;

    extern Usart5_BufTypeDef Usart5Buf;

    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

    void HW_UART_Modem_IRQHandler(UART_HandleTypeDef *huart);

    #endif /* __USART_EX_H */

    /************************ (C) COPYRIGHT 2016-2022,xEndLess *****END OF FILE****/

    4.在stm32f1xx_it.c(Cube生成工程时自动增加)文件中增加如下代码:

    #include "usart_ex.h"

    /**

    * @brief This function handles USART1 global interrupt.

    */

    void USART1_IRQHandler(void)

    {

      /* USER CODE BEGIN USART1_IRQn 0 */

      /* USER CODE END USART1_IRQn 0 */

      HAL_UART_IRQHandler(&huart1);

      /* USER CODE BEGIN USART1_IRQn 1 */

      HW_UART_Modem_IRQHandler(&huart1);

      /* USER CODE END USART1_IRQn 1 */

    }

    5.在main.c中开启空闲中断和DMA接收:

    __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);

    HAL_UART_Receive_DMA(&huart1, Usart1Buf.RxBuffer[Usart1Buf.RxDimension],

    USART1_BUF_LENGTH);

    --------------------------到此就可以串口DMA接收就准备完成-------------------

    当串口接收到数据后,数据会保存在Usart1Buf.RxBuffer数组中。用户只需要关注结构体Usart1Buf。以下代码是Usart1Buf的使用方法之一:

     for(loop = 0; loop < countof(Usart1Buf.RxBuffer); loop++)

    {

      if(Usart1Buf.RxEndFlag[loop] == SET)

    {

       /*

        此处解析接收到的数据

     */

       Usart1Buf.RxEndFlag[loop] = RESET;

       Usart1Buf.RxEndIndex[loop] = 0;

      }

     }

    到此,串口DMA+空闲中断+多级缓冲讲解完成。写的比较乱。谅解。

    ------------------------------------------- ----@xEndLess@分享快乐,共同进步-------------------------------------

    相关文章

      网友评论

          本文标题:STM32F103串口DMA+空闲中断+多级缓冲实现不定长接收

          本文链接:https://www.haomeiwen.com/subject/ihcjzftx.html