ThreadX——IPC应用之消息队列

发布者:yunhao最新更新时间:2025-02-28 来源: cnblogs关键字:ThreadX  消息队列 手机看文章 扫描二维码
随时随地手机看文章

一、应用简介

消息队列是RTOS中常用的一种数据通信方式,常用于任务与任务之间或是中断与任务之间的数据传递。在裸机系统中我们通常会使用全局变量的方式进行数据传递,比如在事件发生后在中断中改变数据和设置标志,然后在主循环中轮询不同的标志是否生效来对全局数据执行不同的操作,执行完毕后清除相关标志。但是这种方式需要不断地轮询标志状态,使得CPU的利用率并不高。而使用RTOS的消息队列则具有任务阻塞机制,当没有需要处理的消息时任务挂起等待消息,此时其他任务占用CPU执行其他操作,当有消息放入队列时任务恢复运行进行消息接收和处理。这种消息处理机制相比裸机而言大大地提高了CPU利用率。


ThreadX的消息队列支持“消息置顶通知”功能,也就是可以将消息放在队列的最前面,使得任务可以及时处理某些紧急消息(RT-Thread的消息队列也有该功能)

ThreadX的消息队列可以传递任意长度的数据,因为它是采用传递数据指针的方式(uCOS也是采用这种引用传递的方式,而FreeRTOS和RT-Thread则支持传递整体数据内容。这两种方式各有优劣吧,指针传递方式优点是执行效率高,缺点是存数据的内存区域如果数据还未及时处理就被覆写了那么就会引发问题;整体数据传递方式优点是安全不需担心数据覆写致错,缺点是数据量大的话传递数据过程执行时间长导致效率低)

二、API简介

下面介绍使用ThreadX的消息队列时常用的几个API函数。


1、创建消息队列

描述

该服务用于创建消息队列。 消息总数是根据指定的消息大小和队列中的字节总数来计算的

如果在队列的内存区域中指定的字节总数不能被指定的消息大小均分,则不会使用该内存区域中的其余字节

参数

queue_ptr 指向消息队列控制块的指针

name_ptr 指向消息队列名称的指针

message_size 指定队列中每条消息的大小。 消息大小选项为1个32位字到16个32位字之间(包含)

queue_start 消息队列的起始地址。 起始地址必须与ULONG数据类型的大小对齐

queue_size 消息队列可用的字节总数

返回值

TX_SUCCESS (0x00) 创建成功

TX_QUEUE_ERROR (0x09) 无效的消息队列指针,指针为NULL或队列已创建

TX_PTR_ERROR (0x03) 消息队列的起始地址无效

TX_SIZE_ERROR (0x05) 消息队列大小无效

TX_CALLER_ERROR (0x13) 该服务的调用者无效

UINT tx_queue_create(

    TX_QUEUE *queue_ptr, 

    CHAR *name_ptr,

    UINT message_size,

    VOID *queue_start, 

    ULONG queue_size);

2、删除消息队列

描述

此服务删除指定的消息队列。所有挂起等待此队列消息的线程都将恢复,并给出TX_DELETED返回状态

在删除队列之前,应用程序必须确保已完成(或禁用)此队列的所有send_notify回调。 此外,应用程序必须防止将来使用已删除的队列

应用程序还负责管理与队列相关联的内存区域,该内存区域在此服务完成后可用

参数

queue_ptr 指向先前创建的消息队列的指针

返回值

TX_SUCCESS (0x00) 删除成功

TX_QUEUE_ERROR (0x09) 消息队列指针无效

TX_CALLER_ERROR (0x13) 该服务的调用者无效

UINT tx_queue_delete(TX_QUEUE *queue_ptr);

3、清空消息队列

描述

此服务删除存储在指定消息队列中的所有消息

如果队列已满,将丢弃所有挂起线程的消息,然后恢复每个挂起的线程,并返回一个指示消息发送成功的返回状态。如果队列为空,则此服务不执行任何操作。

参数

queue_ptr 指向先前创建的消息队列的指针

返回值

TX_SUCCESS (0x00) 操作成功

TX_QUEUE_ERROR (0x09) 消息队列指针无效

UINT tx_queue_flush(TX_QUEUE *queue_ptr);

4、消息置顶

描述

该服务将消息发送到指定消息队列的最前面。 消息从源指针指定的存储区域复制到队列的最前面

