STM32 | FreeRTOS 消息队列

news2025/5/23 2:56:01

01  

一、概述

队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任务间传递信息,实现了任务接收来自其他任务或中断的不固定长度的消息,任务能够从队列里面读取消息,当队列中的消息是空时,读取消息的任务将被阻塞,用户还可以指定阻塞的任务时间 xTicksToWait,在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。

当队列中有新消息时,被阻塞的任务会被唤醒并处理新消息;当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转为就绪态

消息队列是一种异步的通信方式。通过消息队列服务,任务或中断服务例程可以将一条或多条消息放入消息队列中。同样,一个或多个任务可以从消息队列中获得消息。当有多个消息发送到消息队列时,通常是将先进入消息队列的消息先传给任务,也就是说,任务先得到的是最先进入消息队列的消息,即先进先出原则(FIFO),但是也支持后进先出原则(LIFO)。

特性

FreeRTOS 中使用队列数据结构实现任务异步通信工作,具有如下特性: 

  • LIFO)。 

应用场景

消息队列可以应用于发送不定长消息的场合,包括任务与任务间的消息交换,队列是 FreeRTOS 主要的任务间通讯方式,可以在任务与任务间、中断和任务间传送信息,发送到队列的消息是通过拷贝方式实现的,这意味着队列存储的数据是原数据,而不是原数据的引用。

02  

二、消息队列的运作机制

    创建消息队列时 FreeRTOS 会先给消息队列分配一块内存空间,这块内存的大小等于消息队列控制块大小加上(单个消息空间大小与消息队列长度的乘积),接着再初始化消息队列,此时消息队列为空。FreeRTOS的消息队列控制块由多个元素组成,当消息队列被 创建时,系统会为控制块分配对应的内存空间,用于保存消息队列的一些信息如消息的存 储位置,头指针 pcHead、尾指针 pcTail、消息大小uxItemSize以及队列长度uxLength等。同时每个消息队列都与消息空间在同一段连续的内存空间中,在创建成功的时候,这些内存就被占用了,只有删除了消息队列的时候,这段内存才会被释放掉,创建成功的时候就已经分配好每个消息空间与消息队列的容量,无法更改,每个消息空间可以存放不大于消息大小uxItemSize的任意类型的数据,所有消息队列中的消息空间总数即是消息队列的长度,这个长度可在消息队列创建时指定。

    任务或者中断服务程序都可以给消息队列发送消息,当发送消息时,如果队列未满或者允许覆盖入队,FreeRTOS 会将消息拷贝到消息队列队尾,否则,会根据用户指定的阻塞 超时时间进行阻塞,在这段时间中,如果队列一直不允许入队,该任务将保持阻塞状态以等待队列允许入队。当其它任务从其等待的队列中读取入了数据(队列未满),该任务将 自动由阻塞态转移为就绪态。当等待的时间超过了指定的阻塞时间,即使队列中还不允许 入队,任务也会自动从阻塞态转移为就绪态,此时发送消息的任务或者中断程序会收到一个错误码 errQUEUE_FULL。

发送紧急消息的过程与发送消息几乎一样,唯一的不同是,当发送紧急消息时,发送的位置是消息队列队头而非队尾,这样,接收者就能够优先接收到紧急消息,从而及时进行消息处理。

    当某个任务试图读一个队列时,其可以指定一个阻塞超时时间。在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当其它任务或中断服务程序往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态。当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。

    当消息队列不再被使用时,应该删除它以释放系统资源,一旦操作完成,消息队列将被永久性的删除。

03  

1.消息队列创建函数 

QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength,UBaseType_t uxItemSize);

功能描述:用于创建一个新的队列。

参数:

返回值:

成功-如果创建成功则返回一个队列句柄,用于访问创建的队列;

失败-如果创建不成功则返回NULL,可能原因是创建队列需要的 RAM 无法分配成功。

04  

2.消息队列静态创建函数

QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength,UBaseType_t uxItemSize,uint8_t *pucQueueStorageBuffer,StaticQueue_t *pxQueueBuffer );

功能描述:用于创建一个新的队列。

参数:

  1. uint8_t 类型的数组,数组的大小至少有uxQueueLength* uxItemSize 个字节。当 uxItemSize 为 0 时,pucQueueStorageBuffer 可以为 NULL。
  1. StaticQueue_t 类型的变量,该变量用于存储队列的数据结构。

