当前位置:首页 > 技术分析 > 正文内容

FreeRTOS:Queue队列及队列API函数

ruisui881个月前 (05-16)技术分析27

前言

队列是为了任务与任务、任务与中断之间的通信而准备的,可以在任务与任务、任务与中断之间传递消息,队列中可以存储有限的、大小固定的数据项目。任务与任务、任务与中断之间要交流的数据保存在队列中,叫做队列项目。队列所能保存的最大数据项目数量叫做队列的长度,创建队列的时候会指定数据项目的大小和队列的长度。由于队列用来传递消息的,所以也称为消息队列。FreeRTOS 中的信号量的也是依据队列实现的!

一、队列功能介绍

1.数据存储

数据发送到队列中会导致数据拷贝,也就是将要发送的数据拷贝到队列中,这就意味着在队列中存储的是数据的原始值,而不是原数据的引用(即只传递数据的指针),这个也叫做值传递。

因为队列中存储的是原始值,任务将数据发送到队列中,就可以随意修改原始缓冲区。

2.多任务访问

队列不是属于某个特别指定的任务的,任何任务都可以向队列发送消息或者提取消息。

3.出队阻塞

当任务尝试从一个队列中读取消息的时候可以指定一个阻塞时间,这个阻塞时间就是当任务从队列中读取消息无效的时候任务阻塞的时间。

如果阻塞时间为0就是不阻塞,没有数据的话会马上返回任务继续执行接下来的代码。

如果阻塞时间为 0~ portMAX_DELAY,当任务没有从队列中获

取到消息的话就进入阻塞态,阻塞时间指定了任务进入阻塞态的时间,当阻塞时间到了以后还没有接收到数据的话就退出阻塞态,返回任务接着运行下面的代码,如果在阻塞时间内接收到了数据就立即返回,执行任务中下面的代码

当阻塞时间设置为portMAX_DELAY 的话,任务就会一直进入阻塞态等待,直到接收到数据为止!

4.入队阻塞

入队阻塞过程和出队阻塞相类似

二、队列工作过程

任务 A 要向任务 B 发送消息,这个消息是 x 变量的值。首先创建一个队列,并且指定队列的长度和每条消息的长度。这里我们创建了一个长度为 4 的队列,因为要传递的是x 值,而 x 是个 int 类型的变量,所以每条消息的长度就是 int 类型的长度,在 STM32 中就是 4

字节,即每条消息是 4 个字节的。

任务 A 的变量 x 值为 10,将这个值发送到消息队列中。此时队列剩余长度就是3 了。前面说了向队列中发送消息是采用拷贝的方式,所以一旦消息发送完成变量 x 就可以再次被使用,赋其他的值

任务 A 又向队列发送了一个消息,即新的 x 的值,这里是 20。此时队列剩余长度为 2。

中任务 B 从队列中读取消息,并将读取到的消息值赋值给 y,这样 y 就等于 10了。任务 B 从队列中读取消息完成以后可以选择清除掉这个消息或者不清除。当选择清除这个消息的话其他任务或中断就不能获取这个消息了,而且队列剩余大小就会加一,变成 3。如果

不清除的话其他任务或中断也可以获取这个消息,而队列剩余大小依旧是 2。

先发送的先读取

1.API函数

1.队列创建函数

函数 xQueueCreate()

QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength,
UBaseType_t uxItemSize)

参数:

uxQueueLength:要创建的队列的队列长度,这里是队列的数量

uxItemSize: 队列中每个项目(消息)的长度,单位为字节 队列创捷成功以后返回的队列句柄!

NULL: 队列创建失败。

2.入队函数

中断级一般不使用

1、函数 xQueueSend()、xQueueSendToBack()和 xQueueSendToFront()

这三个函数都是用于向队列中发送消息的,这三个函数本质都是宏,其中函数 xQueueSend()和 xQueueSendToBack()是一样的,都是后向入队,即将新的消息插入到队列的后面。函数xQueueSendToToFront()是前向入队,即将新消息插入到队列的前面。然而!这三个函数最后都是调用的同一个函数:xQueueGenericSend()。这三个函数只能用于任务函数中,不能用于中断服务函数,中断服务函数有专用的函数,它们以“FromISR”结尾

BaseType_t xQueueSend( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait);
BaseType_t xQueueSendToBack(QueueHandle_t xQueue,
 const void* pvItemToQueue,
 TickType_t xTicksToWait);
BaseType_t xQueueSendToToFront(QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait);

参数:

xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。

pvItemToQueue:指向要发送的消息,发送时候会将这个消息拷贝到队列中。

xTicksToWait: 阻塞时间,此参数指示当队列满的时候任务进入阻塞态等待队列空闲的最大

时间。如果为 0 的话当队列满的时候就立即返回;当为 portMAX_DELAY 的话就会一直等待,直到队列有空闲的队列 项,也就是死等,但是宏INCLUDE_vTaskSuspend 必须为 1。

返回值:

pdPASS: 向队列发送消息成功!

errQUEUE_FULL: 队列已经满了,消息发送失败。

2.队列操作实验

具体的都在代码里备注了

发送:

void task1_task(void *pvParameters)
{
	u8 key,i=0;
    BaseType_t err;  //错误码
	while(1)
	{
		key=KEY_Scan(0);            	// 扫描按键
        if((Key_Queue!=NULL)&&(key))    //消息队列key_queue 创建成功,并切按下按键
        {
            err=xQueueSend(Key_Queue,&key,10); //发送按键值想队列发送(句柄,发送的数据(取地址),阻塞时间)
            if(err==errQUEUE_FULL)   	//如果发送失败
            {
                printf("队列key_queue已满,数据发送失败!\r\n");
            }
        }
        i++;
        if(i%10==0) check_msg_queue();
        if(i==50)
        {
            i=0;
            LED0=!LED0;
        }
        vTaskDelay(10);                        
	}
}

接收:

阻塞时间采用死等的方式

//Keyprocess_task
void Keyprocess_task(void *pvParameters)
{
	u8 num,key;
	BaseType_t err;
	while(1)
	{
        if(Key_Queue!=NULL)
        {
					  
					err=xQueueReceive(Key_Queue,&key,portMAX_DELAY); 
            if(err==pdTRUE)
            {
                switch(key)
                {
                    case WKUP_PRES:		
                        LED1=!LED1;
                        break;
                    case KEY1_PRES:		
                        BEEP=!BEEP;
                        break;
                    case KEY0_PRES:		
                        num++;
                        LCD_Fill(126,111,233,313,lcd_discolor[num%14]);
                        break;
                }
            }
        } 
		vTaskDelay(10);      
	}
}

(串口)中断发送:

if((USART_RX_STA&0x8000)&&(Message_Queue!=NULL))
	{
		xQueueSendFromISR(Message_Queue,USART_RX_BUF,&xHigherPriorityTaskWoken);
		
		USART_RX_STA=0;	
		memset(USART_RX_BUF,0,USART_REC_LEN);
	
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
	}

定时器接收:


extern QueueHandle_t Message_Queue;	
extern void disp_str(u8* str);


void TIM2_IRQHandler(void)
{
	u8 *buffer;
	BaseType_t xTaskWokenByReceive=pdFALSE;
	BaseType_t err;
	
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		buffer=mymalloc(SRAMIN,USART_REC_LEN);
        if(Message_Queue!=NULL)
        {
            memset(buffer,0,USART_REC_LEN);	
            err=xQueueReceiveFromISR(Message_Queue,buffer,&xTaskWokenByReceive);
            if(err==pdTRUE)			
              {
                  disp_str(buffer);	
              }
        }
		myfree(SRAMIN,buffer);		
		
		portYIELD_FROM_ISR(xTaskWokenByReceive);
	}
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);  
}


总结

队列的具体操作以及相关API函数需要会使用,在后续做项目的过程中,可以节省很多复杂的事。

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/4098.html

标签: js 队列
分享给朋友:

“FreeRTOS:Queue队列及队列API函数” 的相关文章

vue中如何在自定义组件上使用v-model和.sync

自定义事件tips推荐始终使用 kebab-case 的事件名。(v-on会将事件名自动转换为小写,避免匹配不到)changeData ×change-data √自定义组件的v-model用法:父组件定义数据源(不需要定义修改数据的方法),在子组件标签上通过v-model="data...

Acustica Audio 发布模拟Roland Jupiter 双声道合成器插件 TH2

福利: Acustica Audio 发布模拟Roland Jupiter 风格的双声道合成器插件 TH2 免费下载 意大利 Acustica Audio 公司发布布模拟Roland Jupiter 风格的双声道合成器插件 TH2 ,灵感来源于Acustica Audio的THING-8系列,它是...

多项修正 尼康D4s发布最新1.10版固件

尼康公司与2014年8月27日发布了D4s的最新固件,固件版本号为C:1.10。这次固件升级,主要解决了一些BUG,并且对拍摄菜单与相机操作做了一定调整。下面是本次新固件的具体信息:尼康发布D4s最新C固件 1.10版对C固件升级到1.10版所作的修改:当选定运动VR模式并换上 AF-S 尼克尔 4...

Gemini应用在Android上广泛推出2.0闪电模式切换器

#头条精品计划# 快速导读谷歌(搜索)应用的测试频道在安卓设备的双子应用中推出了2.0闪电实验功能,现已向稳定用户开放。双子应用通过谷歌应用运行,目前推出的15.50版本中,用户可通过模型选择器体验不同选项,包括1.5专业版、1.5闪电版和2.0闪电实验版。2.0闪电实验模型提供了更快的响应速度和优...

22《Vue 入门教程》VueRouter 路由嵌套

1. 前言本小节我们介绍如何嵌套使用 VueRouter。嵌套路由在日常的开发中非常常见,如何定义和使用嵌套路由是本节的重点。同学们在学完本节课程之后需要自己多尝试配置路由。2. 配置嵌套路由实际项目中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层...

从 Vue2.0 到 React17——React 开发入门

作者:佚名来源:前端大全前言找工作时发现有一些公司是以React作为技术栈的,而且薪资待遇都不错,为了增加生存的筹码,所以还是得去学一下React,增加一项求生技能。因为我用Vue2.0开发项目已经四年了,故用Vue2.0开发项目的思路来学习React。前端项目是由一个个页面组成的,对于Vue来说,...