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

举报
Meno 发表于 2024/10/21 14:46:42 2024/10/21
【摘要】 2024.5.2日五一假期埋头苦战串口收发数据以下为心得备忘:一.基本收发首先是仿照江科大标准库移植的串口基本收发函数,进行了一些改写,能够在单字节以及数据包之间进行模式转换:uint8_t Receive_Mode = 0;//接收模式:单字节或数据包uint8_t Receive_State;//状态机变量uint8_t Receive_Byte[1],Receive_ITFlag;//...

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));
	}
}

三.实验现象 

以下是实验现象:


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

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。