返回值:

成功-如果创建成功则返回一个队列句柄,用于访问创建的队列;

失败-如果创建不成功则返回NULL,可能原因是创建队列需要的 RAM 无法分配成功。

05  

3.用于向队列尾部发送一个队列消息

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

参数说明:

  1. xTicksToWait 被设置成0,函数立刻返回。超时时间的单位为系统节拍周期,常量portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为ms。如果INCLUDE_vTaskSuspend 设置成1,并且指定延时为portMAX_DELAY 将导致任务挂起(没有超时)。

返回值:

消息发送成功成功返回pdTRUE,否则返回errQUEUE_FULL。

06  

4.在中断服务程序中用于向队列尾部发送一个消息

BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);

参数说明:

  1. 队列句柄。
  2. 指针,指向要发送到队列尾部的消息。
  3. 如果入队导致一个任务解锁,并且解锁的任务优先级高于当前被中断的任务,则将*pxHigherPriorityTaskWoken设置成pdTRUE,然后在中断退出前需要进行一次上下文切换,去执行被唤醒的优先级更高的任务。从FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken 作为一个可选参数,可以设置为NULL。

返回值:

消息发送成功成功返回pdTRUE,否则返回errQUEUE_FULL。

5.向队列队首发送一个消息

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

参数说明:

  1. 被设置成 0,函数立刻返回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时)。

返回值:

消息发送成功成功返回pdTRUE,否则返回errQUEUE_FULL。

6.在中断服务程序中向消息队列队首发送一个消息

BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);

参数说明:

  1. 队列句柄。
  2. 指针,指向要发送到队首的消息。
  3. 如果入队导致一个任务解锁,并且解锁的任务优先级高于当前被中断的任务,则将*pxHigherPriorityTaskWoken设置成pdTRUE,然后在中断退出前需要进行一次上下文切换,去执行被唤醒的优先级更高的任务。从FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken 作为一个可选参数,可以设置为NULL。

返回值:

消息发送成功成功返回pdTRUE,否则返回errQUEUE_FULL。

7.从一个队列中接收消息,并把接收的消息从队列中删除

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

参数说明:

  1. 地址。
  2. 0,函数立刻返 回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用 于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设 置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时)。

返回值:

队列项接收成功返回 pdTRUE,否则返回 pdFALSE。

若接收完消息,不想删除,可以使用xQueuePeek函数。

8.在中断中从一个队列中接收消息,并从队列中删除该消息

BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxHigherPriorityTaskWoken);

参数说明:

  1. 在该队列上。如果 xQueueReceiveFromISR()到账了一个任务解锁了则将 *pxHigherPriorityTaskWoken设置为pdTRUE ,否则 *pxHigherPriorityTaskWoken的值将不变。从 FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken 作为一个可选参数,可以设置为NULL。

返回值:

队列项接收成功返回 pdTRUE,否则返回 pdFALSE。

若接收完消息,不想删除,可以使用xQueuePeekFromISR函数。

07  

四、消息队列使用注意事项

在使用 FreeRTOS 提供的消息队列函数的时候,需要了解以下几点: 

1. 使用 xQueueSend()、xQueueSendFromISR()、xQueueReceive()等这些函数之前应先 创建需消息队列,并根据队列句柄进行操作。 

2. 队列读取采用的是先进先出(FIFO)模式,会先读取先存储在队列中的数据。当 然也 FreeRTOS 也支持后进先出(LIFO)模式,那么读取的时候就会读取到后进 队列的数据。 

3. 在获取队列中的消息时候,我们必须要定义一个存储读取数据的地方,并且该数 据区域大小不小于消息大小,否则,很可能引发地址非法的错误。 

4. 无论是发送或者是接收消息都是以拷贝的方式进行,如果消息过于庞大,可以将消息的地址作为消息进行发送、接收。

5. 队列是具有自己独立权限的内核对象,并不属于任何任务。所有任务都可以向同一队列写入和读出。一个队列由多任务或中断写入是经常的事,但由多个任务读出倒是用的比较少。

6、在中断使用消息队列发送与接收时注意将抢占优先级设置大于等于5,官方库存函数说明如下

/* The highest interrupt priority that can be used by any interrupt service

routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL

INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER

PRIORITY THAN THIS! (higher priorities are lower numeric values. */

#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5

08  

五、示例代码

5.1 任务之间消息传递

1.freertos.c

void MX_FREERTOS_Init(void) {

/* USER CODE BEGIN RTOS_QUEUES */

/* add queues, ... */

/* 创建Test_Queue */

Test_Queue = xQueueCreate((UBaseType_t ) QUEUE_LEN,/* 消息队列的长度 */

  (UBaseType_t ) QUEUE_SIZE);/* 消息的大小 */

if(NULL == Test_Queue)

printf("创建Test_Queue消息队列失败!\r\n");

/* USER CODE END RTOS_QUEUES */

/* Create the thread(s) */

/* definition and creation of defaultTask */

// osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);

//defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

/* USER CODE BEGIN RTOS_THREADS */

/* 创建app_task2任务 */

app_task1_handle=xTaskCreateStatic((TaskFunction_t )app_task1,  /* 任务入口函数 */

(const char*    )"app_task1",/* 任务名字 */

(uint16_t       )512,  /* 任务栈大小 */

(void*          )NULL,/* 任务入口函数参数 */

(UBaseType_t    )4, /* 任务的优先级 */

(StackType_t*   )app_task1_stack,//任务堆栈

(StaticTask_t*  )&app_task1_buffer);/* 任务控制块指针 */ 

app_task2_handle=xTaskCreateStatic((TaskFunction_t )app_task2,  /* 任务入口函数 */

(const char*    )"app_task2",/* 任务名字 */

(uint16_t       )512,  /* 任务栈大小 */

(void*          )NULL,/* 任务入口函数参数 */

(UBaseType_t    )5, /* 任务的优先级 */

(StackType_t*   )app_task2_stack,//任务堆栈

(StaticTask_t*  )&app_task2_buffer);/* 任务控制块指针 */   

/* add threads, ... */

/* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartDefaultTask */

/**

* @brief Function implementing the defaultTask thread.

* @param argument: Not used 

* @retval None

*/

/* USER CODE END Header_StartDefaultTask */

void StartDefaultTask(void const * argument)

{

/* USER CODE BEGIN StartDefaultTask */

/* Infinite loop */

for(;;)

{

osDelay(1000);

}

/* USER CODE END StartDefaultTask */

}

/* Private application code --------------------------------------------------*/

/* USER CODE BEGIN Application */

static void app_task1(void* pvParameters)

{

BaseType_t xReturn=pdFALSE;

uint32_t txd = 1;

for(;;)

{

xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */

&txd,/* 发送的消息内容 */

0 );        /* 等待时间 0 */

if(pdPASS == xReturn)

printf("[app_task1]消息txd发送成功!\r\n");

else

printf("[app_task2]消息txd发送失败!\r\n");

txd++;

vTaskDelay(1000);

}

} 

static void app_task2(void* pvParameters)

{

BaseType_t xReturn=pdFALSE;

uint32_t rxd;/* 定义一个接收消息的变量 */

for(;;)

{

xReturn = xQueueReceive( Test_Queue,     /* 消息队列的句柄 */

&rxd,      /* 发送的消息内容 */

portMAX_DELAY); /* 等待时间一直等 */

if(pdTRUE == xReturn)

printf("[app_task2]本次接收到的数据是%x\r\n",rxd);

else

printf("[app_task2]数据接收出错,错误代码0x%lx\n",xReturn);  

}

} 

2. 演示

09  

5.2 中断向任务发送消息

1.freertos.c

static void app_task1(void* pvParameters)

{

BaseType_t xReturn=pdFALSE;

char buf[QUEUE_SIZE]={0};

uint32_t task_cnt=0;

for(;;)

{

task_cnt++;

sprintf(buf,"app_task1 have run %d times",task_cnt);

xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */

buf,/* 发送的消息内容 */

0 );        /* 等待时间 0 */

if(pdPASS == xReturn)

printf("[app_task1]消息txd发送成功!\r\n");

else

printf("[app_task2]消息txd发送失败!\r\n");

vTaskDelay(3000);

}

} 

static void app_task2(void* pvParameters)

{

BaseType_t xReturn=pdFALSE;

uint8_t buf[QUEUE_SIZE]={0};/* 定义一个接收消息的变量 */

for(;;)

{

xReturn = xQueueReceive( Test_Queue,     /* 消息队列的句柄 */

buf,      /* 发送的消息内容 */

portMAX_DELAY); /* 等待时间一直等 */

if(pdTRUE == xReturn)

printf("[app_task2]本次接收到的数据是%s\r\n",buf);

else

printf("[app_task2]数据接收出错,错误代码0x%lx\n",xReturn);  

memset(buf,0,sizeof buf);

}

} 

