【学习笔记】基于FreeRTOS中的串口不定长接收(使用队列进行数据传输)

本文详细描述了一位开发者如何在STM32平台上使用Freertos框架,通过串口操作实现单字节和数据包的模式转换,包括接收标志位管理、中断回调函数和数据队列的使用。
摘要由CSDN通过智能技术生成

2024.5.2日五一假期埋头苦战串口收发数据

以下为心得备忘:

首先是仿照江科大标准库移植的串口基本收发函数,进行了一些改写,能够在单字节以及数据包之间进行模式转换:

uint8_t Receive_Mode = 0;//接收模式:单字节或数据包
uint8_t Receive_State;//状态机变量
uint8_t Receive_Byte[1],Receive_ITFlag;//接收单字节数据;接收标志位//!!!这里单字节接收缓冲一定要用数组形式!!!
uint8_t Receive_PocketData[Receive_Pocket_Max_Len] = {0};//接收数据包数组
uint16_t Receive_Pocket_Index = 0;//接收指向

extern QueueHandle_t xQueueHandle_Usart3;//队列句柄

void Serial_ChangeMode(Serial_Mode Mode)
{
	Receive_Mode = (uint8_t)Mode;
}

void Serial_SendByte(uint8_t Byte)
{
	//这里非阻塞发送暂时不能用,否则数据会传输错误,原因暂不明
	//HAL_UART_Transmit_IT(&huart3,&Byte,1);
	HAL_UART_Transmit(&huart3,&Byte,1,HAL_MAX_DELAY);
}

void Serial_SendString(uint8_t *String)
{
	HAL_UART_Transmit(&huart3,String,strlen((const char*)String),HAL_MAX_DELAY);
}

void Serial_SendArray(uint8_t* ArrayData,uint16_t Length)
{
	HAL_UART_Transmit(&huart3,ArrayData,Length,HAL_MAX_DELAY);
}

void Serical_SendPocket(uint8_t* PocketData)
{
	Serial_SendByte(0xFD);
	Serial_SendArray(PocketData,Receive_Pocket_Index);
	Serial_SendByte(0xFE);
	Serial_SendByte(0xFF);
}
//接收单字节和数据包标志位
uint8_t Serial_GetReceiveFlag(void)
{
	if(Receive_ITFlag == 1)
	{
		Receive_ITFlag = 0;
		return 1;
	}
	return 0;
}

//单字节数据
uint8_t Serial_ReceiveByte(void)
{
	return Receive_Byte[0];
}
//数据包数据
void Serial_ReceivePocket(uint8_t *PocketArray)
{
	uint8_t i = 0;
	Receive_GetDataState = 0;
	for (i = 0; Receive_PocketData[i] != '\0'; i ++)
	{
		PocketArray[i] = Receive_PocketData[i];
	}
}
//数据包长度
uint16_t Serial_GetPocketLength(void)
{
	return Receive_Pocket_Index;
}

下面是中断回调函数:

        注意中断回调函数中一定要使用中断安全API:xQueueSendToBackFromISR

        否则不能正确接收数据或者程序卡死

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART3)
	{
		switch(Receive_Mode)
		{
		case 0:
			Receive_ITFlag = 1;
			xQueueSendToBackFromISR(xQueueHandle_Usart3,&Receive_Byte,0);
		break;
		case 1:
				if(Receive_State == 0)
				{
					if (Receive_Byte[0] == 0xFD)
					{
						Receive_Pocket_Index = 0;
						Receive_State = 1;
					}
				}
				else if(Receive_State == 1)
				{
					 if(Receive_Byte[0] == 0xFE)
						Receive_State = 2;
					 else
					 {
						Receive_PocketData[Receive_Pocket_Index] = Receive_Byte[0];
						Receive_Pocket_Index++;
					 }
					 if(Receive_Pocket_Index >= Receive_Pocket_Max_Len)
					 {
						Receive_ITFlag = 1;
						Receive_PocketData[Receive_Pocket_Index] = '\0';
						Receive_State = 0;
					 }
					 
				}
				else if(Receive_State == 2)
				{
					if(Receive_Byte[0] == 0xFF)
					{
						Receive_ITFlag = 1;
						Receive_PocketData[Receive_Pocket_Index] = '\0';
						Receive_State = 0;
						xQueueSendToBackFromISR(xQueueHandle_Usart3,&Receive_PocketData,0);
					}
				}
		break;
		}
		HAL_UART_Receive_IT(&huart3,Receive_Byte,1);//使能中断函数,每次使用完就要调用
	}
}

