【蓝桥杯嵌入式】串口空闲中断+DMA接收不定长数据
MCU:STM32G431RBT6
一、实验效果
将USART1接收到的数据原封不动的重新发送回去
二、STM32CUBEMX配置
USART1的模式为异步通信、115200波特率、数据长度8位、无校验位、停止位1位
注意:需要手动将USART1的引脚修改成PA9和PA10
因为开发板上串口使用的是这个引脚进行通信,而开启USART1的默认引脚不是PA9和PA10,需要手动修改一下。
DMA Settings
NVIC Settings
这里两个中断优先级我都给了1,根据不同情况修改中断优先级
生成工程文档
三、keil代码
首先确保魔术棒中的LIB这个选项勾选上,不然串口发送数据会不正常
添加头文件,因为要使用memset函数
#include "string.h"
定义串口接收数据数组
#define BUFF_SIZE 128 //接收缓存大小
uint8_t rx_buffer[BUFF_SIZE]; // 创建接收缓存,大小为BUFF_SIZE
在usart.h外部声明 hdma_usart1_rx,在main函数当中要使用
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN Private defines */
extern DMA_HandleTypeDef hdma_usart1_rx;
/* USER CODE END Private defines */
void MX_USART1_UART_Init(void);
在main函数初始化添加这两个函数,不然串口首次无法进入中断
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUFF_SIZE); //启动串口DMA搬运
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); //手动关闭DMA_IT_HT中断
重定向 HAL_UARTEx_RxEventCallback回调函数,该函数将串口接收到的数据原封不动的发送回去,若接收到的数据长度过长,会导致>Size后的数据丢失。在这里设置的是BUFF_SIZE=128。
/* 串口空闲中断回调函数 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart->Instance == USART1)
{
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE); // 接收完毕后重启
HAL_UART_Transmit(&huart1, rx_buffer, Size, 0xffff); // 将接收到的数据再发出
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); // 手动关闭DMA_IT_HT中断
memset(rx_buffer, 0, BUFF_SIZE); // 清除接收缓存
}
}
重定向 HAL_UART_ErrorCallback串口错误回调函数,若发生错误,重新使能DMA等。
若是一开始电脑与开发板通信的波特率错误会导致开发板的串口空闲中断不再开启,这个回调函数里面的内容,让串口错误之后依旧可以恢复到正常的波特率进行通信。
/* 串口错误回调函数 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef * huart)
{
if(huart->Instance == USART1)
{
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE); // 接收发生错误后重启
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); // 手动关闭DMA_IT_HT中断
memset(rx_buffer, 0, BUFF_SIZE); // 清除接收缓存
}
}
main函数完整代码
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "string.h"
void SystemClock_Config(void);
#define BUFF_SIZE 128 //接收缓存大小
uint8_t rx_buffer[BUFF_SIZE]; // 创建接收缓存,大小为BUFF_SIZE
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUFF_SIZE); //启动串口DMA搬运
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); //手动关闭DMA_IT_HT中断
while (1)
{
}
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV3;
RCC_OscInitStruct.PLL.PLLN = 20;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* 串口空闲中断回调函数 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart->Instance == USART1)
{
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE); // 接收完毕后重启
HAL_UART_Transmit(&huart1, rx_buffer, Size, 0xffff); // 将接收到的数据再发出
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); // 手动关闭DMA_IT_HT中断
memset(rx_buffer, 0, BUFF_SIZE); // 清除接收缓存
}
}
/* 串口错误回调函数 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef * huart)
{
if(huart->Instance == USART1)
{
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE); // 接收发生错误后重启
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); // 手动关闭DMA_IT_HT中断
memset(rx_buffer, 0, BUFF_SIZE); // 清除接收缓存
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
四、参考文章
好文
【STM32 HAL库实战】串口DMA + 空闲中断 实现不定长数据接收_hal dma 空闲中断-CSDN博客
五、原理
参考文章写的挺详细,不过我实践测试起来还是有点小问题,之后补充。