【STM32】基于STM32CubeMX的Semphr+DMA的串口空闲中断接收变长数据(有操作系统)

一、框架概述

  在嵌入式系统的串口通信设计中,针对高吞吐量、低延迟的数据传输需求,可使用DMA辅助处理串口的数据,能够大幅缓解单片机的运行压力,提高串口数据的处理能力,实现可靠通信。我的方案采用的是基于FreeRTOS下的Semphr+DMA的串口空闲中断来接收变长数据。

 二、STM32CubeMX的配置

1、时钟配置

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

3、串口配置

4、中间件FreeRTOS的配置(由于只演示本方案,这里按照默认的配置即可)

5、时钟树的配置

6、工程的设置

  至此,CubeMX配置完毕。

三、代码编写

  首先,先编译运行一下,如果出现以下情况,请将CubeMX的HAL库固件包版本换成1.8.5版本:

以下代码:

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 "semphr.h"
#include "BSP_USART.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 */
//外部声明
extern UART_HandleTypeDef huart1;

//定义UART_TEST任务句柄
TaskHandle_t g_xUART_TestOfTaskHandle;
static BaseType_t sg_xUART_TestOfTask_ret;

//定义串口接收触发信号量句柄
SemaphoreHandle_t g_sUartRx_Semaphore;

/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartDefaultTask(void *argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/**
  * @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, ... */
	//创建串口接收触发信号量句柄的对应的二值信号量
	g_sUartRx_Semaphore = xSemaphoreCreateBinary();
	
  /* 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, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  //动态创建串口任务
  sg_xUART_TestOfTask_ret = xTaskCreate(UART_Test, "UART_Test_TASK", 128, NULL, osPriorityNormal+1, &g_xUART_TestOfTaskHandle);
  if(sg_xUART_TestOfTask_ret != pdPASS)
  {
	  HAL_UART_Transmit_DMA(&huart1, (const uint8_t *)"error", 5);
  }
  
  /* USER CODE END RTOS_THREADS */

  /* USER CODE BEGIN RTOS_EVENTS */
  /* add events, ... */
  /* USER CODE END RTOS_EVENTS */

}

/* 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 *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 128

//函数声明
void UART1_Init(void);
void UART_Test(void *pvParameters);
void BSP_UART1_IRQ(UART_HandleTypeDef *huart);
#endif

BSP_USART.c

#include "BSP_USART.h"

//STM32
#include "usart.h"
#include "dma.h"

//FreeRTOS
#include "FreeRTOS.h"
#include "semphr.h"

//C库
#include "string.h"

//外部声明
extern DMA_HandleTypeDef hdma_usart1_rx;
extern SemaphoreHandle_t g_sUartRx_Semaphore;

//定义发送数组 接收数组
uint8_t TX_Buffer[Buffer_Total_Length] = {0};
uint8_t RX_Buffer[Buffer_Total_Length] = {0};

//用户测试数组
uint8_t User_Test_Buffer[Buffer_Total_Length] = {0};

//定义存放用于计算出实际接收长度
uint16_t Buffer_real_Length = 0;

void UART1_Init(void)
{
	//使能空闲中断和接收DMA
    HAL_UARTEx_ReceiveToIdle_DMA(&huart1, RX_Buffer, Buffer_Total_Length);
}

//串口任务
void UART_Test(void *pvParameters)
{
	//初始化
	UART1_Init();
	
	while(1)
	{
		//获取二值信号量
		if(xSemaphoreTake(g_sUartRx_Semaphore, portMAX_DELAY) == pdTRUE)
		{
			//回显测试
			HAL_UART_Transmit(&huart1, User_Test_Buffer, Buffer_real_Length, HAL_MAX_DELAY);
			
		}
		
	}
}

void BSP_UART1_IRQ(UART_HandleTypeDef *huart)
{
    if(huart == &huart1){
		
        if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) == SET){
			
			//清除标志位
			__HAL_UART_CLEAR_IDLEFLAG(&huart1);
			
			//赋值xHighPriorityTaskWoken
			BaseType_t xHighPriorityTaskWoken = pdFALSE;
			
			//释放二值信号量
			xSemaphoreGiveFromISR(g_sUartRx_Semaphore, &xHighPriorityTaskWoken);
			
			//停止DMA处理其中的数据
            HAL_UART_DMAStop(&huart1);
			
			//计算实际接收长度
            Buffer_real_Length = Buffer_Total_Length  - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
			
			//将数据copy出来用于后面的回显测试
            memcpy(User_Test_Buffer, RX_Buffer, Buffer_real_Length);						
			
			//再次启动
            HAL_UARTEx_ReceiveToIdle_DMA(&huart1, RX_Buffer, Buffer_Total_Length);
			
			//取消上下文切换
			portYIELD_FROM_ISR(xHighPriorityTaskWoken);
        }
    }
}

stm32f1xx_it.c

/*只需要改动两处,其他代码略*/

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
//加入头文件
#include "BSP_USART.h"

/* USER CODE END Includes */


/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
    //使用自己的中断服务函数
	BSP_UART1_IRQ(&huart1);

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

四、实验效果

注意事项:

1、这只是作者的一种实现方案,但是还存在一些问题仍待完善。

2、自定义的中断服务函数那里,为什么不用HAL库的中断回调函数

void HAL_UART_IdleCallback(UART_HandleTypeDef *huart);

呢?因为那么做了后,作者发现它会无法自动进入该中断回调函数,所以就自定义成一个中断服务函数并放入对应中断位置处去运行,目前原因未知,有待解决。

3、硬件平台是野火的指南者。

posted @ 2025-05-25 15:57  Molesidy  阅读(220)  评论(0)    收藏  举报  来源
//雪花飘落效果