STM32开发 数据包环形队列


前言

环形队列的理论知识网上有很多文章,这里我就仅通过代码分享一下使用经验,在我的驱动中,队列内容是一包一包的待处理数据包,而不是一个个的字节。


一、构建环形队列结构体

队列中头部位置和尾部位置的指示是必须的,另外为了使用方便,加入数据包计数cnt,写入一包数据时cnt++,读取一包数据时cnt–,需要判断队列空或满时,就看cnt是否为0或最大值。
*注意:因为在驱动中,队列中放置的是一包一包的待处理数据包,所以又加入了每个数据包的有效长度lenth,读取到数据包后,还返回此包的数据长度。

首先需要定义队列大小:队列中最多存放多少数据包、单包的最大长度
//队列中数据包个数最大值:64包
#define Queue_Packet_MaxNumber	64
//队列中单包最大长度
#define Queue_Packet_MaxLenth	256

//定义队列结构体
typedef struct{
	int front;		//队列当前头位置
	int rear;		//队列当前尾位置
	int	counter;	//队列数据包计数
	uint8_t  data[Queue_Packet_MaxNumber][Queue_Packet_MaxLenth];	//存放数据包内容
	uint16_t lenth[Queue_Packet_MaxNumber];							//数据包对应的长度
}xQueue_Typedef;

二、队列初始化

1.定义一个队列

//串口接收队列
xQueue_Typedef Usart_Receive_Queue;

//初始化一个队列
void xQueue_Init(xQueue_Typedef *Queue)
{	
	Queue->front=0;
	Queue->rear=0;
	Queue->counter=0;
	memset(&Queue->data[0][0],0,Queue_Packet_MaxLenth*Queue_Packet_MaxNumber);
}

2.队列初始化,调用上面的函数即可,会把传入的队列清空

xQueue_Init(&Usart_Receive_Queue);

三、读写数据

1、队列满判断

读取队列内容前,要先判断队列是否为空,如果为空,读取失败。
队列写入数据前,要先判断队列是否已满,如果已满,写入失败。

//队列满判断
//返回:1:队列已满、0:队列未满
uint8_t xQueue_FullJudge(xQueue_Typedef *Queue)
{
	//队列中数据包个数已为最大值,队列已满
	if(Queue->counter==Queue_Packet_MaxNumber)
	{
		return 1;
	}else
	{
		return 0;
	}
}

2、队列空判断

//队列空判断
//返回:1:队列已空、0:队列未空
uint8_t xQueue_EmptyJudge(xQueue_Typedef *Queue)
{
	if(Queue->counter==0)
	{
		return 1;
	}else
	{
		return 0;
	}
}

3、队列写入数据

//存入一包数据
//返回0:队列已满,写入失败、返回1:写入成功
//函数内部不判断写入的长度是否会超过单包最大值,调用函数前应该自行判断
uint8_t xQueue_WriteData(xQueue_Typedef *Queue,uint8_t *Data,uint16_t Lenth)
{
	//判断是否为满
	if(xQueue_FullJudge(Queue))
	{
		return 0;
	}else
	{
		//队列当前数据量++
		Queue->counter++;	
		//写入一包数据
		memcpy(&Queue->data[Queue->rear][0],&Data[0],Lenth);
		//记录此包数据的长度
		Queue->lenth[Queue->rear]=Lenth;
		//包尾位置++
		Queue->rear=(Queue->rear+1)%Queue_Packet_MaxNumber;
		return 1;
	}
}

4、队列读取数据

//读出一包数据
//返回0:队列为空,读取失败、返回非0:读取的数据包长度
uint16_t xQueue_ReadData(xQueue_Typedef *Queue,uint8_t *Data)
{
	uint16_t Lenth;
	//判断是否为空
	if(xQueue_EmptyJudge(Queue))
	{
		return 0;
	}else
	{
		//当前数据量--
		Queue->counter--;
		//此包长度
		Lenth=Queue->lenth[Queue->front];	
		//读出一包数据
		memcpy(&Data[0],&Queue->data[Queue->front][0],Lenth);
		//包头位置++
		Queue->front=(Queue->front+1)%Queue_Packet_MaxNumber;
		return Lenth;
	}
}

四、实际使用

后续有时间了更新一篇STM32串口使用总结:CubeMX配置、接收中断逐字节处理、空闲中断整包处理、空闲中断+DMA接收、串口阻塞发送与DMA发送等等

//定义队列
xQueue_Typedef Usart_Receive_Queue;

int main()
{
	/* code begin*/
	
	//数据队列初始化
	xQueue_Init(&Usart_Receive_Queue);

	/* code end*/

	return 0;
}

//串口1中断服务程序
void USART1_IRQHandler(void)                	
{
	HAL_UART_IRQHandler(&UART1_Handler);
	//空闲中断到
	if(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_IDLE) != RESET)
	{
		//清中断
		__HAL_UART_CLEAR_IDLEFLAG(&UART1_Handler);	
		//DMA停止
		HAL_UART_DMAStop(&UART1_Handler);

		//获取数据包长度
		Receive_Frame_Len_Usart=RECEIVE_MAX_LENTH-__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
		//写入队列,Receive_Buff_Usart是接收的缓冲区,写入队列时没有判断长度
		xQueue_WriteData(&Usart_Receive_Queue,Receive_Buff_Usart,Receive_Frame_Len_Usart);
		
		//重新指定DMA接收的缓冲区
		HAL_UART_Receive_DMA(&UART1_Handler,Receive_Buff_Usart,RECEIVE_MAX_LENTH);		
	}
}

//处理数据
int UnpackModule_Periodic_Handle()
{
	uint8_t Data[Queue_Packet_MaxLenth];
	uint16_t Lenth;
	
	/*code begin*/
	
	//读取队列中的数据
	Lenth=xQueue_ReadData(&Usart_Receive_Queue,Data);
	//长度非0,内容有效
	if(Lenth)
	{
		//判断包头包尾
		if(Data[0] != 0x55 || Data[Lenth-] != 0xAA)
		{
			return 0;
		}

		switch(Data[2])
		{
			default:break;
		}
	}

	/* code end*/
	
	return 0;
}

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值