基于FreeRTOS的队列改造

我之前写过一篇关于队列的blog,需要的戳静态队列。前一段时间分析了FreeRTOS的队列源码。FreeRTOS队列的核心思想和我前边写的队列的核心思想是一致的(我这里说的核心思想是都把队列缓存区看做一个环形缓存),只是在具体实现方式和功能上小有差异,FreeRTOS的队列支持从前向后入队和从后向前入队,这就相当于把栈的功能也实现了。
我把FreeRTOS队列的代码在Visual studio 环境中调试了一下,做了一些改造,方便以后在项目中使用(数据结构的知识很重要,项目中经常用到。而且面试官还总问,因为既能考察你的基础知识,又能考察你的逻辑思维能力,其实我曾经有一次面试就在队列上翻车了,血淋淋的教训)。现在先写到博客中,方便后续查阅。
队列结构体

typedef struct
{
	uint8_t *phead;	//指向第一个队列项的地址			
	uint8_t *ptail;//指向最后一个队列项的下一个地址					
	uint8_t *pwriteto;//指向队列中第一个空闲的列表项				
	uint8_t *preadfrom;	//最后一个出队的队列项的地址		
	volatile ulong_t uxmessageswaiting;//队列中有效的队列项数
	ulong_t uxlength;//队列的长度
	ulong_t uxitemsize;//队列项的大小
}queue;

队列创建函数

/****
功能:队列创建
参数:队列长度
参数:队列项大小
返回值:队列句柄
****/
queue * queue_create(const ulong_t queue_length, const ulong_t queue_itemsize)
{
	queue * handle = NULL;
	ulong_t buffer_size = 0;
	uint8_t * queuestorage = NULL;

	buffer_size = queue_length * queue_itemsize;//计算出队列缓存区的大小

	handle = (queue *)malloc(sizeof(queue)+buffer_size);//申请队列所需要的内存

	if (handle != NULL)
	{
		queuestorage = (uint8_t *)handle + sizeof(queue);//计算出队列缓存区的起始地址

		handle->phead = queuestorage;
		handle->uxitemsize = queue_itemsize;
		handle->uxlength = queue_length;

		queue_rest(handle);
	}
	return handle;
}

队列创建函数完成了两个主要任务。
第一内存申请,申请队列结构体内存和队列缓存区内存。申请完内存如下图。
在这里插入图片描述
第二初始化队列结构体成员。在队列创建函数中初始化了一部分成员变量,在复位函数中初始化了一部分成员。
初始化以后队列结构体中成员变量的指向关系如下图。
在这里插入图片描述
成员uxmessageswaiting表示的是队列中有效的队列项数,初始化以后是0。
队列复位函数

/****
功能:队列复位函数
参数:队列句柄
返回值:无
****/
void queue_rest(queue * const handle)
{
	handle->ptail = handle->phead + handle->uxlength * handle->uxitemsize;
	handle->uxmessageswaiting = 0;
	handle->pwriteto = handle->phead;
	handle->preadfrom = handle->phead + (((handle->uxlength) - 1) * handle->uxitemsize);
}

判断队列满函数