参数

queue_ptr 指向消息队列控制块的指针

source_ptr 指向存放消息的指针

wait_option 定义消息队列已满时服务的行为

TX_NO_WAIT (0x00000000) - 无论是否成功都立即返回(用于非线程调用,例如中断里面)

TX_WAIT_FOREVER (0xFFFFFFFF) - 一直等待直到消息队列有空闲为止

返回值

TX_SUCCESS (0x00) 操作成功

TX_DELETED (0x01) 线程挂起时,消息队列被删除

TX_QUEUE_FULL (0x0B) 服务无法发送消息,因为在指定的等待时间内队列已满

TX_WAIT_ABORTED (0x1A) 被另一个线程、计时器或ISR中断给中止

TX_QUEUE_ERROR (0x09) 无效的消息队列指针

TX_PTR_ERROR (0x03) 消息的源指针无效

TX_WAIT_ERROR (0x04) 在非线程调用中指定了TX_NO_WAIT以外的等待选项

UINT tx_queue_front_send(

    TX_QUEUE *queue_ptr,

    VOID *source_ptr, 

    ULONG wait_option);

5、获取消息队列信息

描述

该服务检索有关指定消息队列的信息

参数(TX_NULL表示不需要获取该参数代表的信息)

queue_ptr 指向先前创建的消息队列的指针

name 指向目标的指针,用于指向队列名称

enqueued 指向目标的指针,表示当前队列中的消息数

available_storage 指向目标的指针,表示队列当前有空间容纳的消息数

first_suspended 指向目标的指针,该指针指向该队列的挂起列表中第一个线程

suspended_count 指向目标的指针,用于指示当前在此队列上挂起的线程数

next_queue 指向下一个创建队列的指针的目标的指针

返回值

TX_SUCCESS (0x00) 操作成功

TX_QUEUE_ERROR (0x09) 无效的消息队列指针

UINT tx_queue_info_get(

    TX_QUEUE *queue_ptr, 

    CHAR **name,

    ULONG *enqueued, 

    ULONG *available_storage

    TX_THREAD **first_suspended, 

    ULONG *suspended_count,

    TX_QUEUE **next_queue);

6、从队列获取消息

描述

该服务从指定的消息队列中检索消息。 检索到的消息从队列复制到目标指针指定的存储区域。 然后将该消息从队列中删除

指定的目标存储区必须足够大以容纳消息。 也就是说,由destination_ptr 指向的消息目标必须至少与此队列的消息大小一样大。 否则,如果目标不够大,则会在存储区域中发生内存地址非法错误

参数

queue_ptr 指向先前创建的消息队列的指针

destination_ptr 指向储存消息的地址

wait_option 定义消息队列为空时服务的行为

TX_NO_WAIT (0x00000000) - 无论是否成功都立即返回(用于非线程调用,例如中断里面)

TX_WAIT_FOREVER (0xFFFFFFFF) - 一直等待直到有消息可以获取

0x00000001 ~ 0xFFFFFFFE- 指定具体等待心跳节拍数(如果心跳频率1KHZ,那么单位就是ms )

返回值

TX_SUCCESS (0x00) 操作成功

TX_DELETED (0x01) 线程挂起时删除了消息队列

TX_QUEUE_EMPTY (0x0A) 服务无法检索消息,因为队列在指定的等待时间段内为空

TX_WAIT_ABORTED (0x1A) 被另一个线程、计时器或ISR中断给中止

TX_QUEUE_ERROR (0x09) 无效的消息队列指针

TX_PTR_ERROR (0x03) 消息的目标指针无效

TX_WAIT_ERROR (0x04) 在非线程调用中指定了TX_NO_WAIT以外的等待选项

UINT tx_queue_receive(

    TX_QUEUE *queue_ptr,

    VOID *destination_ptr, 

    ULONG wait_option);

7、向队列发送消息

描述

此服务将消息发送到指定的消息队列。发送的消息将从源指针指定的内存区域复制到队列中。

参数

queue_ptr 指向先前创建的消息队列的指针

source_ptr 指向消息的指针

wait_option 定义消息队列已满时服务的行为

TX_NO_WAIT (0x00000000) - 无论是否成功都立即返回(用于非线程调用,例如中断里面)

TX_WAIT_FOREVER (0xFFFFFFFF) - 一直等待直到队列有空位可以放置消息

