基于正点原子的FreeRTOS笔记——队列

一、什么是队列

队列是任务到任务、任务到中断、中断到任务数据交流的一种机制。

在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目”,队列能够存储“队列项目”的最大数量称为队列的长度。

在创建队列时要指定队列长度以及队列项目的大小。

二、队列特点

1、数据入队出队方式:队列通常采用”先进先出“(FIFO)先进入消息队列的消息先传给任务,

也可以配置为后进后出(LIFO)的方式。

2、数据传递方式:FreeRTOS消息队列传递的是实际数据即将数据拷贝到队列中进行传递,并不是数据地址,RTX,uCOS-II 和 uCOS-III 是传递的地址。FreeRTOS中可以通过传递指针地址方式来传递指针。

3、多任务访问:队列不属于某个任务,任何任务和中断都可以向队列发送或读取消息。

4、出队/入队阻塞:当任务向一个队列发送消息时可以指定一个阻塞时间。

  • 若阻塞时间为0 :直接返回不会等待。
  • 若为0~port_MAX_DELAY:等待设定的阻塞时间,超时后直接返回。
  • 若为port_MAX_DELAY:一直等到可以入队。

当一个任务试图从一个空队列中读取时,该队列将 进入阻塞状态(因此它不会消耗任何 CPU 时间,且其他任务可以运行) 直到队列中的数据变得可用,或者阻塞时间过期。

当一个任务试图写入到一个满队列时,该队列将 进入阻塞状态(因此它不会消耗任何 CPU 时间,且其他任务可以运行) 直到队列中出现可用空间,或者阻塞时间过期。

如果同一个队列上有多个处于阻塞状态的任务, 那么具有最高优先级的任务将最先解除阻塞。

如果优先级相同,等待时间最长的任务最先解除阻塞。

三、队列的函数

1、xQueueCreate

 QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,UBaseType_t uxItemSize );

创建一个新队列并返回 可引用此队列的句柄。

 configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中被设置为 1,或保留未定义状态(此时,它默认 为 1) ,才能使用此 RTOS API 函数。

每个队列需要 RAM 用于保存队列状态和 包含在队列(队列存储区域)中的项目。 如果使用 xQueueCreate() 创建队列,则所需的 RAM 将自动 从 FreeRTOS 堆中分配。 如果使用 xQueueCreateStatic() 创建队列, 则 RAM 由应用程序编写者提供,这会产生更多的参数, 但这样能够在编译时静态分配 RAM 。

参数:

uxQueueLength  队列可同时容纳的最大项目数 。
uxItemSize  存储队列中的每个数据项所需的大小(以字节为单位)。

数据项按副本排队,而不是按引用排队, 因此该值为每个排队项目将被复制的字节数。队列中每个数据项 必须大小相同。

返回:

如果队列创建成功,则返回所创建队列的句柄 。 如果创建队列所需的内存无法 分配 ,则返回 NULL。

2、xQueueSend

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

这是一个调用 xQueueGenericSend() 的宏。 

等同于 xQueueSendToBack()。

在队列中发布项目。不得从中断服务程序调用此函数。请参阅 xQueueSendFromISR() 以获取 可用于 ISR 的替代方案。

参数:

xQueue 队列的句柄,数据项将发布到此队列。
pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
xTicksToWait 如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果队列已满,并且 xTicksToWait 设置为0 ,调用将立即返回。时间在 tick 周期中定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。

如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

返回:如果成功发布项目,则返回 pdTRUE,否则返回 errQUEUE_FULL。

3、xQueueSendToBack

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

这是一个调用 xQueueGenericSend() 的宏。

等同于 xQueueSend()。

从队列尾部入队一个数据项。 数据项通过复制 而非引用入队。 不得从中断服务程序调用此函数。 请参阅 xQueueSendToBackFromISR (),获取可在 ISR 中使用的 替代方案。

参数:

xQueue 队列的句柄,数据项将发布到此队列。
pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
xTicksToWait 如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果设置为 0,调用将立即返回。时间以滴答周期为单位定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。

如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

返回:

如果成功发布项目,返回 pdTRUE,否则返回 errQUEUE_FULL。

4、xQueueSendToFront
 

BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );

此宏用于调用 xQueueGenericSend()。

从队列头部入队一个数据项。 数据项通过复制 而非引用入队。 不得从中断服务程序 调用此函数。 请参阅 xQueueSendToFrontFromISR() 了解 可在 ISR 中使用的替代方法。

参数:

xQueue 队列的句柄,数据项将发布到此队列。
pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
xTicksToWait 如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果设置为 0,调用将立即返回。时间以滴答周期为单位定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。