/****
功能:判断队列满函数
参数:队列句柄
返回值: 0队列满 1队列不满
****/
uint8_t queue_is_full(const queue * const handle)
{
	if (handle->uxmessageswaiting == handle->uxlength)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

判断队列空函数

/****
功能:判断队列空函数
参数:队列句柄
返回值: 0队列空 1队列不空
****/
uint8_t queue_is_empty(const queue * const handle)
{
	if (handle->uxmessageswaiting == 0)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

成员uxmessageswaiting 表示的是队列中有效的队列项数。当uxmessageswaiting等于成员uxlength时那么队列缓存中就没有多余的空间了,此时队列是满的。当uxmessageswaiting等于0的时候那么队列缓存中没有有效数据,表示队列是空的。
接下来我们看重点入队函数出队函数
入队函数

/****
功能:入队函数
参数:队列句柄
参数:待入队数据的地址
返回值:入队结果 0成功 1失败
****/
uint8_t queue_send(queue * const handle, const void * const ptemp, const uint8_t xposition)
{
	if (handle->uxmessageswaiting < handle->uxlength)
	{
		if (xposition == QUEUE_SEND_TO_BACK)//从前向后入队
		{
			memcpy(handle->pwriteto,ptemp,handle->uxitemsize);//复制数据到队列
			handle->pwriteto += handle->uxitemsize;//偏移到下一个队列项的地址,
			if (handle->pwriteto >= handle->ptail)//存放到末尾再从头开始存,在逻辑上是环形的
			{
				handle->pwriteto = handle->phead;
			}
		}
		else if (xposition == QUEUE_SEND_TO_FRONT)//从后向前入队
		{
			memcpy(handle->preadfrom,ptemp,handle->uxitemsize);
			handle->preadfrom -= handle->uxitemsize;
			if (handle->preadfrom < handle->phead)
			{
				handle->preadfrom = handle->ptail - handle->uxitemsize;
			}

		}
		handle->uxmessageswaiting += 1;
		return 0;
	}
	return 1;
}

1、先判断队列是否有缓存空间存放数据,如果没有空间存放直接返回1,入队失败。如果有空间则做入队操作。
2、这个入队函数写的就比较强大了。我们知道入队就是将数据存入队列缓存。FreeRTOS入队有两种方向:从前向后入队(从缓存区的低地址向缓存区的高地址方向存放)和从后向前入队(从缓存区的高地址向缓存区的低地址方向存放)。
从前向后入队:
先将数据拷贝到队列缓存中。然后pwriteto指针变量向后偏移一个队列项。偏移到末尾的时候回过头来再从第一个开始存。物理上是线性的,逻辑上是环形的。
从后向前入队是同样的道理。
3、如果入队成功队列缓存中有效个数要加1。
出队函数

/****
功能:出队函数
参数:队列句柄
参数:保存出队数据的地址
返回值:出队结果 0成功 1失败
****/
uint8_t queue_receive(queue * const handle, const void * const ptemp)
{
	if (handle->uxmessageswaiting > 0)
	{
		handle->preadfrom += handle->uxitemsize;
		if (handle->preadfrom >= handle->ptail)
		{
			handle->preadfrom = handle->phead;
		}
		memcpy((void *)ptemp,(void *)handle->preadfrom,handle->uxitemsize);
		handle->uxmessageswaiting -= 1;
		return 0;
	}
	return 1;
}

1、判断队列是否为空,如果为空直接返回1,不为空将读取数据的指针向后偏移一个队列项,如果超过队列的缓存边界,就回到缓存的起始地址处。
2、拷贝出数据,然后队列项数减1。
最后我们看下运行结果。

int main()
{
	uart_receive_handle =  queue_create(5,1);

	test_data = 50;

	if (queue_send(uart_receive_handle, &test_data, QUEUE_SEND_TO_FRONT) == 0)
	{
		printf("入队成功\n");
	}
	else
	{
		printf("入队失败\n");
	}

	test_data++;
	if (queue_send(uart_receive_handle, &test_data, QUEUE_SEND_TO_FRONT) == 0)
	{
		printf("入队成功\n");
	}
	else
	{
		printf("入队失败\n");
	}
	test_data++;
	if (queue_send(uart_receive_handle, &test_data, QUEUE_SEND_TO_FRONT) == 0)
	{
		printf("入队成功\n");
	}
	else
	{
		printf("入队失败\n");
	}
	test_data++;
	if (queue_send(uart_receive_handle, &test_data, QUEUE_SEND_TO_FRONT) == 0)
	{
		printf("入队成功\n");
	}
	else
	{
		printf("入队失败\n");
	}
	test_data++;
	if (queue_send(uart_receive_handle, &test_data, QUEUE_SEND_TO_FRONT) == 0)
	{
		printf("入队成功\n");
	}
	else
	{
		printf("入队失败\n");
	}
	test_data++;
	if (queue_send(uart_receive_handle, &test_data, QUEUE_SEND_TO_FRONT) == 0)
	{
		printf("入队成功\n");
	}
	else
	{
		printf("入队失败\n");
	}


	if (queue_receive(uart_receive_handle, &receive_data) == 0)
	{
		printf("出队成功\t%d\n",receive_data);
	}
	else
	{
		printf("出队失败\n");
	}

	if (queue_receive(uart_receive_handle, &receive_data) == 0)
	{
		printf("出队成功\t%d\n", receive_data);
	}
	else
	{
		printf("出队失败\n");
	}

	if (queue_receive(uart_receive_handle, &receive_data) == 0)
	{
		printf("出队成功\t%d\n", receive_data);
	}
	else
	{
		printf("出队失败\n");
	}

	if (queue_receive(uart_receive_handle, &receive_data) == 0)
	{
		printf("出队成功\t%d\n", receive_data);
	}
	else
	{
		printf("出队失败\n");
	}

	if (queue_receive(uart_receive_handle, &receive_data) == 0)
	{
		printf("出队成功\t%d\n", receive_data);
	}
	else
	{
		printf("出队失败\n");
	}

	getchar();
	return 0;
}

从前向后入队的效果。
在这里插入图片描述
从后向前入队的效果。
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值