2.usart.c

static volatile uint8_t g_usart1_recv_buf[64]={0};

static volatile uint32_t g_usart1_recv_cnt = 0;

extern QueueHandle_t Test_Queue; 

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

uint32_t ulReturn;

/* 进入临界段,临界段可以嵌套 */

ulReturn = taskENTER_CRITICAL_FROM_ISR();

if(huart->Instance == USART1)// 判断是由哪个串口触发的中断

{

g_usart1_recv_buf[g_usart1_recv_cnt]=hal_uart_rx_data;

//记录多少个数据

g_usart1_recv_cnt++;

//检测到'#'符或接收的数据满的时候则发送数据

if(hal_uart_rx_data=='#' || g_usart1_recv_cnt>=(sizeof g_usart1_recv_buf))

{

xQueueSendFromISR(Test_Queue,(void *)g_usart1_recv_buf,NULL);

g_usart1_recv_cnt=0;

}

// 重新使能串口1接收中断

HAL_UART_Receive_IT(huart,&hal_uart_rx_data,1);

}

/* 退出临界段 */

taskEXIT_CRITICAL_FROM_ISR( ulReturn );

}

3. 演示

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

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

相关文章

便捷的Office批量转PDF工具

软件介绍 本文介绍的软件是一款能实现Office批量转换的工具,名为五五Excel word批量转PDF。 软件小巧 这款五五Excel word批量转PDF软件大小不到2M。 操作步骤一 使用该软件时,只需把软件和需要转换的Word或Excel文件放在同一个文件夹里。 操作步骤…

opcUA 编译和建模入门教程(zhanzhi学习笔记)

一、使用SIOME免费工具建模 从西门子官网下载软件SIOS,需要注册登录,下载安装版就行。下载后直接安装就可以用了,如图: 安装完成后打开,开始建模,如图左上角有新建模型的按钮。 新建了新工程后&#xff0c…

【关联git本地仓库,上传项目到github】

目录 1.下载git2.绑定用户3.git本地与远程仓库交互4.github项目创建5.上传本地项目到github6.完结撒花❀❀❀!!! 1.下载git git下载地址:https://git-scm.com/downloads 下载安装后创建快捷地址:(此处比较…

WebRTC技术EasyRTC音视频实时通话驱动智能摄像头迈向多场景应用

一、方案背景​ 在物联网蓬勃发展的当下,智能摄像头广泛应用于安防、家居、工业等领域。但传统智能摄像头存在视频传输延迟高、设备兼容性差、网络波动时传输不稳定等问题,难以满足用户对实时流畅交互视频的需求。EasyRTC凭借低延迟、高可靠、跨平台特性…

java 代码查重(三)常见的距离算法和相似度(相关系数)计算方法

目录 一、几种距离度量方法 【 海明距离 /汉明距离】 【 欧几里得距离(Euclidean Distance) 】 【 曼哈顿距离 】 【 切比雪夫距离 】 【 马氏距离 】 二、相似度算法 【 余弦相似度 】 【 皮尔森相关系数 】 【 Jaccard相似系数 /杰卡德距离】…

LangChain4j入门AI(六)整合提示词(Prompt)

前言 提示词(Prompt)是用户输入给AI模型的一段文字或指令,用于引导模型生成特定类型的内容。通过提示词,用户可以告诉AI“做什么”、 “如何做”以及“输出格式”,从而在满足需求的同时最大程度减少无关信息的生成。有…

redis--redisJava客户端:Jedis详解

在Redis官网中提供了各种语言的客户端,地址: https://redis.io/docs/latest/develop/clients/ Jedis 以Redis命令做方法名称,学习成本低,简单实用,但是对于Jedis实例是线程不安全的(即创建一个Jedis实例&a…

[CSS3]百分比布局

移动端特点 PC和手机 PC端网页和移动端网页的有什么不同? PC屏幕大,网页固定版心手机屏幕小,网页宽度多数为100% 谷歌模拟器 使用谷歌模拟器可以在电脑里面调试移动端的网页 屏幕尺寸 了解屏幕尺寸概念 屏幕尺寸: 指的是屏幕对角线的长度&#xff…

