微软Azure RTOS ThreadX 官方演示代码

1,051 阅读18分钟

概述

每个 ThreadX 产品分发均包含演示系统,可在所有受支持的微处理器上运行。 此示例系统可在分发文件demo_threadx.c 中定义,旨在说明如何在嵌入式多线程环境中使用 ThreadX。 演示包括初始化、八个线程、一个字节池、一个内存块池、一个队列、一个信号量、一个互斥量和一个事件标志组。

备注: 除线程的堆栈大小外,该演示应用程序在所有 ThreadX 支持的处理器上均相同。 demo_threadx.c 的完整列表,包括在本章其余部分引用的行号。

应用程序定义

完成基本的 ThreadX 初始化后,将执行 tx_application_define 函数。 该函数负责设置所有初始系统资源,包括线程、队列、信号量、互斥体、事件标志和内存池。 演示系统的 tx_application_define(第 60-164 行)按以下顺序创建演示对象:

  • byte_pool_0
  • thread_0
  • thread_1
  • thread_2
  • thread_3
  • thread_4
  • thread_5
  • thread_6
  • thread_7
  • queue_0
  • semaphore_0
  • event_flags_0
  • mutex_0
  • block_pool_0 该演示系统不会创建任何其他附加 ThreadX 对象。 但是,实际应用程序可能会在运行时在执行线程内部创建系统对象。

初始执行

所有线程均使用TX_AUTO_START 选项创建。 因此,这些线程初步准备就绪,可以执行。 tx_application_define 完成后,控制权将转移到线程计划程序,并从该处转移到各个线程。 线程执行的顺序取决于它们的优先级和创建顺序。 在演示系统中,首先执行 thread_0,因为其具有最高优先级(创建时优先级为 1)。 thread_0 挂起后,将执行 thread_5,然后执行 thread_3thread_4thread_6thread_7thread_1 ,最后执行thread_2

备注: 即使 thread_3thread_4 具有相同优先级(创建时优先级均为 8),仍会首先执行 thread_3,因为 thread_3thread_4 之前已创建并准备就绪。具有相同优先级的线程以 FIFO 方式执行。

线程 0

函数thread_0_entry 标记线程的入口点(第 167-190 行)。 Thread_0 是演示系统中要执行的第一个线程。 其处理方式很简单:递增其计数器,休眠 10 个计时器时钟周期,设置一个事件标志以唤醒 thread_5,然后重复该序列。 Thread_0 是系统中优先级最高的线程。 当其请求的休眠过期时,将抢占演示中任何其他正在执行的线程。

线程 1

函数 thread_1_entry 标记线程的入口点(第 193-216 行 )。Thread_1 是演示系统中要执行的倒数第二个线程。其处理方式包括递增其计数器,(通过 queue_0)向 thread_2 发送消息,并重复该序列。请注意,只要 queue_0 已满,thread_1 就会挂起(第 207 行)。

线程 2

函数 thread_2_entry 标记线程的入口点(第 219-243 行)。 Thread_2 是演示系统中要执行的最后一个线程。 其处理方式包括递增其计数器,(通过queue_0)从 thread_1 获取消息,并重复该序列。 请注意,只要queue_0 为空,thread_2 就会挂起(第 233 行)。

尽管 thread_1thread_2 在演示系统中共享最低优先级(优先级为 16),但它们是线程 3 和 4, 也是在大多数时候都可以执行的唯一线程。 它们也是使用时间切片创建的唯一线程(第 87 行和第 93 行)。 在执行另一个线程之前,每个线程最多可执行 4 个计时器时钟周期。

线程 3 和 4

