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

文本提供的代码是基于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生成工程,这里介绍串口部分的设置:

STM32F103串口DMA+空闲中断+多级缓冲实现不定长接收_第1张图片
STM32F103串口DMA+空闲中断+多级缓冲实现不定长接收_第2张图片

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+空闲中断+多级缓冲实现不定长接收)