如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

返回:

如果成功发布项目,返回 pdTRUE,否则返回 errQUEUE_FULL。

5、xQueueReceive

BaseType_t xQueueReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait );

这是一个调用 xQueueGenericReceive() 函数的宏。

从队列中接收项目。该项目通过复制接收,因此必须提供足够大小的缓冲区。创建队列时定义了复制到缓冲区中的字节数。

成功接收后会将数据删除。

中断服务程序中不得使用此函数。请参阅 xQueueReceiveFromISR 了解可以选择的替代方案。

参数:

xQueue 要从中接收项目的队列的句柄。
pvBuffer 指向缓冲区的指针,接收到的项目将被复制到这个缓冲区。
xTicksToWait  如果在调用时队列为空,则任务应阻塞等待项目接收的最长时间。 如果队列为空,将 xTicksToWait 设置为 0 将导致函数立即返回。时间在滴答周期中定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。

如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

返回:

如果从队列成功接收到项目,返回 pdTRUE,否则返回 pdFALSE。

6、xQueuePeek
 

BaseType_t xQueuePeek( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait );

这是一个调用 xQueueGenericReceive() 函数的宏。

从队列中接收项目,而无须从队列中删除该项目。 项目由复制接收,因此必须提供适当大小的缓冲区 。 队列创建时,复制到缓冲区中的字节数已定义 。

成功接收的数据不会删除其仍在队列中。

中断服务例程中不得使用此宏。

参数:

xQueue 要从中接收项目的队列的句柄。
pvBuffer 指针,指向将复制收到的项目的缓冲区。 它必须至少足够大,才能容纳创建队列时定义的队列项的大小。
xTicksToWait 如果在调用时队列为空,则任务应阻塞等待项目接收的最长时间。 时间已在滴答周期中定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 来将其转换为实时。

如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

返回:

如果从队列中成功接收(窥视)项目,则返回 pdTRUE,否则返回 pdFALSE。

四、实验程序

 1、句柄声明和数组声明

QueueHandle_t Key_Hander  = NULL;
QueueHandle_t BigData_Hander  = NULL;
char BigData[50] = {"123zxcv123vbnm"};

2、 创建队列和开始任务