【Java微服务组件】异步通信P2—Kafka与消息

欢迎来到啾啾的博客🐱。 记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。 欢迎评论交流,感谢您的阅读😄。 目录 引言Kafka与消息生产者发送消息到Kafka批处理发送设计消息的幂等信息确保消息送达acks配置…

R语言空间数据处理入门教程

我的课程《R语言空间数据处理入门教程》已重新恢复课程售卖,有需要的读者可以学习。 👇点击下方链接(文末“阅读原文”可直达),立即开启你的空间数据之旅: https://www.bilibili.com/cheese/play/ss13775…

使用zap,对web应用/API接口 做安全检测

https://www.zaproxy.org/getting-started/ 检测方法 docker pull ghcr.io/zaproxy/zaproxy:stable# 执行baseline测试 docker run -t ghcr.io/zaproxy/zaproxy:stable zap-baseline.py \ -t https://baseline.yeshen.org# 执行api测试 docker run -t ghcr.io/zaproxy/zaproxy…

UE5.6新版本—— 动画光照系统重点更新

UE5.6预览版已经可以下载,发布会在下个月的6.5号发布。 5.6界面UI设计 5.6 对引擎进行了大规模的重新设计,先看整体内容,主题UI设计 被调整了位置,左边大多数的选择,框选工具,吸附工具,挪到了左…

腾讯位置服务重构出行行业的技术底层逻辑

位置智能:重构出行行业的技术底层逻辑 在智慧城市建设与交通出行需求爆发的双重驱动下,位置服务正从工具层跃升为出行行业的核心基础设施。腾讯位置服务以“连接物理世界与数字空间”为核心理念,通过构建高精度定位、实时数据融合、智能决策…

如何用JAVA手写一个Tomcat

一、初步理解Tomcat Tomcat是什么? Tomcat 是一个开源的 轻量级 Java Web 应用服务器,核心功能是 运行 Servlet/JSP。 Tomcat的核心功能? Servlet 容器:负责加载、实例化、调用和销毁 Servlet。 HTTP 服务器:监听端口…

使用 Qt QGraphicsView/QGraphicsScene 绘制色轮

使用 Qt QGraphicsView/QGraphicsScene 绘制色轮 本文介绍如何在 Qt 中利用 QGraphicsView 和 QGraphicsScene 实现基础圆形绘制,以及进阶的色轮(Color Wheel)效果。 色轮是色彩选择器的常见控件,广泛应用于图形设计、绘画和 UI …

使用glsl 来做视频矫正

描述、优点 使用glsl来代替opencv的undistort 和 鱼眼矫正,并且最后使用opencv的LUT给glsl 来使用,来达到加速的目的,并且做到和opencv 一模一样的效果,达到实时视频的加速矫正。 优点: 没有cuda,也可以做到实时视频矫正,包含各类板子和amd的cpu,intel核显 矫正的基本作…

03-Web后端基础(Maven基础)

1. 初始Maven 1.1 介绍 Maven 是一款用于管理和构建Java项目的工具,是Apache旗下的一个开源项目 。 Apache 软件基金会,成立于1999年7月,是目前世界上最大的最受欢迎的开源软件基金会,也是一个专门为支持开源项目而生的非盈利性…

蓝桥杯19682 完全背包

问题描述 有 N 件物品和一个体积为 M 的背包。第 i 个物品的体积为 vi​,价值为 wi​。每件物品可以使用无限次。 请问可以通过什么样的方式选择物品,使得物品总体积不超过 M 的情况下总价值最大,输出这个最大价值即可。 输入格式 第一行…

DeepSeek源码解构:从MoE架构到MLA的工程化实现

文章目录 **一、代码结构全景:从模型定义到分布式训练****二、MoE架构:动态路由与稀疏激活的工程化实践****1. 专家路由机制(带负载均衡)****数学原理:负载均衡损失推导** **三、MLA注意力机制:低秩压缩与解…

【VLNs篇】02:NavGPT-在视觉与语言导航中使用大型语言模型进行显式推理

方面 (Aspect)内容总结 (Content Summary)论文标题NavGPT: 在视觉与语言导航中使用大型语言模型进行显式推理 (NavGPT: Explicit Reasoning in Vision-and-Language Navigation with Large Language Models)核心问题探究大型语言模型 (LLM) 在复杂具身场景(特别是视…