文本提供的代码是基于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@分享快乐,共同进步-------------------------------------
网友评论