【STM32】基于STM32CubeMX的凭借Queue队列管理的串口空闲中断接收数据(有操作系统)
一、框架概述
该方案采用的是基于FreeRTOS下的凭借Queue队列管理的串口空闲中断来接收数据。
二、STM32CubeMX的配置
1、时钟配置

2、时钟源换为TIM6(systick需要提供给FreeRTOS)

3、串口配置(这里未开启DMA)


4、FreeRTOS的配置(开启后就直接按默认配置,若有自身需求,请自行进行其他的配置)

5、时钟树的配置

6、工程的配置


至此,STM32CubeMX配置完毕。
三、代码编写
freertos.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : freertos.c
* Description : Code for freertos applications
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "BSP_USART.h"
#include "queue.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
//定义串口测试任务句柄
static TaskHandle_t sg_xUART_TestOfTaskHandle;
//定义接收队列句柄
QueueHandle_t g_xQueueHandle_UART_RX;
/* USER CODE END Variables */
osThreadId defaultTaskHandle;
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void const * argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );
/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
*ppxIdleTaskStackBuffer = &xIdleStack[0];
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
/* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */
/**
* @brief FreeRTOS initialization
* @param None
* @retval None
*/
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
//动态创建接收队列
g_xQueueHandle_UART_RX = xQueueCreate(3, sizeof(uint8_t));
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* definition and creation of defaultTask */
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 256);
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
//动态创建串口测试任务
xTaskCreate(UART_Test, "UART_Test_TASK", 128, NULL, osPriorityNormal+1, &sg_xUART_TestOfTaskHandle);
/* USER CODE END RTOS_THREADS */
}
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
for(;;)
{
osDelay(1);
}
/* USER CODE END StartDefaultTask */
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
/* USER CODE END Application */
BSP_USART.h
#ifndef __BSP_USART_H
#define __BSP_USART_H
#include "stm32f1xx_hal.h"
#define Buffer_Total_Length 10
void UART_Test(void *pvParamter);
void UsartPrintf(UART_HandleTypeDef *USARTx, char *fmt,...);
#endif
BSP_USART.c
#include "BSP_USART.h"
//STM32
#include "usart.h"
//FreeRTOS
#include "freertos.h"
#include "queue.h"
#include "semphr.h"
//C
#include "stdio.h"
#include <stdarg.h>
#include <string.h>
//外部声明
extern QueueHandle_t g_xQueueHandle_UART_RX;
//接受数组
uint8_t RX_Buffer[Buffer_Total_Length] = {0};
//用户测试数组
uint8_t USER_RX_Buffer[Buffer_Total_Length] = {0};
//初始化
void UART_Init(void )
{
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_UART_Receive_IT(&huart1, RX_Buffer, sizeof(RX_Buffer));
}
//测试任务
void UART_Test(void *pvParamter)
{
UART_Init();
while(1)
{
if(xQueueReceive(g_xQueueHandle_UART_RX, USER_RX_Buffer, portMAX_DELAY) == pdTRUE)
{
//打印并回显接收的数据的第一个字符
UsartPrintf(&huart1, "%d", USER_RX_Buffer[0]);
}
}
}
//自定义打印函数
void UsartPrintf(UART_HandleTypeDef *USARTx, char *fmt, ...)
{
unsigned char UsartPrintfBuf[256];
va_list ap;
unsigned int len;
va_start(ap, fmt);
len = vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap);
va_end(ap);
if (len > 0) {
HAL_UART_Transmit(USARTx, UsartPrintfBuf, len, 1000);
while(HAL_UART_GetState(USARTx) == HAL_UART_STATE_BUSY_TX);
}
}
// 在BSP_USART.c中添加中断回调实现
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart1) {
// 将接收到的数据发送到队列
if (g_xQueueHandle_UART_RX != NULL)
{
xQueueSendFromISR(g_xQueueHandle_UART_RX, RX_Buffer, NULL);
}
// 继续监听下一个字节
HAL_UART_Receive_IT(&huart1, RX_Buffer, sizeof(RX_Buffer));
}
}
四、实验效果

注意事项:
1、使用的硬件平台是野火指南者开发板(STM32F103VET6)。
2、本来的设想是队列加二值信号量来构建一下对于接收数据的管理,但是,在过程中发现如果在这个中断回调里使用释放二值信号量(任务while中等待获取二值信号量),会导致无法进入中断完成回调函数里,目前原因未知,所以放弃使用二值信号量。
3、在这个过程中,注意到了对于void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);这个中断接收完成回调函数,进入这个回调函数之前就对接收缓存非空、空闲中断标志位置1并且清零了,所以不用在这个中断接收完成回调函数里使用if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE/IDLE)==SET){};这个再去判断一次了。

浙公网安备 33010602011771号