0x00000001 ~ 0xFFFFFFFE - 指定具体等待心跳节拍数(如果心跳频率1KHZ,那么单位就是ms )

返回值

TX_SUCCESS (0x00) 操作成功

TX_DELETED (0x01) 线程挂起时删除了消息队列

TX_QUEUE_FULL (0x0B) 服务无法发送消息,因为队列在指定的等待时间内已满

TX_WAIT_ABORTED (0x1A) 被另一个线程、计时器或ISR中断给中止

TX_QUEUE_ERROR (0x09) 无效的消息队列指针

TX_PTR_ERROR (0x03) 消息的目标指针无效

TX_WAIT_ERROR (0x04) 在非线程调用中指定了TX_NO_WAIT以外的等待选项

UINT tx_queue_send(

    TX_QUEUE *queue_ptr,

    VOID *source_ptr, 

    ULONG wait_option);

8、注册发送通知回调函数

描述

此服务注册一个通知回调函数,每当一条消息发送到指定的队列时就会调用该函数。 通知回调的处理由应用程序定义

不允许在应用程序的队列发送通知回调函数中调用具有暂停选项的ThreadX API

参数

queue_ptr 指向先前创建的队列的指针

queue_send_notify 指向应用程序队列发送通知功能的指针。 如果此值为TX_NULL,则禁用通知

返回值

TX_SUCCESS (0x00) 操作成功

TX_QUEUE_ERROR (0x09) 无效的队列指针

TX_FEATURE_NOT_ENABLED (0xFF) 禁用了通知功能

UINT tx_queue_send_notify(

    TX_QUEUE *queue_ptr,

    VOID (*queue_send_notify)(TX_QUEUE *));

三、实例演示

该应用实例创建三个任务和一个队列消息发送通知回调

任务1:按键1按一次向消息队列1发送一条消息(单个变量消息)

任务2:按键2按一次向消息队列2发送一条消息(结构体指针消息)

任务3:向消息队列3发送消息;接收任务1和任务2的消息并打印输出消息内容

回调功能:输出消息队列3的相关信息

创建消息队列


#define DEMO_STACK_SIZE         (2 * 1024)

#define DEMO_BYTE_POOL_SIZE     (32 * 1024)


TX_THREAD       thread_0;

TX_THREAD       thread_1;

TX_THREAD       thread_2;


TX_BYTE_POOL    byte_pool_0;

UCHAR           memory_area[DEMO_BYTE_POOL_SIZE];


/* 消息队列 */

TX_QUEUE        tx_queue1;

TX_QUEUE        tx_queue2;

TX_QUEUE        tx_queue3;


ULONG           msg_queue1[32];

ULONG           msg_queue2[16];

ULONG           msg_queue3[8];


struct S_DATA{

    uint32_t id;

    uint16_t flag;

    uint8_t msg[2];

};

struct S_DATA data_package;


void thread_0_entry(ULONG thread_input);

void thread_1_entry(ULONG thread_input);

void thread_2_entry(ULONG thread_input);

void queue3_send_notify(TX_QUEUE *input);

void tx_application_define(void *first_unused_memory)

{

    CHAR    *pointer = TX_NULL;


    /* Create a byte memory pool from which to allocate the thread stacks. */

    tx_byte_pool_create(&byte_pool_0, 'byte pool 0', memory_area, DEMO_BYTE_POOL_SIZE);


    /* Allocate the stack for thread 0. */

    tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create the main thread. */

    tx_thread_create(&thread_0, 'thread 0', thread_0_entry, 0,  

                    pointer, DEMO_STACK_SIZE, 

                    1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);


    /* Allocate the stack for thread 1. */

    tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create threads 1 */

    tx_thread_create(&thread_1, 'thread 1', thread_1_entry, 0,  

                    pointer, DEMO_STACK_SIZE, 

                    2, 2, TX_NO_TIME_SLICE, TX_AUTO_START);


    /* Allocate the stack for thread 2. */

    tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create threads 1 */

    tx_thread_create(&thread_2, 'thread 2', thread_2_entry, 0,  

                    pointer, DEMO_STACK_SIZE, 

                    3, 3, TX_NO_TIME_SLICE, TX_AUTO_START);


    /* 创建消息队列 */

    tx_queue_create(&tx_queue1, 'tx_queue1', 1, msg_queue1, sizeof(msg_queue1));

    tx_queue_create(&tx_queue2, 'tx_queue2', 1, msg_queue2, sizeof(msg_queue2));

    tx_queue_create(&tx_queue3, 'tx_queue2', 1, msg_queue3, sizeof(msg_queue3));

    

    /* 注册发送消息回调 */

    tx_queue_send_notify(&tx_queue3, queue3_send_notify);

}