void freertos_demo(void)
{Key_Hander = xQueueCreate(2,sizeof(uint8_t));if(Key_Hander != NULL){printf("小数据队列创建成功\r\n");}BigData_Hander = xQueueCreate(1,sizeof(char *));if(BigData_Hander != NULL){printf("大数据队列创建成功\r\n");}xTaskCreate( (TaskFunction_t        ) start_task,(char *                ) "start_task",(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,(void *                ) NULL,(UBaseType_t           ) START_TASK_PRIO,(TaskHandle_t *        ) &start_task_handler );vTaskStartScheduler();
}

 3、开始任务

void start_task( void * pvParameters )
{taskENTER_CRITICAL();/*进入临界区*/xTaskCreate( (TaskFunction_t        ) task1,(char *                ) "task1",(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,(void *                ) NULL,(UBaseType_t           ) TASK1_PRIO,(TaskHandle_t *        ) &task1_handler );xTaskCreate( (TaskFunction_t        ) task2,(char *                ) "task2",(configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,(void *                ) NULL,(UBaseType_t           ) TASK2_PRIO,(TaskHandle_t *        ) &task2_handler );						 xTaskCreate( (TaskFunction_t        ) task3,(char *                ) "task3",(configSTACK_DEPTH_TYPE) TASK3_STACK_SIZE,(void *                ) NULL,(UBaseType_t           ) TASK3_PRIO,(TaskHandle_t *        ) &task3_handler );			vTaskDelete(NULL);taskEXIT_CRITICAL(); 		/*退出临界区*/				 
}

 4、任务1、2、3

/* 入队 */
void task1( void * pvParameters )
{uint8_t key = 0;BaseType_t backValue = 0;char *buf;buf = &BigData[0];while(1){key = key_scan(0);if(key == KEY0_PRES){backValue = xQueueSendToBack(Key_Hander,&key,portMAX_DELAY); if(backValue == pdTRUE){printf("小数据入队成功\r\n");}else printf("小数据入队失败\r\n");}else if(key == KEY1_PRES){backValue = xQueueSendToBack(BigData_Hander,&buf,portMAX_DELAY); if(backValue == pdTRUE){printf("大数据入队成功\r\n");}else printf("大数据入队失败\r\n");}}
}/* 出队 */
void task2( void * pvParameters )
{uint8_t key = 0;BaseType_t backValue = 0;while(1){backValue = xQueueReceive(Key_Hander,&key,portMAX_DELAY);if(backValue == pdTRUE){printf("小数据出队成功\r\n");}else printf("小数据出队失败\r\n");}}void task3( void * pvParameters )
{char *bigData;BaseType_t backValue = 0;while(1){backValue = xQueueReceive(BigData_Hander,&bigData,portMAX_DELAY);if(backValue == pdTRUE){printf("大数据出队成功\r\n%s\r\n",bigData);}else printf("大数据出队失败\r\n");}}

五、实验现象

1、按下KEY0按键,小数据队列入队出队

数据入队后由于任务2优先级大于任务1,所以任务2会抢占任务1

2、按下KEY1,大数据队列入队出队

六、发送大数据指针分析

memcpy函数原型 

void *memcpy(void *str1, const void *str2, size_t n)

参数: 

  • str1 -- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
  • str2 -- 指向要复制的数据源,类型强制转换为 void* 指针。
  • n -- 要被复制的字节数。

发送函数xQueueGenericSend会调用prvCopyDataToQueue将数据复制到队列

static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue,const void * pvItemToQueue,const BaseType_t xPosition )
{BaseType_t xReturn = pdFALSE;UBaseType_t uxMessagesWaiting;/********省略******************/if( pxQueue->uxItemSize == ( UBaseType_t ) 0 ){/********省略******************/}else if( xPosition == queueSEND_TO_BACK ){( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); }else{( void ) memcpy( ( void * ) pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /********省略******************/}/********省略******************/
}
/*-----------------------------------------------------------*/

接收函数xQueueReceive 调用prvCopyDataFromQueue将数据从队列中复制

static void prvCopyDataFromQueue( Queue_t * const pxQueue,void * const pvBuffer )
{if( pxQueue->uxItemSize != ( UBaseType_t ) 0 ){/********省略******************/( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); }
}

由于函数使用memcpy函数,需要传入数据的地址来进行复制,任务1中

char *buf; buf = &BigData[0];

 xQueueSendToBack(BigData_Hander,&buf,portMAX_DELAY); 

buf存放的是数组BigData的地址,&buf传递的是buf的地址相当于传递了数组地址的地址,接收队列复制后的数据就是数组的地址。任务3 bigData就等于&BigData[0];

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3225798.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

蹭一个围棋亚军!不要和低维的人说话——早读(逆天打工人爬取热门微信文章解读)

熬夜后需要补什么呢? 引言Python 代码第一篇 洞见 不要和低维的人说话(深度好文)第二篇 冲冲冲结尾 引言 昨晚真的是熬夜又想不出东西 真的头大 最近下围棋 这个棋感很好呀 我是K级选手 目前是8级 套几个buff 纯自学 为什么决定学围棋呢? 是…

翰德恩咨询赋能材料行业上市公司,共筑IPD管理体系新篇章

赋能背景概览 坐落于江苏的某材料行业领军企业,作为国内无机陶瓷膜元件及成套设备领域的佼佼者,以其庞大的生产规模、丰富的产品系列及卓越的研发实力,屹立行业之巅二十余年。公司不仅在新材料研发、技术创新、工艺设计、设备制造及整体解决…

swift与Internvl下的多模态大模型分布式微调指南(附代码和数据)

大模型相关目录 大模型,包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步,扬帆起航。 基于Dify的智能分类方案:大模型结合KNN算法(附代码&#xff…

InavFlight飞控固件学习-1《开发环境搭建》

目录 文章目录 目录摘要1.官网2.形成Linux开发环境工具2.1 简介2.2 相关工具2.2.1 Ubuntu / Debian系统配置命令2.2.2 Fedora系统配置命令2.2.3 Fedora系统配置命令 2.3 克隆存储库2.4 构建工具2.5 使用cmake2.6 构建固件2.7 清除2.8 cmake 缓存维护2.9 编译通过ninja2.10 更新…

小程序跳企业微信教程

来别急,我说几个点,你先记着, 必须真机才能测必须要企业微信里头去配置下关联到对应的小程序(文章底部有图)要同一个公司主体! 说说几个报错, 一、代码其实就这么一个,你别在微信…

virtuoso:layout如何做flat修drc

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 有些drc在virtuoso layout中修更节省时间,比如遇到一些via array需要做删改时,发现工具把via array做成了一个cell,如图。 去改cell可能会影…

高考志愿怎么选专业,哪些是热门专业?

选专业看上去非常简单,但是真正做起来的时候确实不容易,因为对于很多结束高考的学生来说,选专业就意味着他们选择自己的未来,这可是直接关系到未来的学习和职业发展,关系到将来的就业方向,再加上现在的社会…

逆向案例十一——华强北登录逆向

网址:aHR0cHM6Ly9wYXNzcG9ydC5ocWV3LmNvbS9sb2dpbg 登陆页面: 打开开发者工具会出现debugger调试: 直接使用一律不再此处暂停即可。点击登录,找到登录包。 发现有三个参数进行了加密,分别是Password,UserName和Devic…

如何修复 wxpython 事件调用?

1、问题背景 在使用 wxpython 开发 GUI 时,遇到了一个问题。当鼠标悬停在 BitmapButton 上时,会同时调用两个事件: self.Bind(wx.EVT_LEAVE_WINDOW, self.onPanelMouseLeave)self.Bind(wx.EVT_ENTER_WINDOW, self.onPanelMouseOver) 这导致…

解决U盘识别不了的一个小思路

我的U盘在自己在其他地方能正常使用,但是到了自己电脑突然就用不了了,其他设备也同样在自己电脑可以正常读写。 看了经验贴,大多是在设备管理器设置启动,但我的U盘明显不是这个问题。后面我看到一个贴子上的一个评论,想…

MQ四兄弟:如何保证消息顺序性

在当今的分布式系统架构中,消息队列(MQ)是不可或缺的组成部分。它们在确保系统组件之间高效通信方面发挥着关键作用。特别是在金融交易、物流跟踪等对消息处理顺序有严格要求的场景中,消息队列的顺序性保证显得更为重要。接下来&a…

本地电脑连接FTP服务器,显示无权限连接?

问题: 打开文档,在这一栏输入ftp服务器地址,按下回车,弹出无权限提示。 解决方案: 1、系统设置——网络和Internet——网络和共享中心 2、Internet选项 3、高级——取消“使用被动FTP”的勾选

@RequestBody注解的使用及源码解析

前言 RequestBody 注解是我们进行JavaEE开发,最常见的几个注解之一,这篇博文我们以案例和源码相结合,帮助大家更好的了解 RequestBody 注解 使用案例 1.自定义实体类 Data NoArgsConstructor AllArgsConstructor public class User {priv…

如何判断一个js对象为数组类型

如何判断一个js对象为数组类型? 能想到的最常见的intanceof是吗?开始是这么认为,但是不是哈,看下面的解释,也没有太明白,暂且记住吧 综上,判断js对象为数组的两种方式 Array.isArray([]) // trueObject.prototype.toString.call([]) ‘[object Array]’ //true

Oracle基础以及一些‘方言’(一)

1、什么是Oracle ORACLE数据库系统是美国ORACLE公司(甲骨文)提供的以分布式数据库为核心的一组软件产品,是最流行的客户/服务器(CLIENT/SERVER)或B/S体系结构的数据库之一。 ORACLE 通常应用于大型系统的数据库产品。 ORACLE 数据库是目前世界…

CSDN回顾与前行:我的创作之旅——2048天的技术成长与感悟

CSDN回顾与前行:我的创作之旅——2048天的技术成长与感悟 💖The Begin💖点点关注,收藏不迷路💖 前言 时光荏苒,岁月如梭。转眼间,从我在CSDN上写下第一篇技术博客《2-6 带头结点的链式表操作集…

从头开始学习扩散模型 Stable Diffusion

今天我们来揭开 Stable Diffusion 技术的神秘面纱。 1.稳定扩散原理 Stable Diffusion 在2022年发表,一种基于Latent Diffusion Models的新兴机器学习技术。它基于扩散过程,利用数学模型将机器学习中的高维度数据降低到低维度空间,并在该空间…

安卓 无线投屏 sink端 RTSP报 “505 RTSP Version not supported“

最近做安卓无线投屏的sink端,自己手搓RTSP协议,自己手搓容易出错,然后报了上面505,这个RTSP文档的意思是版本不一致,但是出现 "505 RTSP Version not supported"不一定是版本不一致,可能是 消息错…

AI究竟是在帮助开发者还是取代他们?来看大佬的观点你就明白了

AI(人工智能)在现代社会中扮演着越来越重要的角色,其在软件开发领域的应用也日益广泛。关于AI是在帮助开发者还是取代他们,V 哥个人认为,一半一半吧,为什么这么说,先不用噴,我们需要…

一个使用Go语言和现代Web技术构建跨平台桌面应用程序开源项目

大家好,今天给大家分享一个使用Go语言和现代Web技术构建跨平台桌面应用程序开源项目Wails。 Wails是一个允许开发者使用Go和Web技术编写桌面应用程序的项目。 它被设计为Go的快速且轻量的Electron替代品,旨在提供一个平台,让开发者可以利用Go…