函数 thread_3_and_4_entry 标记 thread_3thread_4的入口点(第 246-280 行)。 这两个线程的优先级均为 8,这使得它们成为演示系统中要执行的第三个和第四个线程。 每个线程的处理方式均相同:递增其计数器,获取 semaphore_0,休眠 2 个计时器时钟周期,释放 semaphore_0,并重复该序列。 请注意,只要 semaphore_0 不可用,所有线程均会挂起(第 264 行)。 同时,两个线程在主处理中使用相同函数。 这不会带来任何问题,因为它们都有自己唯一的堆栈,并且 C 天生是可重入函数。 每个线程通过检查线程输入参数(第 258 行)来确定自己是哪一个线程,该参数可在创建线程时设置(第 102 行和第 109 行)。

备注: 在线程执行过程中获取当前线程点,并将其与控制块的地址进行比较,以确定线程标识,这一操作也很合理。

线程 5

函数 thread_5_entry 标记线程的入口点(第 283-305 行)。 Thread_5 是演示系统中要执行的第二个线程。 其处理方式包括递增其计数器,(通过 event_flags_0)从 thread_0 获取事件标志,并重复该序列。 请注意,只要 event_flags_0 中的事件标志不可用,thread_5 就会挂起(第 298 行)。

线程 6 和 7

函数 thread_6_and_7_entry 标记 thread_6thread_7 的入口点(第 307-358 行)。 这两个线程的优先级均为 8,这使得它们成为演示系统中要执行的第五个和第六个线程。 每个线程的处理方式均相同:递增其计数器,获取 mutex_0 两次,休眠 2 个计时器时钟周期,释放 mutex_0 两次,并重复该序列。 请注意,只要 mutex_0 不可用,所有线程均会挂起(第 325 行)。 同时,两个线程在主处理中使用相同函数。 这不会带来任何问题,因为它们都有自己唯一的堆栈,并且 C 天生是可重入函数。 每个线程通过检查线程输入参数(第 319 行)来确定自己是哪一个线程,该参数可在创建线程时设置(第 126 行和第 133 行)。

观看演示

每个演示线程均会递增自己的唯一计数器。 可以检查以下计数器以核实演示的操作:

  • thread_0_counter
  • thread_1_counter
  • thread_2_counter
  • thread_3_counter
  • thread_4_counter
  • thread_5_counter
  • thread_6_counter
  • thread_7_counter 在演示执行过程中,每个计数器都应继续增加,其中thread_1_counterthread_2_counter_ 的增加速度最快。

分发文件:demo_threadx.c

此部分显示 demo_threadx.c 的完整列表,包括本章中引用的行号。

/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight
threads of different priorities, using a message queue, semaphore, mutex, event flags group,
byte pool, and block pool. */

#include "tx_api.h"

#define DEMO_STACK_SIZE 1024
#define DEMO_BYTE_POOL_SIZE 9120
#define DEMO_BLOCK_POOL_SIZE 100
#define DEMO_QUEUE_SIZE 100

/* Define the ThreadX object control blocks... */

TX_THREAD thread_0;
TX_THREAD thread_1;
TX_THREAD thread_2;
TX_THREAD thread_3;
TX_THREAD thread_4;
TX_THREAD thread_5;
TX_THREAD thread_6;
TX_THREAD thread_7;
TX_QUEUE queue_0;//队列
TX_SEMAPHORE semaphore_0;//信号量
TX_MUTEX mutex_0;//互斥量
TX_EVENT_FLAGS_GROUP event_flags_0;//事件标志组
TX_BYTE_POOL byte_pool_0;//字节池
TX_BLOCK_POOL block_pool_0;//内存块池

/* Define the counters used in the demo application... */

ULONG thread_0_counter;
ULONG thread_1_counter;
ULONG thread_1_messages_sent;
ULONG thread_2_counter;
ULONG thread_2_messages_received;
ULONG thread_3_counter;
ULONG thread_4_counter;
ULONG thread_5_counter;
ULONG thread_6_counter;
ULONG thread_7_counter;

/* Define thread prototypes. */

void thread_0_entry(ULONG thread_input);
void thread_1_entry(ULONG thread_input);
void thread_2_entry(ULONG thread_input);
void thread_3_and_4_entry(ULONG thread_input);
void thread_5_entry(ULONG thread_input);
void thread_6_and_7_entry(ULONG thread_input);