任务1


void    thread_0_entry(ULONG thread_input)

{

    uint8_t i =0, key_flag = 0;

    uint8_t data_single = 0;


    while(1)

    {

        if (0 == key_flag)

        {

            if (GPIO_PIN_SET == HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin))

            {

                key_flag = 1;

            }

        }

        else

        {

            if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin))

            {

                key_flag = 0;

                /*按键1触发,向队列1发送消息*/

                data_single++;

                tx_queue_send(&tx_queue1, &data_single, TX_NO_WAIT);

            }

        }

        tx_thread_sleep(20);

    }

}

任务2


void    thread_1_entry(ULONG thread_input)

{

    uint8_t key_flag = 0;

    struct S_DATA *pData;


    pData = &data_package;

    pData->id       = 1;

    pData->flag     = 2;

    pData->msg[0]   = 3;

    pData->msg[1]   = 4;


    while(1)

    {

        if (0 == key_flag)

        {

            if (GPIO_PIN_SET == HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin))

            {

                key_flag = 1;

[1] [2]
关键字:ThreadX  消息队列 引用地址:ThreadX——IPC应用之消息队列

上一篇:STM32MP157开发板调试笔记
下一篇:嵌入式开发笔记——调试组件SEGGER_RTT

推荐阅读最新更新时间:2026-03-25 15:12

STM32与FreeRTOS中的消息队列详解
01 一、概述 队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任务间传递信息,实现了任务接收来自其他任务或中断的不固定长度的消息,任务能够从队列里面读取消息,当队列中的消息是空时,读取消息的任务将被阻塞,用户还可以指定阻塞的任务时间 xTicksToWait,在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。 当队列中有新消息时,被阻塞的任务会被唤醒并处理新消息;当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转为就绪态。 消息队列是一种异步的通信方式。通过消息队列服务,任务或中断服务例程可以将一条或多条消息放入消息队列中。同样,
[单片机]
STM32与FreeRTOS中的<font color='red'>消息</font><font color='red'>队列</font>详解
ucos-ii示例5:消息队列测试
环境: 主机:WIN8 开发环境:MDK4.72 ucgui版本:3.90 ucos版本:ucos-ii mcu: stm32f103VE 说明: 本示例中task1时间片为1s,task2时间片为8s。task1每秒往消息队列写入1个数据,task2全部读取出来。所以task2每次能读到8个数据。 注意: 1.消息队列需要一个指针数组指向消息 2.应该有一个固定的消息数组存储消息,不应将局部变量填入发送消息函数 3.QSQPost为先进先出函数(FIFO),QSQPostFornt为后进先出函数(LIFO) 效果图: 源码: #define TASK_STK_SIZE 512
[单片机]
ucos-ii示例5:<font color='red'>消息</font><font color='red'>队列</font>测试
深入探讨《Small RTOS51中消息队列的一处隐患》
摘要:Small RTOS51是一款重要的小型实时内核,消息队列是其提供的重要任务间通信的机制。针对其消息队列实现代码中的缺陷以及可能导致的消息丢失这一严重问题,从操作系统等待与唤醒机制理论的角度出发,剖析Small RTOS51内核在消息队列甚至互斥型信号量等实现机制上的漏洞所在;进一步指出原内核实现方式的修改方法,以及《Small RTOS51中消息队列的一处隐患》作者提出的第2种修改方法的完美实现。 关键词:Small RTOS51 消息队列 唤醒模型 隐患分析 引言   贵刊2005年第7期《Small RTOS51中消息队列的一处隐患》一文,对Small RTOS51V1.12.1版本的消息队列机制进行了周密的分析,
[应用]
LPS音频功放选型及其在IPC应用
随着语音播放、交互功能的不断普及,在各类民用电子产品中,音频模块越来越成为必备的一部分。由于音频部分直接影响终端客户的使用体验,音频功放的设计也需要进行更多的考虑和取舍。市面上产品琳琅满目,各类产品的功能要求和侧重点也不尽相同,设计者需要根据具体应用选择最合适的音频放大器方案,熟悉各种可用的音频放大器类型及其特征也非常重要。以下是一些音频功放的选用要点,帮助您进一步了解其相关的选型和设计。 01 音频功放类型 A类:失真最小,静点工作电流最大,效率最低。 B类:失真较大,静点工作电流最小,效率较高。 AB类:失真中等,静点工作电流中等,效率中等,如LPA4890,LPA4891。 D类:工作原理完全不同的放大器,也称之为数
[嵌入式]
LPS音频功放选型及其在<font color='red'>IPC</font>的<font color='red'>应用</font>
凌华科技推出下一代IPC革新边缘侧行业应用,提供可扩展设计和定制功能模块
支持第12/13 代Intel® Core™ 处理器的MVP-5200 / MVP-6200 AI无风扇模块化计算机 摘要: • 无与伦比的性能:支持第 12/13 代 Intel® Core™ i9/i7/i5/i3 处理器,提供一流的性能。 • 极致的灵活性:模块化和可扩展的设计,提供易于扩展的功能和可选的功能模块,满足特定领域的需求。 • 边缘人工智能就绪平台:内置 GPU 和机器视觉的功能,让凌华科技的MVP-5200 / MVP-6200可以立即部署AI应用。 中国上海 – 2023 年 7月 5日 全球领先的边缘计算解决方案、工业PC和主板提供商,英特尔®合作伙伴联盟钛金会员—
[工业控制]
凌华科技推出下一代<font color='red'>IPC</font>革新边缘侧行业<font color='red'>应用</font>,提供可扩展设计和定制功能模块
IPC摄像机的智能化发展现状及应用趋势
    通过一台设备集成智能、IP、IT等诸多高科技技术与一身,设定多种智能分析策略,联动多种周边外设,形成一个完整的智能系统,对多种场景、多个事件进行自动侦测和提前预警,一定程度上做到事前预防,操作简单,不需要专用的智能平台软件,直接登录IE或客户端即可配置,例如天地伟业的智能分析只需要在IE中打钩开下侦测功能就可以实现。     随着IPC市场份额的逐年放量,高清已经得到普及并形成一套完整的解决方案。而随着智能化、高清化、平台化三驾马车的发力,基于端到端的智能高清平台已经初具规模,IPC智能化的时代已经到来。那么IPC智能化应用的现状以及未来趋势将朝着什么方向发展呢?     一、IPC发展现状     目前市面上
[安防电子]
IPC网络摄像机广泛应用于多个领域 优势明显
    随着社会经济的发展,IPC网络摄像机作为新一代的网络监控设备受到了肯定与青睐,除了提供高速的网络接入外还能取代同轴电缆图像传输线路,将实时图像监控纳入宽带信息网络应用范围,这就使得信息网络代替传统闭路电视成为必然。       IPC网络摄像机在视频监控领域的地位      IPC网络摄像机无疑是现今视频监控领域的第一大主题:一方面,电子、网络技术的快速发展大大提升了其应用价值和适用范围;另一方面,全球安防需求的急速膨胀以及用户越来越高的使用要求也极大刺激了网络摄像机的发展。      IPC网络摄像机只要插在网线上就能用,无需电脑配合,无须视频采集卡,没有监控局域限制,在任何地方打开网页就能监控,并支持12个客户端同时监控
[安防电子]
MSMQ技术在Windows Mobile系统通信中应用
引言    随着3G网络技术、市场和政策的不断成熟,移动终端设备的使用量在急速增加,广泛应用各个领域。Windows Mobile是微软为智能移动终端设备开发的操作系统,Windows Mobile将用户熟悉的桌面Windows体验扩展到了移动设备上。Windows Mobile为移动终端设备之间的数据交换和信息共享提供了简单、安全的解决方案。在Windows Mobile平台上的数据通信技术有Socket、Web Service、MSMQ(微软消息队列技术)。Socket传送数据会自定义传送数据格式并且服务端要使用到多线程接收客户端信息,编程复杂且不好控制;Web Service编程简单易控制,但是只可能调用服务端的函数
[网络通信]
MSMQ技术在Windows Mobile系统通信中<font color='red'>应用</font>
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved