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

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

作者: xEndLess | 来源:发表于2022-04-22 07:44 被阅读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生成工程,这里介绍串口部分的设置:

image image

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/qbugertx.html