以下是在freertos.c中写的测试代码:

        这里是一些定义声明:

/**********串口相关变量************/
extern uint8_t Receive_Byte[1];
BaseType_t ret;

/**********************************/

/**********任务句柄声明************/
TaskHandle_t  xLedTaskHandle;
/**********************************/

/**********队列句柄声明************/
QueueHandle_t xQueueHandle_Usart3;//串口3队列句柄
/**********************************/

        这里是初始化代码,删除了部分不重要的注释:

                这里的队列单元大小要注意,必须设置为sizeof(uint8_t *),在stm32这种32位MCU中指针的大小通常是4个字节,即32位,所以这里要把队列单元设置为指针的大小,代表存储地址,

如果这里设置的不对,那么接收的数据就会出现丢包,数据错误。

void MX_FREERTOS_Init(void) 
{
	Serial_ChangeMode(Pocket);
	HAL_UART_Receive_IT(&huart3,Receive_Byte,1);//启动串口3

    xQueueHandle_Usart3 = xQueueCreate(128,sizeof(uint8_t*));

    ret = xTaskCreate(Led_Task,"Led_Task",512,NULL,24,&xLedTaskHandle);
}

        这里是测试函数:

                这里非常重要,因为在串口模块中我们是将数据的地址写入队列中的,故这里接收必须使用二重指针,*Signal来接收队列里储存的地址,将地址取出后解引**Signal即可读取数据

void Led_Task(void *params)
{
	uint8_t **Signal = 0;//这里必须是二维指针
	while(1)
	{
		if (xQueueReceive(xQueueHandle_Usart3,*Signal, portMAX_DELAY) == pdPASS)
		{
			//Serial_SendByte(**Signal);
			Serical_SendPocket(*Signal);//传入字符或字符串地址
			//Led_SetState((Led_State)(**Signal));
			
		}
		vTaskDelay(pdMS_TO_TICKS(1000));
	}
}

以下是实验现象:

 这里单字节和不定长数据包都能实现相应功能,就只展示数据包的接收过程了。

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用FreeRTOS进行消息队列接收不定数据的过程,可以采用动态分配内存的方式来实现。 首先,需要创建一个消息队列(Queue)以接收不定数据。可以通过调用xQueueCreate函数来创建一个消息队列,并指定队列度和每个消息的大小。在此情况下,每个消息的大小可以设置为合适的最大度。 然后,在发送消息的任务,可以首先根据不定数据的度动态分配内存空间。可以使用pvPortMalloc函数来分配所需的内存大小,并将其作为消息发送到消息队列。这样,接收消息的任务就可以通过从消息队列接收消息来获取不定数据。 在接收消息的任务,可以先使用uxQueueMessagesWaiting函数来获取还未读取的消息数量。然后,可以使用xQueueReceive函数来从消息队列接收消息。在接收到消息后,可以根据接收到的数据进行相应的处理。 接收消息的任务需要根据接收到的消息度动态分配内存空间,可以使用pvPortMalloc函数来分配所需的内存大小,并将接收到的数据复制到分配的内存空间。最后,在处理完接收到的数据后,需要使用vPortFree函数释放分配的内存空间,以防止内存泄漏。 需要注意的是,在使用动态分配内存的情况下,必须小心处理内存分配和释放的问题,以避免内存泄漏或过多的内存碎片问题。可以根据实际需求和系统资源来调整消息队列度和内存大小,以确保系统的正常运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值