/* Define main entry point. */

int main()
{
    /* Enter the ThreadX kernel. */
    tx_kernel_enter();
}

/* Define what the initial system looks like. */
void tx_application_define(void *first_unused_memory)
{

    CHAR *pointer;

    /* Create a byte memory pool from which to allocate the thread stacks. */
    tx_byte_pool_create(&byte_pool_0,        //指向内存池控制块的指针;
                        "byte pool 0",       //指向内存池名称的指针;
                        first_unused_memory, //内存池的起始地址;
                        DEMO_BYTE_POOL_SIZE);//内存池可用的总字节数,这里是9120;

    /* Put system definition stuff in here, e.g., thread creates and other assorted
        create information. */

    /* Allocate the stack for thread 0. */
    tx_byte_allocate(&byte_pool_0,       //指向之前创建的内存池指针;
                        &pointer,        //指向目标内存的指针;
                        DEMO_STACK_SIZE, //所请求的字节数;
                        TX_NO_WAIT);     //定义没有足够内存可用时的行为方式(设为立即返回);

    /* Create the main thread. */
    tx_thread_create(&thread_0,          //指向线程控制块的指针;
                    "thread 0",          //指向线程名称的指针;
                    thread_0_entry,      //指定线程执行的初始C函数;
                    0,                   //首次执行时传递给线程的入口函数的32值,供应用自由使用;
                    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, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create threads 1 and 2. These threads pass information through a ThreadX
        message queue. It is also interesting to note that these threads have a time
        slice.请注意这里使用了时间切片 */
    tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1,
        pointer, DEMO_STACK_SIZE,
        16, 16, 4, TX_AUTO_START);

    /* Allocate the stack for thread 2. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
        tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2,
        pointer, DEMO_STACK_SIZE,
        16, 16, 4, TX_AUTO_START);

    /* Allocate the stack for thread 3. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore.
        An interesting thing here is that both threads share the same instruction area. */
    tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3,
        pointer, DEMO_STACK_SIZE,
        8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* Allocate the stack for thread 4. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4,
        pointer, DEMO_STACK_SIZE,
        8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* Allocate the stack for thread 5. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create thread 5. This thread simply pends on an event flag, which will be set
        by thread_0. 这个线程由thread_0通过一个事件标志来控制*/
    tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5,
        pointer, DEMO_STACK_SIZE,
        4, 4, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* Allocate the stack for thread 6. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create threads 6 and 7. These threads compete for a ThreadX mutex. 互斥量*/
    tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6,
        pointer, DEMO_STACK_SIZE,
        8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* Allocate the stack for thread 7. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7,
        pointer, DEMO_STACK_SIZE,
        8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* Allocate the message queue. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT);

    /* Create the message queue shared by threads 1 and 2.队列 */
    tx_queue_create(&queue_0,                      //指向消息队列的指针;
                    "queue 0",                     //指向信息队列名称的指针;
                    TX_1_ULONG,                    //指定信息队列中每条消息的大小;
                    pointer,                       //信息队列的起始地址;
                    DEMO_QUEUE_SIZE*sizeof(ULONG));//可用于信息队列的总字节数;

    /* Create the semaphore used by threads 3 and 4. 信号量*/
    tx_semaphore_create(&semaphore_0,       //指向信号量控制块的指针;
                        "semaphore 0",      //指向信号灯名称的指针;
                        1);                 //指定此信号量的初始计数值;

    /* Create the event flags group used by threads 1 and 5.事件标记组 */
    tx_event_flags_create(&event_flags_0,     //指向事件标志组控制块的指针;
                            "event flags 0"); //指向事件标志组名称的指针;

    /* Create the mutex used by thread 6 and 7 without priority inheritance. 互斥量*/
    tx_mutex_create(&mutex_0,                 //指向互斥量控制块的指针;
                    "mutex 0",                //指向互斥量名称的指针;
                    TX_NO_INHERIT);           //指定此信号量是否支持优先级继承(此处设为不支持)

    /* Allocate the memory for a small block pool. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT);

    /* Create a block memory pool to allocate a message buffer from. */
    tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer,
        DEMO_BLOCK_POOL_SIZE);

    /* Allocate a block and release the block memory. */
    tx_block_allocate(&block_pool_0, &pointer, TX_NO_WAIT);

    /* Release the block back to the pool. */
    tx_block_release(pointer);
}

/* Define the test threads. */
void thread_0_entry(ULONG thread_input)
{
    UINT status;


    /* This thread simply sits in while-forever-sleep loop. */
    while(1)
    {

        /* Increment the thread counter. */
        thread_0_counter++;

        /* Sleep for 10 ticks. */
        tx_thread_sleep(10);

        /* Set event flag 0 to wakeup thread 5. */
        status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;
    }
}


void thread_1_entry(ULONG thread_input)
{
    UINT status;


    /* This thread simply sends messages to a queue shared by thread 2. */
    while(1)
    {
        /* Increment the thread counter. */
        thread_1_counter++;

        /* Send message to queue 0. */
        status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER);

        /* Check completion status. */
        if (status != TX_SUCCESS)
            break;

        /* Increment the message sent. */
        thread_1_messages_sent++;
    }
}


void thread_2_entry(ULONG thread_input)
{
    ULONG received_message;
    UINT status;

    /* This thread retrieves messages placed on the queue by thread 1. */
    while(1)
    {
        /* Increment the thread counter. */
        thread_2_counter++;

        /* Retrieve a message from the queue. */
        status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER);

        /* Check completion status and make sure the message is what we
        expected. */
        if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received))
            break;

        /* Otherwise, all is okay. Increment the received message count. */
        thread_2_messages_received++;
    }
}


void thread_3_and_4_entry(ULONG thread_input)
{
    UINT status;


    /* This function is executed from thread 3 and thread 4. As the loop
    below shows, these function compete for ownership of semaphore_0. */
    while(1)
    {
        /* Increment the thread counter. */
        if (thread_input == 3)
            thread_3_counter++;
        else
            thread_4_counter++;

        /* Get the semaphore with suspension. */
        status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;

        /* Sleep for 2 ticks to hold the semaphore. */
        tx_thread_sleep(2);

        /* Release the semaphore. */
        status = tx_semaphore_put(&semaphore_0);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;
    }
}


void thread_5_entry(ULONG thread_input)
{
    UINT status;
    ULONG actual_flags;


    /* This thread simply waits for an event in a forever loop. */
    while(1)
    {
        /* Increment the thread counter. */
        thread_5_counter++;

        /* Wait for event flag 0. */
        status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR,
            &actual_flags, TX_WAIT_FOREVER);

        /* Check status. */
        if ((status != TX_SUCCESS) || (actual_flags != 0x1))
            break;
    }
}

void thread_6_and_7_entry(ULONG thread_input)
{
    UINT status;

    /* This function is executed from thread 6 and thread 7. As the loop
        below shows, these function compete for ownership of mutex_0.互斥量 */
    while(1)
    {
        /* Increment the thread counter. */
        if (thread_input == 6)
            thread_6_counter++;
        else
            thread_7_counter++;

        /* Get the mutex with suspension. */
        status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;

        /* Get the mutex again with suspension. This shows
            that an owning thread may retrieve the mutex it
            owns multiple times. */
        status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;

        /* Sleep for 2 ticks to hold the mutex. */
        tx_thread_sleep(2);

        /* Release the mutex. */
        status = tx_mutex_put(&mutex_0);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;

        /* Release the mutex again. This will actually
            release ownership since it was obtained twice. */
        status = tx_mutex_put(&mutex_0);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;
    }
}

示例代码中的函数说明:

tx_byte_pool_create

创建内存字节池

原型

UINT tx_byte_pool_create(
    TX_BYTE_POOL *pool_ptr,    
    CHAR *name_ptr,            
    VOID *pool_start,         
    ULONG pool_size);         

说明

此服务在指定的区域中创建内存字节池。 最初,池基本上仅包含一个非常大的可用块。 但是,随着不断进行分配,池将分成多个较小的块。

参数

  • pool_ptr:指向内存池控制块的指针。
  • name_ptr:指向内存池名称的指针。
  • pool_start:内存池的起始地址。 起始地址必须与 ULONG 数据类型的大小一致。
  • pool_size:内存池可用的总字节数。

返回值

  • TX_SUCCESS:(0X00) 成功创建内存池。
  • TX_POOL_ERROR:(0x02) 内存池指针无效。 指针为 NULL 或池已创建。
  • TX_PTR_ERROR:(0x03) 池的起始地址无效。
  • TX_SIZE_ERROR:(0x05) 池大小无效。
  • NX_CALLER_ERROR:(0x13) 此服务的调用方无效。

允许来自

初始化和线程

可以抢占

tx_byte_allocate

分配内存的字节

原型

UINT tx_byte_allocate(
    TX_BYTE_POOL *pool_ptr,
    VOID **memory_ptr, 
    ULONG memory_size,
    ULONG wait_option);

说明

此服务从指定的内存字节池中分配指定的字节数。

重要: 请务必确保应用程序代码不在已分配的内存块之外写入。如果发生这种情况,则会损坏其相邻的内存块(通常是后面的内存块)。其结果不可预测,通常很严重!

备注: 此服务的性能是块大小和池中碎片量的函数。因此,在执行的时间关键型线程期间不应使用此服务。

参数

  • pool_ptr:指向之前创建的内存块池的指针。
  • memory_ptr:指向目标内存指针的指针。 成功分配时,已分配内存区域的地址就位于在此参数所指向的位置。
  • memory_size:请求的字节数。
  • wait_option:定义此服务在没有足够内存可用时的行为方式。 等待选项的定义如下:
    • TX_NO_WAIT (0x00000000) - 如果选择 TX_NO_WAIT,则无论此服务是否成功,都会导致立即从此服务返回 。 如果从初始化调用服务,则这是唯一有效的选项。
    • TX_WAIT_FOREVER (0xFFFFFFFF) - 选择 TX_WAIT_FOREVER 会导致发出调用的线程无限期挂起,直到有足够内存可用为止 。
    • 超时值(0x00000001 至 0xFFFFFFFE)- 如果选择一个数值(1 到 0xFFFFFFFE),则会指定在等待内存时发出调用的线程保持挂起的最大计时器时钟周期数。

返回值

  • TX_SUCCESS:(0x00) 成功分配内存。
  • TX_DELETED:(0x01) 线程挂起时删除了内存池。
  • TX_NO_MEMORY:(0X10) 服务无法在指定的等待时间内分配内存。
  • TX_WAIT_ABORTED (0x1A) 挂起状态由其他线程、计时器或 ISR 中止。
  • TX_POOL_ERROR:(0x02) 内存池指针无效。
  • TX_PTR_ERROR:(0x03) 指向目标指针的指针无效。
  • TX_SIZE_ERROR:(0X05) 所请求的大小为零或超过池大小。
  • TX_WAIT_ERROR:(0x04) 从非线程调用时指定了除 TX_NO_WAIT 以外的等待选项。
  • NX_CALLER_ERROR:(0x13) 此服务的调用方无效。

允许来自

初始化和线程

可以抢占

tx_thread_create

创建应用程序线程

原型

UINT tx_thread_create(
    TX_THREAD *thread_ptr,
    CHAR *name_ptr, 
    VOID (*entry_function)(ULONG),
    ULONG entry_input, 
    VOID *stack_start,
    ULONG stack_size, 
    UINT priority,
    UINT preempt_threshold, 
    ULONG time_slice,
    UINT auto_start);

说明

此服务创建一个应用程序线程,该线程在指定的任务入口函数处开始执行。 堆栈、优先级、抢占阈值和时间片都是由输入参数指定的属性。 此外,还指定了线程的初始执行状态。

参数

  • thread_ptr:指向线程控制块的指针。
  • name_ptr:指向线程名称的指针。
  • entry_function:指定线程执行的初始 C 函数。 当线程从此入口函数返回时,它将处于完成状态并无限期挂起。
  • entry_input:首次执行时传递给线程的入口函数的 32 位值。 该输入的用途完全由应用程序决定。
  • stack_start:堆栈的内存区域的起始地址。
  • stack_size:堆栈内存区域中的字节数。 线程的堆栈区域必须足够大,以应对最坏情况下的函数调用嵌套和局部变量使用情况。
  • priority:线程的优先级数值。 合法值的范围为 0 至 (TX_MAX_PRIORITES-1),其中 0 表示最高优先级。
  • preempt_threshold:禁用抢占的最高优先级(0 至 (TX_MAX_PRIORITIES-1))。 只有高于此级别的优先级才允许抢占此线程。 该值必须小于或等于指定的优先级。 如果某值等于线程优先级,则将禁用抢占阈值。
  • time_slice:在优先级相同的其他就绪线程有机会运行之前,允许该线程运行的计时器时钟周期数。 请注意,使用抢占阈值将禁用时间片。 合法时间片值的范围为 1 至 0xFFFFFFFF(含)。 值为 TX_NO_TIME_SLICE(值为 0)将禁用此线程的时间片。

备注 使用时间片会产生少量的系统开销。由于时间片仅适用于多个线程具有相同优先级的情况,因此不应为具有唯一优先级的线程分配时间片。 auto_start:指定线程是立即启动还是置于挂起状态。 合法选项为 TX_AUTO_START (0x01) 和 TX_DONT_START (0x00) 。 如果指定了 TX_DONT_START,应用程序随后必须调用 tx_thread_resume 以便线程运行。

返回值

  • TX_SUCCESS:(0X00) 成功创建线程。
  • TX_THREAD_ERROR:(0x0E) 线程控制指针无效。 指针为 NULL 或已创建线程。
  • TX_PTR_ERROR:(0x03) 入口点的起始地址无效或堆栈区域无效(通常为 NULL)。
  • TX_SIZE_ERROR:(0x05) 堆栈区域大小无效。 线程必须至少有 TX_MINIMUM_STACK 个字节才能执行。
  • TX_PRIORITY_ERROR:(0x0F) 线程优先级无效,该值超出范围(0 至 (TX_MAX_PRIORITIES-1))。
  • TX_THRESH_ERROR:(0x18) 指定的抢占阈值无效。 此值必须是小于或等于线程初始优先级的有效优先级。
  • TX_START_ERROR:(0x10) 自动启动选择无效。
  • NX_CALLER_ERROR:(0x13) 此服务的调用方无效。

允许来自

初始化和线程

可以抢占

tx_queue_create

创建消息队列

原型

UINT tx_queue_create(
    TX_QUEUE *queue_ptr, 
    CHAR *name_ptr,
    UINT message_size,
    VOID *queue_start, 
    ULONG queue_size);

说明

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

备注: 如果队列内存区域中指定的总字节数不能被指定的消息大小整除,则不会使用内存区域中剩余的字节。

参数

  • queue_ptr:指向消息队列控制块的指针。
  • name_ptr:指向消息队列名称的指针。
  • message_size:指定队列中每条消息的大小。 消息大小从 1 个 32 位字到 16 个 32 位字不等。 有效的消息大小选项是从 1 到 16(含)的数值。
  • queue_start:消息队列的起始地址。 起始地址必须与 ULONG 数据类型的大小一致。
  • queue_size:可用于消息队列的总字节数。

返回值

  • TX_SUCCESS:(0X00) 成功创建消息队列。
  • TX_QUEUE_ERROR:(0X09) 消息队列指针无效。 指针为 NULL 或已创建队列。
  • TX_PTR_ERROR:(0x03) 消息队列的起始地址无效。
  • TX_SIZE_ERROR:(0x05) 消息队列大小无效。
  • NX_CALLER_ERROR:(0x13) 此服务的调用方无效。

允许来自

初始化和线程

可以抢占

tx_semaphore_create

创建计数信号量

原型

UINT tx_semaphore_create(
    TX_SEMAPHORE *semaphore_ptr,
    CHAR *name_ptr, 
    ULONG initial_count);

说明

此服务为线程间同步创建信号量。 初始信号量计数指定为输入参数。

参数

  • semaphore_ptr:指向信号量控制块的指针。
  • name_ptr:指向信号量名称的指针。
  • initial_count:指定此信号量的初始计数值。 合法值的范围为 0x00000000 至 0xFFFFFFFF。

返回值

  • TX_SUCCESS:(0X00) 成功创建信号量。
  • TX_SEMAPHORE_ERROR:(0x0C) 信号量指针无效。 指针为 NULL 或已创建信号量。
  • NX_CALLER_ERROR:(0x13) 此服务的调用方无效。

允许来自

初始化和线程

可以抢占

tx_event_flags_create

创建事件标志组

原型

UINT tx_event_flags_create(
    TX_EVENT_FLAGS_GROUP *group_ptr,
    CHAR *name_ptr);

说明

此服务创建一组事件标志(32 个)。 该组中的所有 32 个事件标志都被初始化为零。 每个事件标志由一个位表示。

参数

  • group_ptr:指向事件标志组控制块的指针。
  • name_ptr:指向事件标志组名称的指针。

返回值

  • TX_SUCCESS:(0X00) 成功创建事件组。
  • TX_GROUP_ERROR:(0x06) 事件组指针无效。 指针为 NULL 或事件组已创建。
  • NX_CALLER_ERROR:(0x13) 此服务的调用方无效。

允许来自

初始化和线程

可以抢占

tx_mutex_create

创建互斥信号量

原型

UINT tx_mutex_create(
    TX_MUTEX *mutex_ptr,
    CHAR *name_ptr, 
    UINT priority_inherit);

说明

此服务为线程间互斥创建一个互斥信号量,用于保护资源。

参数

  • mutex_ptr:指向互斥信号量控制块的指针。
  • name_ptr:指向互斥信号量名称的指针。
  • priority_inherit:指定此互斥信号量是否支持优先级继承。 如果此值为 TX_INHERIT,则支持优先级继承。 但是,如果指定了 TX_NO_INHERIT,则此互斥锁不支持优先级继承。

返回值

  • TX_SUCCESS:(0X00) 成功创建互斥信号量。
  • TX_MUTEX_ERROR:(0X1C) 互斥信号量指针无效。 指针为 NULL 或已创建互斥锁。
  • NX_CALLER_ERROR:(0x13) 此服务的调用方无效。
  • TX_INHERIT_ERROR:(0x1F) 优先级继承参数无效。

允许来自

初始化和线程

可以抢占

tx_block_release

释放固定大小的内存块

原型

UINT tx_block_release(VOID *block_ptr);

说明

此服务将以前分配的块释放回其关联的内存池。 如果有一个或多个线程挂起并等待此池中的内存块,则为第一个挂起的线程提供此内存块并恢复该线程。

重要

内存块区域已释放回池中后,应用程序应阻止使用该内存块区域。

参数

  • block_ptr:指向之前分配的内存块的指针。

返回值

  • TX_SUCCESS:(0x00) 成功释放内存块。
  • TX_PTR_ERROR:(0x03) 指向内存块的指针无效。

允许来自

初始化、线程、计时器和 ISR

可以抢占