2、ThreadX 操作系统

启动过程

1) 预处理(引用头文件,宏定义,创建全局变量,函数声明)
2) main()中启用ThreadX kernel
3) tx_application_define() 中申请系统资源
4) thread_x_and_x_entry() 中实现线程回调函数

tx_kernel_enter();如下

VOID  _tx_initialize_kernel_enter(VOID)
{

    /* Determine if the compiler has pre-initialized ThreadX.  */
    if (_tx_thread_system_state != TX_INITIALIZE_ALMOST_DONE)
    {

        /* No, the initialization still needs to take place.  */

        /* Ensure that the system state variable is set to indicate 
           initialization is in progress.  Note that this variable is 
           later used to represent interrupt nesting.  */
        _tx_thread_system_state =  TX_INITIALIZE_IN_PROGRESS;

        /* Invoke the low-level initialization to handle all processor specific
           initialization issues.  */
        _tx_initialize_low_level();
    
        /* Invoke the high-level initialization to exercise all of the 
           ThreadX components and the application's initialization 
           function.  */
        _tx_initialize_high_level();
    }

    /* Ensure that the system state variable is set to indicate 
       initialization is in progress.  Note that· this variable is 
       later used to represent interrupt nesting.  */
    _tx_thread_system_state =  TX_INITIALIZE_IN_PROGRESS;

    /* Call the application provided initialization function.  Pass the
       first available memory address to it.  */
    tx_application_define(_tx_initialize_unused_memory);

    /* Set the system state in preparation for entering the thread 
       scheduler.  */
    _tx_thread_system_state =  TX_INITIALIZE_IS_FINISHED;

    /* Enter the scheduling loop to start executing threads!  */
    _tx_thread_schedule();
}

(一)线程thread

ThreadX——线程使用
常用API:

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 到 0xFFFF(含)。值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) 自动启动选择无效。
TX_CALLER_ERROR服务(0x13)无效调用。

tx_thread_delete

UINT tx_thread_delete(TX_THREAD *thread_ptr);

参数:
thread_ptr:指向以前创建的应用程序线程的指针。

返回值:
TX_SUCCESS (0x00) :成功删除线程。
TX_THREAD_ERROR (0x0E) :无效的应用程序线程指针。
TX_DELETE_ERROR (0x11) :指定的线程未处于终止或已完成状态。
TX_CALLER_ERROR服务(0x13) :无效调用。

tx_thread_preemption_change

UINT tx_thread_preemption_change(
TX_THREAD *thread_ptr,
UINT new_threshold,
UINT *old_threshold);
参数:
thread_ptr:指向以前创建的应用程序线程的指针。
new_threshold:新的抢占阈值优先级(0 到 (TX_MAX_PRIORITIES-1)。
old_threshold:指向位置的指针以返回上一个抢占阈值。

返回值:
TX_SUCCESS (0x00) 成功抢占阈值更改。
TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
TX_THRESH_ERROR (0x18) 指定的新抢占阈值不是有效的线程优先级(0 到 (TX_MAX_PRIORITIES-1))以外的值,或大于(优先级较低)的当前线程优先级。
TX_PTR_ERROR (0x03) 无效指针指向以前的抢占保留存储位置。
TX_CALLER_ERROR服务(0x13)无效调用方。

tx_thread_priority_change

UINT tx_thread_priority_change(
TX_THREAD *thread_ptr,
UINT new_priority,
UINT *old_priority);
参数:
thread_ptr :指向以前创建的应用程序线程的指针。
new_priority :新线程优先级(0 到 (TX_MAX_PRIORITIES-1)。
old_priority :指向位置的指针以返回线程的上一个优先级。

返回值 :
TX_SUCCESS (0x00) 成功优先级更改。
TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
TX_PRIORITY_ERROR (0x0F) 指定的新优先级无效(0 到TX_MAX_PRIORITIES-1) 以外的值)。
TX_PTR_ERROR (0x03) 指向上一个优先级存储位置的无效指针。
TX_CALLER_ERROR服务(0x13)无效调用。

tx_thread_relinquish

VOID tx_thread_relinquish(VOID);
以相同或更高的优先级将处理器控制放弃给其他随时可以运行的线程。

tx_thread_reset

UINT tx_thread_reset(TX_THREAD *thread_ptr);

thread_ptr :指向以前创建的线程的指针。

返回值:
TX_SUCCESS (0x00) :成功重置线程。
TX_NOT_DONE (0x20) :指定的线程未处于TX_COMPLETED或者TX_TERMINATED状态。
TX_THREAD_ERROR (0x0E) :无效线程指针。
TX_CALLER_ERROR服务(0x13) :无效调用。

tx_thread_resume

UINT tx_thread_resume(TX_THREAD *thread_ptr);
参数:
thread_ptr指向挂起的应用程序线程的指针。
返回值:
TX_SUCCESS (0x00) 成功线程恢复。
TX_SUSPEND_LIFTED (0x19) 先前设置的延迟悬挂已解除。
TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
TX_RESUME_ERROR (0x12) 指定的线程未挂起,或以前由服务挂起,tx_thread_suspend。

tx_thread_sleep

UINT tx_thread_sleep(ULONG timer_ticks);

timer_ticks挂起调用应用程序线程的计时器数,范围从 0 到 0xFFFF。如果指定了 0,服务将立即返回。
返回值:
TX_SUCCESS (0x00) 成功的线程睡眠。
TX_WAIT_ABORTED (0x1A) 挂起被另一个线程、计时器或 ISR 中止。
TX_CALLER_ERROR (0x13) 服务从非线程调用。

tx_thread_suspend

UINT tx_thread_suspend(TX_THREAD *thread_ptr);

参数:
thread_ptr :指向应用程序线程的指针。

返回值 :
TX_SUCCESS (0x00) 成功线程挂起。
TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
TX_SUSPEND_ERROR (0x14) 指定的线程处于终止或已完成状态。
TX_CALLER_ERROR服务(0x13)无效调用。
tx_thread_terminate

tx_thread_terminate

UINT tx_thread_terminate(TX_THREAD *thread_ptr);

参数:
thread_ptr :指向应用程序线程的指针。

返回值 :
TX_SUCCESS (0x00) 成功线程终止。
TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
TX_CALLER_ERROR服务(0x13)无效调用方。

tx_thread_wait_abort

UINT tx_thread_time_slice_change(TX_THREAD *thread_ptr,
ULONG new_time_slice, ULONG *old_time_slice);

参数:
thread_ptr指向以前创建的应用程序线程的指针。

返回值:
TX_SUCCESS (0x00) 成功线程等待中止。
TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
TX_WAIT_ABORT_ERROR (0x1B) 指定的线程未处于等待状态。

tx_tthrread_prriiorriitty_change

函数原型:
UINT tx_thread_priority_change(
TX_THREAD *thread_ptr,
UINT new_priority,
UINT *old_priority);
函数描述:
函数 tx_thread_priority_change 用于实现 ThreadX 任务优先级的修改。
◆ 第 1 个参数 thread_ptr 是任务句柄,用于区分不同的任务。
◆ 第 2 个参数 new_priority 是新任务优先级(0 至 (TX_MAX_PRIORITIES-1))。0 表示最高优先级。
◆ 第 3 个参数 old_priority 是任务之前的优先级。
注意事项:
1. 允许在任务和定时器组里面调用。
2. 指定任务的抢占阈值自动设置为新的优先级。如果需要新的阈值,则必须在此调用之后使用函数
tx_thread_preemption_change。
3. 第二个参数数值不可大于等于 tx_port.h 文件中的宏定义:
#define TX_MAX_PRIORITIES 配置的数值。

使用举例:

TX_THREAD AppTaskUserIFTCB;
UINT OldPriority;
//优先级将其设置为 6 
tx_thread_priority_change(&AppTaskUserIFTCB, 6, &OldPriority);

ttx_tthrread_iinffo_gett

函数原型:
UINT tx_thread_info_get(
TX_THREAD *thread_ptr,
CHAR **name,
UINT *state,
ULONG *run_count,
UINT *preemption_threshold,
ULONG *time_slice,
TX_THREAD **next_thread,
TX_THREAD **suspended_thread);

函数描述:
函数 tx_thread_info_get 不仅仅可用于获取 ThreadX 任务优先级。还可以获取其它相关信息。
◆ 第 1 个参数 thread_ptr 是任务句柄,用于区分不同的任务。
◆ 第 2 个参数 name 用于获取任务名。
◆ 第 3 个参数 state 用于获取任务状态,可能的值如下所示。
  ⚫ TX_READY (0x00)
  ⚫ TX_COMPLETED (0x01)
  ⚫ TX_TERMINATED (0x02)
  ⚫ TX_SUSPENDED (0x03)
  ⚫ TX_SLEEP (0x04)
  ⚫ TX_QUEUE_SUSP (0x05)
  ⚫ TX_SEMAPHORE_SUSP (0x06)
  ⚫ TX_EVENT_FLAG (0x07)
  ⚫ TX_BLOCK_MEMORY (0x08)
  ⚫ TX_BYTE_MEMORY (0x09)
  ⚫ TX_MUTEX_SUSP (0x0D)
◆ 第 4 个参数 run_count 用于获取任务运行计数。
◆ 第 5 个参数 priority 用于获取任务优先级。
◆ 第 6 个参数 preemption_threshold 用于获取抢占阀值。
◆ 第 7 个参数 time_slice 用于获取时间片。
◆ 第 8 个参数 next_thread 指向下一个创建的任务句柄。
◆ 第 9 个参数 suspended_thread 指向挂起列表中下一个任务句柄。

◆ 返回值
  ⚫ TX_SUCCESS:(0X00) 成功检索任务信息。
  ⚫ TX_THREAD_ERROR:(0x0E) 任务控制指针无效。
注意事项:
◆ 可以在初始化阶段,任务,定时器组和中断服务程序里面调用。
使用举例:

TX_THREAD AppTaskUserIFTCB;
CHAR *name;
UINT state;
ULONG run_count;
UINT priority;
UINT preemption_threshold;
UINT time_slice;
TX_THREAD *next_thread;
TX_THREAD *suspended_thread;
UINT status;
status = tx_thread_info_get(&AppTaskUserIFTCB, 
&name,
&state, 
&run_count,
&priority, 
&preemption_threshold,
&time_slice, 
&next_thread,
&suspended_thread);

threadx_demo

threadx_demo

/*
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-08-15    Psycho_real   the first version
 */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "tx_api.h"
/* USER CODE END Includes */
	......
/* USER CODE BEGIN PV */
TX_THREAD my_thread_1;
TX_THREAD my_thread_2;
uint8_t pData[] = "=========ThreadX=========\n";
uint8_t pData1[] = "I am thread1 ";
uint8_t pData2[] = "I am thread2 ";
/* USER CODE END PV */
	......	
/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void)
{
	......
	
	/* USER CODE BEGIN 2 */

	tx_kernel_enter(); //threadx 入口

	/* USER CODE END 2 */
	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1);
	/* USER CODE END 3 */
}

/* USER CODE BEGIN 4 */
void thread1_entry(ULONG entry_input)
{

	INT count = 0;
	uint8_t init_data[]="start now";
	while (1)
	{

		HAL_UART_Transmit(&huart1, pData1, sizeof(pData1), HAL_MAX_DELAY);
		if(count == 0)
		{
			HAL_UART_Transmit(&huart1, init_data, sizeof(init_data), HAL_MAX_DELAY);
		}
		count++;
		//HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_7|GPIO_PIN_8);

		tx_thread_sleep(1000); // 线程睡眠1000 timer_ticks

	}
}

void thread2_entry(ULONG entry_input)
{
	INT count = 0;
	while (1)
	{
		HAL_UART_Transmit(&huart1, pData2, sizeof(pData2), HAL_MAX_DELAY);
		if (count == 3)
		{
			/*挂起线程1*/
			tx_thread_suspend(&my_thread_1);
		}
		else if (count == 6)
		{
			/*恢复线程1*/
			tx_thread_resume(&my_thread_1);
		}
		else if (count == 9)
		{
			/*终止线程1*/
			tx_thread_terminate(&my_thread_1);
		}
		else if (count == 12)
		{
			/*重置线程1*/
			tx_thread_reset(&my_thread_1);
			/*恢复线程1*/
			tx_thread_resume(&my_thread_1);
		}
		else if (count == 13)
		{
			/*终止线程1-2*/
			tx_thread_terminate(&my_thread_1);
			tx_thread_terminate(&my_thread_2);
		}
		else
		{
			;
		}
		count++;
		tx_thread_sleep(1000); // 线程睡眠500 timer_ticks
	}
}
void my_entry_exit_notify(TX_THREAD *thread_ptr, UINT condition)
{
	uint8_t entry_data[] = " thread1-entry ";
	uint8_t exit_data[] = " thread1-exit ";
	/* Determine if the thread was entered or exited. */

	if (condition == TX_THREAD_ENTRY)
	{
		/* Thread entry! */
		HAL_UART_Transmit(&huart1, entry_data, sizeof(pData2), HAL_MAX_DELAY);
		//HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9);
	}
	if (condition == TX_THREAD_EXIT)
	{
		/* Thread exit! */
		HAL_UART_Transmit(&huart1, exit_data, sizeof(pData2), HAL_MAX_DELAY);
	}

}

void tx_application_define(void *first_unused_memory)
{

	/*线程1*/
	tx_thread_create(&my_thread_1,	//线程控制块指针
			"my_thread1",//线程名字
			thread1_entry,//线程入口函数
			0,//线程入口参数
			first_unused_memory,//线程的起始地址(这里偷懒,没有进行分配,直接使用未用的起始地址)
			1024,//内存区域大小K
			3,//优先级3  (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			3,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);
	/*线程2*/
	tx_thread_create(&my_thread_2,	//线程控制块指针
			"my_thread2",//线程名字
			thread2_entry,//线程入口函数
			0,//线程入口参数
			first_unused_memory+1024,//线程的起始地址+1024 (-被前面线程用掉了)
			1024,//内存区域大小K
			1,//优先级3  (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			1,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);
	/*线程进入和退出时通知*/
	tx_thread_entry_exit_notify(&my_thread_1, my_entry_exit_notify);
}
/* USER CODE END 4 */

	......

(二)信号量semaphore

tx_semaphore_create

UINT tx_semaphore_create(
TX_SEMAPHORE *semaphore_ptr,
CHAR *name_ptr,
ULONG initial_count);
参数:
semaphore_ptr指向信号量控件块的指针。
name_ptr 指向信号量名称的指针。
initial_count指定此信号量的初始计数。法律值的范围为 0x000000000 到 0xFFFF。

返回值:
TX_SUCCESS (0x00) 成功创建信号量。
TX_SEMAPHORE_ERROR (0x0C) 无效信号量指针。指针为 NULL 或已创建信号量。
TX_CALLER_ERROR服务(0x13)无效调用。

tx_semaphore_delete

UINT tx_semaphore_delete(TX_SEMAPHORE *semaphore_ptr);

参数
semaphore_ptr指向以前创建信号量的指针。

返回值:
TX_SUCCESS (0x00) 成功计数信号量删除。
TX_SEMAPHORE_ERROR (0x0C) 无效计数信号量指针。
TX_CALLER_ERROR服务(0x13)无效调用。

tx_semaphore_get

UINT tx_semaphore_get(TX_SEMAPHORE *semaphore_ptr,
ULONG wait_option);

参数:
semaphore_ptr 指向以前创建的计数信号量。
wait_option 定义服务的行为方式(如果没有可用的信号量实例);
即,信号量计数为零。等待选项的定义如下:
TX_NO_WAIT (0x00000000) - TX_NO_WAIT,无论它是否成功,都会立即从该服务返回。如果从非线程调用服务,则这是唯一有效的选项;例如,初始化、计时器或 ISR。
TX_WAIT_FOREVER (0xFFFF) - TX_WAIT_FOREVER,这将导致调用线程无限期挂起,直到信号量实例可用。
超时值 (0x0000001 到 0xFFFFFE) - 选择数值 (1-0xFFFFFFFE) 指定在等待信号量实例时保持挂起计时器的最大数量。

返回值:
TX_SUCCESS (0x00) 成功检索信号量实例。
TX_DELETED (0x01) 计数信号量在线程挂起时被删除。
TX_NO_INSTANCE (0x0D) 服务无法检索计数信号量的实例(信号量计数在指定等待时间内为零)。
TX_WAIT_ABORTED (0x1A) 挂起被另一个线程、计时器或 ISR 中止。
TX_SEMAPHORE_ERROR (0x0C) 无效计数信号量指针。
TX_WAIT_ERROR (0x04) 在非线程的TX_NO_WAIT上指定了除其他帐户以外的等待选项。

tx_semaphore_put

UINT tx_semaphore_put(TX_SEMAPHORE *semaphore_ptr);

参数:
semaphore_ptr指向以前创建的计数信号量控件块的指针。

返回值:
TX_SUCCESS (0x00) 成功放入信号量。
TX_SEMAPHORE_ERROR (0x0C) 到计数信号量的无效指针。
tx_semaphore_put_notify

tx_semaphore_put_notify

UINT tx_semaphore_put_notify(
TX_SEMAPHORE *semaphore_ptr,
VOID (*semaphore_put_notify)(TX_SEMAPHORE *));

参数:
semaphore_ptr指向以前创建信号量的指针。
semaphore_put_notify指向应用程序信号量 put 通知函数的指针。如果此值为TX_NULL,则禁用通知。

返回值:
TX_SUCCESS (0x00) 成功注册信号量放通知。
TX_SEMAPHORE_ERROR (0x0C) 无效信号量指针。
TX_FEATURE_NOT_ENABLED (0xFF) 系统已编译,并禁用了通知功能。

semaphore_demo

/*
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 * semaphore_demo.c
 * Change Logs:
 * Date           Author       Notes
 * 2020年8月29日     	  henji      the first version
 */
	........
	
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "tx_api.h"
/* USER CODE END Includes */

/* USER CODE BEGIN PV */
TX_THREAD Producer;
TX_THREAD Consumer;

/*线程栈大小*/
#define DEMO_STACK_SIZE 1024
/*内存池总大小*/
#define DEMO_BYTE_POOL_SIZE 1024*5
/*内存块池总大小*/
#define DEMO_BLOCK_POOL_SIZE 100
/*内存字节池控制块*/
TX_BYTE_POOL byte_pool_0;

/* 指向内存的指针 */
UCHAR *memory_ptr;

/*信号量  空的篮子*/
TX_SEMAPHORE sem_empty;

/*信号量  临界区*/
TX_SEMAPHORE sem_lock;

/*信号量  满的篮子*/
TX_SEMAPHORE sem_full;

/*信号量状态*/
UINT status;

/* Tracex使用 */
/*跟踪缓冲区的内存大小*/
#define trace_buffer_size 64000
/*要保留在跟踪注册表中的应用程序ThreadX对象的数量*/
#define registry_entries 40
UCHAR trace_buffer_start[trace_buffer_size];
UINT trace_status;
/* USER CODE END PV */

int main(void)
{
	.......
	
	/* USER CODE BEGIN 2 */

	tx_kernel_enter(); //threadx 入口

	/* USER CODE END 2 */

	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1);
}


/* USER CODE BEGIN 4 */
void producer_entry(ULONG entry_input)
{

	while (1)
	{
		/*获取空篮子信号量*/
		status = tx_semaphore_get(&sem_empty, TX_WAIT_FOREVER);
		if (status == TX_SUCCESS)
		{
			/*获取空篮子成功*/;
		}

		/*临界区上锁*/
		tx_semaphore_get(&sem_lock, TX_WAIT_FOREVER);

		/*临界区   生产者生产*/
		HAL_UART_Transmit(&huart1, (uint8_t*) "Producing...",sizeof("Producing..."), HAL_MAX_DELAY);
		/*临界区释放锁*/
		tx_semaphore_put(&sem_lock);

		/*释放满篮子信号量*/
		tx_semaphore_put(&sem_full);

		tx_thread_sleep(20);
	}
}

void consumer_entry(ULONG entry_input)
{
	while (1)
	{
		/*获取满篮子信号量*/
		status = tx_semaphore_get(&sem_full, TX_WAIT_FOREVER);
		if (status == TX_SUCCESS)
		{
			/*获取满篮子成功*/;
		}

		/*临界区上锁*/
		tx_semaphore_get(&sem_lock, TX_WAIT_FOREVER);

		/*临界区 消费者消费*/
		HAL_UART_Transmit(&huart1, (uint8_t*) "Consuming...",sizeof("Consuming..."), HAL_MAX_DELAY);
		/*临界区释放锁*/
		tx_semaphore_put(&sem_lock);

		/*释放空篮子信号量*/
		tx_semaphore_put(&sem_empty);

		tx_thread_sleep(10);
	}
}

void tx_application_define(void *first_unused_memory)
{
	/*使能追踪*/
	trace_status = tx_trace_enable(&trace_buffer_start, trace_buffer_size,registry_entries);

	/*创建一个内存池用于分配线程栈*/
	tx_byte_pool_create(&byte_pool_0, 		//内存池的指针
			"byte pool 0",//名称
			first_unused_memory,//分配内存地址
			DEMO_BYTE_POOL_SIZE//分配内存池大小
			);

	/*分配一个栈空间用于线程1*/
	tx_byte_allocate(&byte_pool_0,		   //内存池的指针
			(VOID**) &memory_ptr,		   //指向目标内存指针的指针
			DEMO_STACK_SIZE,     //分配栈大小
			TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
			);

	/*创建线程1*/
	tx_thread_create(&Producer,	//线程控制块指针
			"my_thread1",//线程名字
			producer_entry,//线程入口函数
			0,//线程入口参数
			memory_ptr,//线程的起始地址
			DEMO_STACK_SIZE,//线程栈大小 K
			2,//优先级3  (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			2,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);

	/*分配一个栈空间用于线程2*/
	tx_byte_allocate(&byte_pool_0,		   //内存池的指针
			(VOID**) &memory_ptr,		   //指向目标内存指针的指针
			DEMO_STACK_SIZE,     //分配栈大小
			TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
			);
	/*线程2*/
	tx_thread_create(&Consumer,	//线程控制块指针
			"my_thread2",//线程名字
			consumer_entry,//线程入口函数
			0,//线程入口参数
			memory_ptr,//线程的起始地址
			DEMO_STACK_SIZE,//线程栈大小 K
			3,//优先级2 (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			3,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);

	/* 篮子数量一个 */
	/*创建信号量 空的篮子 */
	tx_semaphore_create(&sem_empty, "sem_empty", 1);

	/*创建信号量 满的篮子 */
	tx_semaphore_create(&sem_full, "sem_full", 0);

	/*创建信号量 临界区 */
	tx_semaphore_create(&sem_lock, "sem_lock", 1);

}
/* USER CODE END 4 */

(三)互斥量Mutexes

互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

除了信号量外,ThreadX还提供了一个互斥对象。 互斥锁基本上是二进制信号量,这意味着一次只有一个线程可以拥有一个互斥锁。 此外,同一线程可以多次对拥有的互斥锁执行一次成功的互斥锁获取操作,确切地说是4,294,967,295。 互斥对象有两个操作:
tx_mutex_get和tx_mutex_put。 get操作获取不属于另一个线程的互斥锁,而put操作释放先前获取的互斥锁。
为了使线程释放互斥锁,放置操作的数量必须等于先前的获取操作的数量。

每个互斥锁都是公共资源。 ThreadX对使用互斥体没有任何限制。

ThreadX互斥锁仅用于互斥。 与计数信号量不同,互斥锁不能用作事件通知的方法。

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 或已创建互斥。
TX_CALLER_ERROR服务(0x13)无效调用。
TX_INHERIT_ERROR (0x1F) 无效优先级继承参数。

tx_mutex_delete

UINT tx_mutex_delete(TX_MUTEX *mutex_ptr);

参数:
mutex_ptr指向以前创建的互斥的指针。

返回值:
TX_SUCCESS (0x00) 成功静音删除。
TX_MUTEX_ERROR (0x1C) 无效的互斥指针。
TX_CALLER_ERROR服务(0x13)无效调用。

tx_mutex_get

UINT tx_mutex_get(
TX_MUTEX *mutex_ptr,
ULONG wait_option);

参数:
mutex_ptr 指向以前创建的互斥的指针。
wait_option 定义如果互斥已被另一个线程拥有,服务的行为方式。等待选项的定义如下:
1、TX_NO_WAIT (0x00000000) - TX_NO_WAIT,无论它是否成功,都会立即从该服务返回。如果从初始化调用服务,则这是唯一有效的选项。
2、TX_WAIT_FOREVER超时值 (0xFFFF) - TX_WAIT_FOREVER,这将导致调用线程无限期挂起,直到互斥可用。超时值 (0x0000001 到 0xFFFFFE) - 选择数值 (1-0xFFFFFFFE) 指定在等待互斥时保持挂起计时器的最大数量。

返回值:
TX_SUCCESS (0x00) 成功静音获取操作。
TX_DELETED (0x01) Mutex 在线程挂起时被删除。
TX_NOT_AVAILABLE (0x1D) 服务无法在指定时间内获得互斥的所有权以等待。
TX_WAIT_ABORTED (0x1A) 挂起被另一个线程、计时器或 ISR 中止。
TX_MUTEX_ERROR (0x1C) 无效的互斥指针。
TX_WAIT_ERROR (0x04) 在非线程的TX_NO_WAIT上指定了除其他帐户以外的等待选项。
TX_CALLER_ERROR服务(0x13)无效调用。

tx_mutex_put

UINT tx_mutex_put(TX_MUTEX *mutex_ptr);

参数:
mutex_ptr指向以前创建的互斥的指针。

返回值:
TX_SUCCESS (0x00) 成功静音释放。
TX_NOT_OWNED (0x1E) Mutex 不归呼叫者所有。
TX_MUTEX_ERROR (0x1C) 到互斥的无效指针。
TX_CALLER_ERROR服务(0x13)无效调用。

mutex_demo

/*
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 * mutex_demo.c
 * Change Logs:
 * Date           Author       Notes
 * 2020年8月25日     	  henji      the first version
 */
/* USER CODE BEGIN Includes */
#include "tx_api.h"
#include "stdio.h"

/* USER CODE BEGIN PV */
TX_THREAD MyThread_1;
TX_THREAD MyThread_2;

/*线程栈大小*/
#define DEMO_STACK_SIZE 1024
/*内存池总大小*/
#define DEMO_BYTE_POOL_SIZE 1024*5
/*内存块池总大小*/
#define DEMO_BLOCK_POOL_SIZE 100
/*内存字节池控制块*/
TX_BYTE_POOL byte_pool_0;

/* 指向内存的指针 */
UCHAR *memory_ptr;

/*互斥量	临界区*/
TX_MUTEX mutex_lock;

UINT count_A = 0;
UINT count_B = 0;

/* Tracex使用 */
/*跟踪缓冲区的内存大小*/
#define trace_buffer_size 64000
/*要保留在跟踪注册表中的应用程序ThreadX对象的数量*/
#define registry_entries 40
UCHAR trace_buffer_start[trace_buffer_size];
UINT trace_status;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
CHAR buffer[20]; //my_printf缓冲区 
void my_printf(CHAR *s,INT var);

/* USER CODE END PFP */

int main(void)
{
	.......
	/* USER CODE BEGIN 2 */

	tx_kernel_enter(); //threadx 入口

	/* USER CODE END 2 */
	while (1);
}
void MyThread_1_entry(ULONG entry_input)
{

	while (1)
	{
		/*临界区上锁*/
	//	tx_mutex_get(&mutex_lock,TX_WAIT_FOREVER);

		/*临界区 ...*/

		count_A ++;

		/* 让给线程2 使得count_A & count_B 不同步 */
		tx_thread_sleep(1000);

		count_B ++;
		/*临界区释放锁*/
	//	tx_mutex_put(&mutex_lock);

	}
}

void MyThread_2_entry(ULONG entry_input)
{

	while (1)
	{
		/*临界区上锁*/
	//	tx_mutex_get(&mutex_lock,TX_WAIT_FOREVER);

		/*临界区 ...*/

		if(count_A == count_B)
		{
			my_printf("count_A = count_B", HAL_MAX_DELAY);
		}
		else
		{
			my_printf("count_A != count_B", HAL_MAX_DELAY);
		}

		count_A ++;
		count_B ++;

		/*临界区释放锁*/
	//	tx_mutex_put(&mutex_lock);

		tx_thread_sleep(500);
	}
}

void tx_application_define(void *first_unused_memory)
{
	/*使能追踪*/
	trace_status = tx_trace_enable(&trace_buffer_start, trace_buffer_size,registry_entries);

	/*创建一个内存池用于分配线程栈*/
	tx_byte_pool_create(&byte_pool_0, 		//内存池的指针
			"byte pool 0",//名称
			first_unused_memory,//分配内存地址
			DEMO_BYTE_POOL_SIZE//分配内存池大小
			);

	/*分配一个栈空间用于线程1*/
	tx_byte_allocate(&byte_pool_0,		   //内存池的指针
			(VOID**) &memory_ptr,		   //指向目标内存指针的指针
			DEMO_STACK_SIZE,     //分配栈大小
			TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
			);
	/*线程1*/
	tx_thread_create(&MyThread_1,	//线程控制块指针
			"MyThread_1",//线程名字
			MyThread_1_entry,//线程入口函数
			0,//线程入口参数
			memory_ptr,//线程的起始地址
			DEMO_STACK_SIZE,//线程栈大小 K
			3,//优先级3 (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			3,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);
	/*分配一个栈空间用于线程2*/
	tx_byte_allocate(&byte_pool_0,		   //内存池的指针
			(VOID**) &memory_ptr,		   //指向目标内存指针的指针
			DEMO_STACK_SIZE,     //分配栈大小
			TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
			);
	/*线程2*/
	tx_thread_create(&MyThread_2,	//线程控制块指针
			"MyThread_2",//线程名字
			MyThread_2_entry,//线程入口函数
			0,//线程入口参数
			memory_ptr,//线程的起始地址
			DEMO_STACK_SIZE,//线程栈大小 K
			1,//优先级1 (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			1,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);

	/*创建互斥量 临界区 */
	tx_mutex_create(&mutex_lock,"mutex_lock",TX_NO_INHERIT);

}

void my_printf(CHAR *s,INT var)
{
	if(var == HAL_MAX_DELAY)
	{
		sprintf(buffer,s);
		HAL_UART_Transmit(&huart1, (uint8_t*)buffer, sizeof(buffer), HAL_MAX_DELAY);
	}
	else
	{
		sprintf(buffer,s,var);
		HAL_UART_Transmit(&huart1, (uint8_t*)buffer, sizeof(buffer), HAL_MAX_DELAY);
	}
}
/* USER CODE END 4 */

没有使用互斥量的时候,在线程1 tx_thread_sleep(1000); 挂起的时候,线程2操作变量使得 count_A 与 count_B 不同步

(四)事件集event

tx_event_flags_create

UINT tx_event_flags_create(
TX_EVENT_FLAGS_GROUP *group_ptr,
CHAR *name_ptr);
参数:
group_ptr 指向事件标志组控件块的指针。
name_ptr 指向事件标志组的名称。

返回值:
TX_SUCCESS (0x00) 成功创建事件组。
TX_GROUP_ERROR (0x06) 无效事件组指针。指针为 NULL 或已创建事件组。
TX_CALLER_ERROR服务(0x13)无效调用。

注意:
初始状态,32个标志位都是0。
只能在初始化和任务中调用。

tx_event_flags_delete

UINT tx_event_flags_delete(TX_EVENT_FLAGS_GROUP *group_ptr);

参数:
group_ptr指向以前创建的事件标志组。

返回值:
TX_SUCCESS (0x00) 成功事件标志组删除。
TX_GROUP_ERROR (0x06) 无效事件标志组指针。
TX_CALLER_ERROR服务(0x13)无效调用。

tx_event_flags_get

UINT tx_event_flags_get(TX_EVENT_FLAGS_GROUP *group_ptr,
ULONG requested_flags, UINT get_option,
ULONG *actual_flags_ptr, ULONG wait_option);

参数:
group_ptr 指向以前创建的事件标志组。
requested_flags 32 位未签名的变量,表示请求的事件标志。
get_option 指定是否需要所有或任何请求的事件标志。以下是有效的选择:
TX_AND (0x02)
TX_AND_CLEAR (0x03)
TX_OR (0x00)
TX_OR_CLEAR (0x01)
函数描述:

注释:
1、 第1个参数是事件标志组控制块。
2、 第2个参数是请求的事件标志,32bit数据值,每个bit代表一个标志位。
3、 第3个参数支持如下四种参数

TX_AND
等待第2个参数所有请求置1的bit全部被置1。

TX_AND_CLEAR
等待第2个参数所有请求置1的bit全部被设置,之后相应的bit将被清零。

TX_OR
等待第2个参数所有请求置1的bit中任何一个bit被置1。

TX_OR_CLEAR
等待第2个参数所有请求置1的bit中任何一个bit被置1,之后相应的bit将被清零。

推荐:
这个函数的第3个参数推荐配置为TX_AND_CLEAR或者TX_OR_CLEAR,否则用户满足条件时,函数tx_event_flags_get会一直成立。

4、 第4个参数是32bit事件标志的实际数值(未被清零前的数值)。

5、 第5个参数是等待选项

TX_NO_WAIT (0x00000000),表示不管是否满足等待条件,立即返回。如果在定时器组,初始化或中断里面调用,必须要设置成这个参数。
TX_WAIT_FOREVER (0xFFFFFFFF),表示永久等待,直到满足等待条件。
等待时间,范围0x00000001 到 0xFFFFFFFE,单位系统时钟节拍、

返回值:
TX_SUCCESS (0x00) 成功事件标志。
TX_DELETED时,删除了第 0x01 个事件标志组。
TX_NO_EVENTS (0x07) 服务无法在指定时间内获取指定的事件等待。
TX_WAIT_ABORTED (0x1A) 挂起被另一个线程、计时器或 ISR 中止。
TX_GROUP_ERROR (0x06) 无效事件标志组指针。
TX_PTR_ERROR事件标志的"0x03"无效指针。
TX_WAIT_ERROR (0x04) 在从非TX_NO_WAIT的呼叫上指定的等待选项(非线程) 以外的等待选项。
TX_OPTION_ERROR (0x08) 无效获取选项。

tx_event_flags_set

UINT tx_event_flags_set(
TX_EVENT_FLAGS_GROUP *group_ptr,
ULONG flags_to_set,UINT set_option);

参数:
group_ptr 指向以前创建的事件标志组控件块的指针。
flags_to_set 指定要根据所选的设置选项设置或清除的事件标志。
set_option 指定的事件标志是 ANDed 还是 ORed 进入组的当前事件标志。

注释:
此函数用于设置事件标志。
1、 第1个参数是事件标志组控制块。
2、 第2个参数根据第3个参数的类型,用于设置或者清楚标志位。32bit数值,每个bit代表一个标志位。
3、 第3个参数支持如下两种参数:
TX_AND (0x02)
TX_OR (0x00)

参数:TX_AND,与操作。
表示第2个参数的设置值与事件标志当前32bit数值的与操作。比如第2个参数设置为0x0000 0003,表示事件标志组32bit变量的bit0和bit1保持不变,其它全部清零。

TX_OR,或操作。
表示第2个参数的设置值与事件标志当前32bit数值的或操作。比如第2个参数设置为0x0000 0003,表示事件标志组32bit变量的bit0和bit1全部置1,其它bit不变。

返回值:
TX_SUCCESS (0x00) 成功事件标志集。
TX_GROUP_ERROR (0x06) 到事件标志组的无效指针。
TX_OPTION_ERROR (0x08) 指定的无效设置选项。

tx_event_flags_set_notify

UINT tx_event_flags_set_notify(
TX_EVENT_FLAGS_GROUP *group_ptr,
VOID (*events_set_notify)(TX_EVENT_FLAGS_GROUP *));

参数:
group_ptr 指向以前创建的事件标志组。
events_set_notify 指向应用程序的事件标志设置通知函数。如果此值为TX_NULL,则禁用通知。

返回值:
TX_SUCCESS (0x00) 成功注册事件标志集通知。
TX_GROUP_ERROR (0x06) 无效事件标志组指针。
TX_FEATURE_NOT_ENABLED (0xFF) 系统已编译,并禁用了通知功能。

Event _Flags_demo_1

/*
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 * Event _Flags_demo.c
 * Change Logs:
 * Date           Author       Notes
 * 2020年8月25日     	  henji      the first version
 */
/* USER CODE BEGIN Includes */
#include "tx_api.h"
#include "stdio.h"

/* USER CODE BEGIN PV */
TX_THREAD MyThread_1;
TX_THREAD MyThread_2;

/*线程栈大小*/
#define DEMO_STACK_SIZE 1024
/*内存池总大小*/
#define DEMO_BYTE_POOL_SIZE 1024*5
/*内存块池总大小*/
#define DEMO_BLOCK_POOL_SIZE 100
/*内存字节池控制块*/
TX_BYTE_POOL byte_pool_0;

/* 指向内存的指针 */
UCHAR *memory_ptr;

/* 事件组 */
TX_EVENT_FLAGS_GROUP event_flags_0;

UINT count_A = 0;
UINT count_B = 0;
CHAR buffer[64];
/* Tracex使用 */
/*跟踪缓冲区的内存大小*/
#define trace_buffer_size 64000
/*要保留在跟踪注册表中的应用程序ThreadX对象的数量*/
#define registry_entries 40
UCHAR trace_buffer_start[trace_buffer_size];
UINT trace_status;
/* USER CODE END PV */
	......
int main(void)
{
	........
	/* USER CODE BEGIN 2 */

	tx_kernel_enter(); //threadx 入口

	/* USER CODE END 2 */

	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1);
}

void MyThread_1_entry(ULONG entry_input)
{
	ULONG status;
	ULONG actual_flags;
	while (1)
	{
        //MyThread_1_entry比 MyThread_2_entry值更小优先级更高
		status = tx_event_flags_get(&event_flags_0, //事件控制块
						   	   	   0x00000111,	   //事件1、4、8标志
								   TX_AND_CLEAR,   //事件1、4、8同时置位触发,且清零
								   &actual_flags,  //保存复位前的状态
								   5 //超时2 ticks
									);
		if(status == TX_SUCCESS)
		{
			HAL_UART_Transmit(&huart1, (uint8_t *)"Event flag: 1 & 4 & 8 ",sizeof("Event flag: 1 & 4 & 8 "),HAL_MAX_DELAY);
			tx_event_flags_set(&event_flags_0, //事件组控制块
							   0x00000001, 	   //设置事件1标志位
					           TX_OR           //TX_OR 当前置位
							  );
		}


		status = tx_event_flags_get(&event_flags_0, //事件控制块
						   	   	   0x00000001,	   //事件1标志
								   TX_AND_CLEAR,   //事件1置位触发,且不清零
								   &actual_flags,  //保存复位前的状态
								   5 //超时20 ticks
									);
		if(status == TX_SUCCESS)
		{
			HAL_UART_Transmit(&huart1, (uint8_t *)"Event flag: 1 ",sizeof("Event flag: 1 "),HAL_MAX_DELAY);
			tx_event_flags_set(&event_flags_0, //事件组控制块
							   0x00000011, 	   //设置事件1、4标志位
					           TX_OR           //TX_OR 当前置位
							  );
		}

		tx_thread_sleep(5);
	}
}

void MyThread_2_entry(ULONG entry_input)
{
	    ULONG status;
		ULONG actual_flags;
		while (1)
		{

			status = tx_event_flags_get(&event_flags_0, //事件控制块
							   	   	   0x00000011,	   //事件1标志
									   TX_AND_CLEAR,   //事件1置位触发,且清零
									   &actual_flags,  //保存复位前的状态
									   5 //超时2 ticks
										);
			if(status == TX_SUCCESS)
			{
				HAL_UART_Transmit(&huart1, (uint8_t *)"Event flag: 1 & 4 ",sizeof("Event flag: 1 & 4 "),HAL_MAX_DELAY);
				tx_event_flags_set(&event_flags_0, //事件组控制块
								   0x00000111, 	   //设置事件8标志位
						           TX_OR           //TX_OR 当前置位
								  );
			}
			tx_thread_sleep(5);

		}

}

void tx_application_define(void *first_unused_memory)
{
	/*使能追踪*/
	trace_status = tx_trace_enable(&trace_buffer_start, trace_buffer_size,registry_entries);

	/*创建一个内存池用于分配线程栈*/
	tx_byte_pool_create(&byte_pool_0, 		//内存池的指针
			"byte pool 0",//名称
			first_unused_memory,//分配内存地址
			DEMO_BYTE_POOL_SIZE//分配内存池大小
			);

	/*分配一个栈空间用于线程1*/
	tx_byte_allocate(&byte_pool_0,		   //内存池的指针
			(VOID**) &memory_ptr,		   //指向目标内存指针的指针
			DEMO_STACK_SIZE,     //分配栈大小
			TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
			);
	/*线程1*/
	tx_thread_create(&MyThread_1,	//线程控制块指针
			"MyThread_1",//线程名字
			MyThread_1_entry,//线程入口函数
			0,//线程入口参数
			memory_ptr,//线程的起始地址
			DEMO_STACK_SIZE,//线程栈大小 K
			1,//优先级1 (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			1,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);
	/*分配一个栈空间用于线程2*/
	tx_byte_allocate(&byte_pool_0,		   //内存池的指针
			(VOID**) &memory_ptr,		   //指向目标内存指针的指针
			DEMO_STACK_SIZE,     //分配栈大小
			TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
			);
	/*线程2*/
	tx_thread_create(&MyThread_2,	//线程控制块指针
			"MyThread_2",//线程名字
			MyThread_2_entry,//线程入口函数
			0,//线程入口参数
			memory_ptr,//线程的起始地址
			DEMO_STACK_SIZE,//线程栈大小 K
			3,//优先级3 (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			3,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);

	/* 创建事件组 */
	tx_event_flags_create(&event_flags_0,"event flags 0");

	/* 一个事件组包含32个事件,0 ~ 31 */
	/* 一位十六进制 表示 四位二进制 ,也就是说一位十六进制 涵盖了 4 个 事件*/
	/* 0x00000001   表示事件1*/
	/* 0x00000011   表示事件1和4*/
	/* 0x????????*/

	tx_event_flags_set(&event_flags_0, //事件组控制块
					   0x00000111, 	   //设置事件1、4、8标志位
			           TX_OR           //TX_OR 当前置位 、 TX_AND 当前复位 相当于取反事件(除去0、4、8)
					  );


}

Event _Flags_demo_2

static UINT channel_send_status;
TX_EVENT_FLAGS_GROUP           Channel_Send_Event;

tx_event_flags_create(&Channel_Send_Event,"Channel_Send_Event");//creat

tx_event_flags_set(&Channel_Send_Event,0x01,TX_OR); //set bit0 |00 0001
tx_event_flags_set(&Channel_Send_Event,0x02,TX_OR); //set bit1 |00 0010
tx_event_flags_set(&Channel_Send_Event,0x04,TX_OR); //set bit2 |00 0100
tx_event_flags_set(&Channel_Send_Event,0x08,TX_OR); //set bit3 |00 1000
tx_event_flags_set(&Channel_Send_Event,0x10,TX_OR); //set bit4 |01 0000
tx_event_flags_set(&Channel_Send_Event,0x20,TX_OR); //set bit5 |01 0000

channel_send_status=tx_event_flags_get(&Channel_Send_Event,0x3f, //获取到bit0-bit6的任何一个值
					TX_OR,&channel_send_actual_event,
					TX_WAIT_FOREVER);
if (channel_send_status==TX_SUCCESS)
{
	if (channel_send_actual_event & 0x1)
	{
		//do something
	  tx_event_flags_set(&Channel_Send_Event,0xfffffffE,TX_AND); //clear  bit0即:&11 1110
	}
	else if (channel_send_actual_event & 0x2)
	{
		tx_event_flags_set(&Channel_Send_Event,0xfffffffd,TX_AND);//clear bit1即:&11 1101
	}
	else if (channel_send_actual_event & 0x4)
	{
		tx_event_flags_set(&Channel_Send_Event,0xfffffffb,TX_AND);//clear bit2即:&11 1011
	}
	else if (channel_send_actual_event & 0x8)
	{
		tx_event_flags_set(&Channel_Send_Event,0xfffffff7,TX_AND);//clear bit3即:&11 0111
	}
	else if (channel_send_actual_event & 0x10)
	{
		tx_event_flags_set(&Channel_Send_Event,0xffffffef,TX_AND);//clear bit4即:&10 1111
	}
	else if (channel_send_actual_event & 0x20)
	{
		tx_event_flags_set(&Channel_Send_Event,0xffffffdf,TX_AND);//clear bit5即:&01 1111
	}
}


(五)消息队列queue

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) 消息队列的大小无效。
TX_CALLER_ERROR服务(0x13)无效调用。

tx_queue_delete

UINT tx_queue_delete(TX_QUEUE *queue_ptr);

参数:
queue_ptr 指向以前创建的消息队列的指针。

返回值:
TX_SUCCESS (0x00) 成功删除消息队列。
TX_QUEUE_ERROR (0x09) 无效消息队列指针。
TX_CALLER_ERROR服务(0x13)无效调用。

x_queue_flush

UINT tx_queue_flush(TX_QUEUE *queue_ptr);
参数:
queue_ptr 指向以前创建的消息队列的指针。

返回值:
TX_SUCCESS (0x00) 成功消息队列刷新。
TX_QUEUE_ERROR (0x09) 无效消息队列指针。

tx_queue_front_send

UINT tx_queue_front_send(
TX_QUEUE *queue_ptr,
VOID *source_ptr,
ULONG wait_option);

参数
queue_ptr 指向消息队列控件块的指针。
source_ptr 指向消息的指针。
wait_option 定义如果消息队列已满,服务的行为方式。等待选项的定义如下:
TX_NO_WAIT (0x00000000) - TX_NO_WAIT,无论它是否成功,都会立即从该服务返回。如果从非线程调用服务,则这是唯一有效的选项;例如,初始化、计时器或 ISR。
TX_WAIT_FOREVER (0xFFFF) - TX_WAIT_FOREVER,这将导致调用线程无限期挂起,直到队列中存在空间。
超时值 (0x0000001 到 0xFFFFFFFE) - 选择数值 (1-0xFFFFFFFE) 指定在等待队列中的房间时保持挂起的计时器刻度的最大数量。

返回值:
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上指定了除其他帐户以外的等待选项。

tx_queue_receive

UINT tx_queue_receive(
TX_QUEUE *queue_ptr,
VOID *destination_ptr,
ULONG wait_option);

参数:
queue_ptr 指向以前创建的消息队列。
destination_ptr 复制邮件的位置。
wait_option 定义如果消息队列为空,服务的行为方式。等待选项的定义如下:
TX_NO_WAIT (0x00000000) - TX_NO_WAIT,无论它是否成功,都会立即从该服务返回。如果从非线程调用服务,则这是唯一有效的选项;例如,初始化、计时器或 ISR。
TX_WAIT_FOREVER (0xFFFF) - TX_WAIT_FOREVER调用线程将无限期挂起,直到消息可用。
超时值 (0x0000001 到 0xFFFFFFFE) - 选择数值 (1-0xFFFFFFFE) 指定在等待消息时保持挂起计时器刻度的最大数量。

返回值:
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的呼叫上指定的等待选项(非线程) 以外的等待选项。

tx_queue_send

UINT tx_queue_send(
TX_QUEUE *queue_ptr,
VOID *source_ptr,
ULONG wait_option);

参数:
queue_ptr 指向以前创建的消息队列。
source_ptr 指向消息的指针。
wait_option 定义如果消息队列已满,服务的行为方式。等待选项的定义如下:
TX_NO_WAIT (0x00000000) - TX_NO_WAIT,无论它是否成功,都会立即从该服务返回。如果从非线程调用服务,则这是唯一有效的选项;例如,初始化、计时器或 ISR。
TX_WAIT_FOREVER (0xFFFF) - TX_WAIT_FOREVER,这将导致调用线程无限期挂起,直到队列中存在空间。
超时值 (0x0000001 到 0xFFFFFFFE) - 选择数值 (1-0xFFFFFFFE) 指定在等待队列中的房间时保持挂起的计时器刻度的最大数量。

返回值:
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的呼叫上指定的等待选项(非线程) 以外的等待选项。

tx_queue_send_notify

UINT tx_queue_send_notify(
TX_QUEUE *queue_ptr,
VOID (*queue_send_notify)(TX_QUEUE *));

参数:
queue_ptr 指向以前创建的队列的指针。
queue_send_notify 指向应用程序的队列发送通知函数的指针。如果此值为TX_NULL,则禁用通知。

返回值:
TX_SUCCESS (0x00) 成功注册队列发送通知。
TX_QUEUE_ERROR (0x09) 无效队列指针。
TX_FEATURE_NOT_ENABLED (0xFF) 系统已编译,并禁用了通知功能。

Queue_demo

/*
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 * Queue_demo.c
 * Change Logs:
 * Date           Author       Notes
 * 2020年8月28日     	  henji      the first version
 */

/* USER CODE BEGIN Includes */
#include "tx_api.h"
#include "stdio.h"


/* USER CODE BEGIN PV */
TX_THREAD MyThread_1;
TX_THREAD MyThread_2;


/*线程栈大小*/
#define DEMO_STACK_SIZE 1024
/*内存池总大小*/
#define DEMO_BYTE_POOL_SIZE 1024*5
/*内存块池总大小*/
#define DEMO_BLOCK_POOL_SIZE 100
/*内存字节池控制块*/
TX_BYTE_POOL byte_pool_0;

/* 指向内存的指针 */
UCHAR *memory_ptr;

/* 消息队列 */
TX_QUEUE my_queue;

/* Tracex使用 */
/*跟踪缓冲区的内存大小*/
#define trace_buffer_size 64000
/*要保留在跟踪注册表中的应用程序ThreadX对象的数量*/
#define registry_entries 40
UCHAR trace_buffer_start[trace_buffer_size];
UINT trace_status;
/* USER CODE END PV */

int main(void)
{
	/* USER CODE BEGIN 2 */

	tx_kernel_enter(); //threadx 入口

	/* USER CODE END 2 */

	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1);
}

void MyThread_1_entry(ULONG entry_input)
{
    ULONG status;
	INT thread_1_send = 'A';
	INT special_send = 'Z';
	CHAR buf_send[19];
	while (1)
	{

		/* 紧急消息 */
		if(thread_1_send == 'C')
		{
			status = tx_queue_front_send(&my_queue,      //消息队列控制块
								         &special_send, //发送消息内容指针
									     TX_WAIT_FOREVER //无限等待
									     );
					if(status == TX_SUCCESS)
					{
						sprintf(buf_send,"special_send %c ",special_send);
						HAL_UART_Transmit(&huart1, (uint8_t*)buf_send, sizeof(buf_send), HAL_MAX_DELAY);
					}

		}
		else if(thread_1_send == 'I')
		{
			/* 到 'I' 的时候就结束进程  */
			tx_thread_terminate(&MyThread_1);
		}
		else
		{
			status = tx_queue_send(&my_queue,      //消息队列控制块
								   &thread_1_send, //发送消息内容指针
								   TX_WAIT_FOREVER //无限等待
								   );
					if(status == TX_SUCCESS)
					{
						sprintf(buf_send,"thread_1_send %c ",thread_1_send);
						HAL_UART_Transmit(&huart1, (uint8_t*)buf_send, sizeof(buf_send), HAL_MAX_DELAY);
					}
		}

		thread_1_send++;

		tx_thread_sleep(500);
	}
}

void MyThread_2_entry(ULONG entry_input)
{
	    ULONG status;
		INT thread_2_received;
		CHAR buf_rec[21];
		while (1)
		{
			status = tx_queue_receive(&my_queue, //消息队列控制块
								      &thread_2_received,//发送消息内容指针
					                  TX_WAIT_FOREVER //无限等待
					              );
			if(status == TX_SUCCESS)
			{
				sprintf(buf_rec,"thread_2_received %c ",thread_2_received);
				HAL_UART_Transmit(&huart1, (uint8_t*)buf_rec, sizeof(buf_rec), HAL_MAX_DELAY);
			}
			tx_thread_sleep(2000);

		}

}

void tx_application_define(void *first_unused_memory)
{
	/*使能追踪*/
	trace_status = tx_trace_enable(&trace_buffer_start, trace_buffer_size,registry_entries);

	/*创建一个内存池用于分配线程栈*/
	tx_byte_pool_create(&byte_pool_0, 		//内存池的指针
			"byte pool 0",//名称
			first_unused_memory,//分配内存地址
			DEMO_BYTE_POOL_SIZE//分配内存池大小
			);

	/*分配一个栈空间用于线程1*/
	tx_byte_allocate(&byte_pool_0,		   //内存池的指针
			(VOID**) &memory_ptr,		   //指向目标内存指针的指针
			DEMO_STACK_SIZE,     //分配栈大小
			TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
			);
	/*线程1*/
	tx_thread_create(&MyThread_1,	//线程控制块指针
			"MyThread_1",//线程名字
			MyThread_1_entry,//线程入口函数
			0,//线程入口参数
			memory_ptr,//线程的起始地址
			DEMO_STACK_SIZE,//线程栈大小 K
			1,//优先级1 (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			1,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);
	/*分配一个栈空间用于线程2*/
	tx_byte_allocate(&byte_pool_0,		   //内存池的指针
			(VOID**) &memory_ptr,		   //指向目标内存指针的指针
			DEMO_STACK_SIZE,     //分配栈大小
			TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
			);
	/*线程2*/
	tx_thread_create(&MyThread_2,	//线程控制块指针
			"MyThread_2",//线程名字
			MyThread_2_entry,//线程入口函数
			0,//线程入口参数
			memory_ptr,//线程的起始地址
			DEMO_STACK_SIZE,//线程栈大小 K
			3,//优先级3 (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			3,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);

	/*分配一个空间用于消息队列*/
		tx_byte_allocate(&byte_pool_0,//内存池的指针
				(VOID**) &memory_ptr, //指向目标内存指针的指针
				100,   //分配消息队列大小 100字节
				TX_NO_WAIT //无论它是否成功,都会立即从该服务返回
				);
		/* 创建消息队列 */
		tx_queue_create(&my_queue, //消息队列控制块
				"my_queue",		   //消息队列名称
				4,				   //每一个消息大小
				memory_ptr,		   //消息队列地址
				100				   //总大小100
				);


}
/* USER CODE END 4 */

thread_1 发送 ‘A’ —> ‘H’ 500 ticks
thread_1 发送 ‘C’ 的时候转变位紧急消息
thread_1 发送 ‘I’ 的时候终止进程

thread_2 接受 2000 ticks

(六)定时器TIMER

tx_timer_create

UINT tx_timer_create(
TX_TIMER *timer_ptr,
CHAR *name_ptr,
VOID (*expiration_function)(ULONG),
ULONG expiration_input,
ULONG initial_ticks,
ULONG reschedule_ticks,
UINT auto_activate);

功能:创建定时器

参数:
timer_ptr 指向计时器控制块的指针
name_ptr 指向计时器名称的指针。
expiration_function 当计时器过期时要调用的应用程序函数。
expiration_input 当计时器过期时,输入将传递到过期函数。
initial_ticks 指定计时器过期的初始刻度数。值的范围为 1 到 0xFFFF。
reschedule_ticks 指定第一个计时器过期后的所有计时器的刻度数。此参数的零使计时器成为一次计时器。否则,对于定期计时器,合法值的范围为 1 到 0xFFFF。
注意 一次计时器过期后,必须通过一个tx_timer_change重置,然后才能再次激活。
auto_activate 确定计时器在创建过程中是否自动激活。如果此值为
TX_AUTO_ACTIVATE (0x01),则计时器将处于活动状态。否则,如果选择了
TX_NO_ACTIVATE (0x00) 的值,则计时器将创建为非活动状态。在这种情况下,需要进行tx_timer_activate服务调用,才能实际启动计时器。

返回值:
TX_SUCCESS (0x00) 成功创建应用程序计时器。
TX_TIMER_ERROR (0x15) 无效应用程序计时器指针。指针为 NULL 或已创建计时器。
TX_TICK_ERROR报价提供的无效值 (0x16) 无效值 (零)。
TX_ACTIVATE_ERROR (0x17) 无效激活。
TX_CALLER_ERROR服务(0x13)无效调用。

tx_timer_delete

UINT tx_timer_delete(TX_TIMER *timer_ptr);
功能:删除定时器
参数:
timer_ptr 指向以前创建的应用程序计时器的指针。

返回值
TX_SUCCESS (0x00) 成功删除应用程序计时器。
TX_TIMER_ERROR (0x15) 无效应用程序计时器指针。
TX_CALLER_ERROR服务(0x13)无效调用。
tx_timer_activate

tx_timer_activate

UINT tx_timer_activate(TX_TIMER *timer_ptr);
功能:激活定时器
参数:
timer_ptr 指向以前创建的应用程序计时器的指针。

返回值:
TX_SUCCESS (0x00) 成功激活应用程序计时器。
TX_TIMER_ERROR (0x15) 无效应用程序计时器指针。
TX_ACTIVATE_ERROR (0x17) 计时器已处于活动状态,或者是已过期的一次计时器。

tx_timer_change

UINT tx_timer_change(
TX_TIMER *timer_ptr,
ULONG initial_ticks,
ULONG reschedule_ticks);
功能:修改定时器定时值
参数:
timer_ptr 指向计时器控制块的指针。
initial_ticks 指定计时器过期的初始刻度数。值的范围为 1 到 0xFFFF。
reschedule_ticks 指定第一个计时器过期后的所有计时器的刻度数。此参数的零使计时器成为一次计时器。否则,对于定期计时器,合法值的范围为 1 到 0xFFFF。
注意 过期的一次定时器必须通过tx_timer_change,然后才能再次激活。

返回值:
TX_SUCCESS (0x00) 成功的应用程序计时器更改。
TX_TIMER_ERROR (0x15) 无效应用程序计时器指针。
TX_TICK_ERROR报价提供的无效值 (0x16) 无效值 (零)。
TX_CALLER_ERROR服务(0x13)无效调用。

tx_timer_deactivate

UINT tx_timer_deactivate(TX_TIMER *timer_ptr);

参数:
timer_ptr指向以前创建的应用程序计时器的指针。

返回值:
TX_SUCCESS (0x00) 成功应用计时器停用。
TX_TIMER_ERROR (0x15) 无效应用程序计时器指针。

tx_timer_info_get

UINT tx_timer_info_get(TX_TIMER *timer_ptr, CHAR **name, UINT *active, ULONG *remaining_ticks, ULONG *reschedule_ticks, TX_TIMER **next_timer) 获取内存块池信息:

第 1 个参数 timer_ptr 是定时器控制块指针。
第 2 个参数 name 是定时器名字符串,获取后存储的指针。
第 3 个参数 active 是定时器启动或暂停状态,获取后存储的指针。
第 4 个参数 remaining_ticks 是定时器剩余滴答数,获取后存储的指针。
第 5 个参数 reschedule_ticks 是定时器第二次及以后的定时滴答数,获取后存储的指针。
第 6 个参数 next_timer 是下一个定时器指针,获取后存储的指针。
返回值:
TX_SUCCESS(0x00)成功获取信息。
TX_TIMER_ERROR:(0x15)无效的应用程序计时器指针。

TIME_demo

/*
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 * TIME_demo.c
 * Change Logs:
 * Date           Author       Notes
 * 2020年8月28日     	  henji      the first version
 */

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "tx_api.h"
#include "stdio.h"
	........
/* 软定时器 */
TX_TIMER timer_1;
TX_TIMER timer_2;
	........
/* Tracex使用 */
/*跟踪缓冲区的内存大小*/
#define trace_buffer_size 64000
/*要保留在跟踪注册表中的应用程序ThreadX对象的数量*/
#define registry_entries 40
UCHAR trace_buffer_start[trace_buffer_size];
UINT trace_status;
/* USER CODE END PV */
	.......
int main(void)
{
	........
	/* USER CODE BEGIN 2 */

	tx_kernel_enter(); //threadx 入口

	/* USER CODE END 2 */

	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1);
}

void Timer_1_entry(ULONG expiration_input)
{
	HAL_UART_Transmit(&huart1, (uint8_t *)"I am timer 1 ", sizeof("I am timer 1 "), HAL_MAX_DELAY);
}

void Timer_2_entry(ULONG expiration_input)
{
	HAL_UART_Transmit(&huart1, (uint8_t *)"I am timer 2 ", sizeof("I am timer 2 "), HAL_MAX_DELAY);
}

void tx_application_define(void *first_unused_memory)
{
	/*使能追踪*/
	trace_status = tx_trace_enable(&trace_buffer_start, trace_buffer_size,registry_entries);

	/* 创建定时器1 */
	tx_timer_create(&timer_1,      //定时器控制块
					"timer 1",     //定时器名称
					Timer_1_entry, //定时器入口函数
					0,   //定时器入口参数
					500, //定时器初始定时 500 Ticks
					500, //定时器重载500 Ticks (0 ticks 一次性定时器   )
					TX_AUTO_ACTIVATE //自动激活
					);

	/* 创建定时器2 */
	tx_timer_create(&timer_2,      //定时器控制块
					"timer 2",     //定时器名称
					Timer_2_entry, //定时器入口函数
					0,   //定时器入口参数
					100, //定时器初始定时 500 Ticks
					100, //定时器重载500 Ticks (0 ticks 一次性定时器   )
					TX_AUTO_ACTIVATE //自动激活
					);

}

(七)内存管理

tx_block_allocate

UINT tx_block_allocate(
TX_BLOCK_POOL *pool_ptr,
VOID **block_ptr,
ULONG wait_option);

功能:
This function checks for errors in allocate bytes function call.

参数:
pool_ptr 指向以前创建的内存块池的指针。
block_ptr 指向目标块指针的指针。成功分配时,分配的内存块的地址将放置在此参数点的位置。
wait_option 定义服务在没有可用的内存块时的行为方式。
等待选项的定义如下:
TX_NO_WAIT (0x0x0000000) - TX_NO_WAIT,无论它成功与否,都会立即从该服务返回。如果从非线程调用服务,则这是唯一有效的选项;例如,初始化、计时器或 ISR。
TX_WAIT_FOREVER (0xFFFF) - TX_WAIT_FOREVER,这将导致调用线程无限期挂起,直到内存块可用。
超时值(0x0000001 到 0xFFFFFFFE) - 选择数值 (1-0xFFFFFFFE) 指定在等待内存块时保持挂起计时器的最大数量。

返回值
TX_SUCCESS (0x00) 成功的内存块分配。
TX_DELETED时,已删除内存块池 (0x01) 内存块池。
TX_NO_MEMORY (0x10) 服务无法在指定时间内分配内存块以等待。
TX_WAIT_ABORTED (0x1A) 挂起被另一个线程、计时器或 ISR 中止。
TX_POOL_ERROR (0x02) 无效内存块池指针。
TX_WAIT_ERROR (0x04) 在从非TX_NO_WAIT的呼叫上指定的等待选项(非线程) 以外的等待选项。
TX_PTR_ERROR (0x03) 到目标指针的无效指针。

tx_block_pool_create

UINT tx_block_pool_create(
TX_BLOCK_POOL pool_ptr,
CHAR name_ptr,
ULONG block_size,
VOID pool_start,
ULONG pool_size);

功能:
This function checks for errors in the create byte pool memory

参数:
pool_ptr指向内存块池控件块的指针。
name_ptr指向内存块池的名称的指针。
block_size每个内存块中的字节数。
pool_start内存块池的起始地址。起始地址必须与 ULONG 数据类型的大小对齐。
pool_size内存块池可用的字节总数。

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

tx_block_pool_delete

UINT tx_block_pool_delete(TX_BLOCK_POOL *pool_ptr);

参数
pool_ptr指向以前创建的内存块池的指针。

返回值
TX_SUCCESS (0x00) 成功删除内存块池。
TX_POOL_ERROR (0x02) 无效内存块池指针。
TX_CALLER_ERROR服务(0x13)无效调用方。

tx_block_release

UINT tx_block_release(VOID *block_ptr);

参数
block_ptr指向以前分配的内存块的指针。

返回值
TX_SUCCESS (0x00) 成功释放内存块。
TX_PTR_ERROR (0x03) 内存块的无效指针。

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 0xFFFF) - TX_WAIT_FOREVER,这将导致调用线程无限期挂起,直到有足够的内存可用。
超时值(0x0000001 到 0xFFFFFE) - 选择数值 (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的呼叫上指定的等待选项(非线程) 以外的等待选项。
TX_CALLER_ERROR服务(0x13)无效调用方。

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) 池的大小无效。
TX_CALLER_ERROR服务(0x13)无效调用方。

tx_byte_pool_delete

UINT tx_byte_pool_delete(TX_BYTE_POOL *pool_ptr);

参数
pool_ptr指向以前创建的内存池的指针。
返回值
TX_SUCCESS (0x00) 成功删除内存池。
TX_POOL_ERROR (0x02) 无效内存池指针。
TX_CALLER_ERROR服务(0x13)无效调用方。

tx_byte_release

UINT tx_byte_release(VOID *memory_ptr);

参数
memory_ptr指向以前分配的内存区域的指针。

返回值
TX_SUCCESS (0x00) 成功释放内存。
TX_PTR_ERROR (0x03) 无效内存区域指针。
TX_CALLER_ERROR服务(0x13)无效调用方。

memory_demo

/*
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 * tracex_demo.c
 * Change Logs:
 * Date           Author       Notes
 * 2020年8月25日     	  henji      the first version
 */
	.......
	.......
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "tx_api.h"
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
TX_THREAD my_thread_1;
TX_THREAD my_thread_2;
TX_THREAD trace_thread;
uint8_t pData[] = "=========ThreadX=========\n";
uint8_t pData1[] = "I am thread1 ";
uint8_t pData2[] = "I am thread2 ";


/*线程栈大小*/
#define DEMO_STACK_SIZE 1024
/*内存池总大小*/
#define DEMO_BYTE_POOL_SIZE 1024*5
/*内存块池总大小*/
#define DEMO_BLOCK_POOL_SIZE 100
/*内存字节池控制指针*/
TX_BYTE_POOL byte_pool_0;
/*内存块池控制指针*/
TX_BLOCK_POOL block_pool_0;
/* 指向内存的指针 */
UCHAR *memory_ptr;

/* Tracex使用 */
/*跟踪缓冲区的内存大小*/
#define trace_buffer_size 64000
/*要保留在跟踪注册表中的应用程序ThreadX对象的数量*/
#define registry_entries 40
UCHAR trace_buffer_start[trace_buffer_size];
UINT trace_status;
/* USER CODE END PV */
	.......
int main(void)
{
    .......
	/* USER CODE BEGIN 2 */

	tx_kernel_enter(); //threadx 入口

	/* USER CODE END 2 */
	while (1);
}

/* USER CODE BEGIN 4 */
void thread1_entry(ULONG entry_input)
{

	INT count = 0;
	uint8_t init_data[] = "start now";
	while (1)
	{
		if (count == 0)
		{
			HAL_UART_Transmit(&huart1, init_data, sizeof(init_data),
			HAL_MAX_DELAY);
		}
		else
		{
			HAL_UART_Transmit(&huart1, pData1, sizeof(pData1), HAL_MAX_DELAY);
		}
		count++;
		HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_7 | GPIO_PIN_8);
		tx_thread_sleep(30);
	}
}

void thread2_entry(ULONG entry_input)
{
	INT count = 0;
	while (1)
	{
		HAL_UART_Transmit(&huart1, pData2, sizeof(pData2), HAL_MAX_DELAY);
		if (count == 1)
		{
			/*分配一个内存块空间*/
			 tx_byte_allocate(&byte_pool_0,		   //内存池的指针
					 	 	  (VOID **)&memory_ptr,//指向目标内存指针的指针
							  DEMO_BLOCK_POOL_SIZE,//分配内存块区域
							  TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
							  );
			 /*创建内存块*/
			 tx_block_pool_create(&block_pool_0, 	 //内存块池的指针
					 	 	 	 "block pool 0", 	 //内存块池名称
								 10,			 	 //每块大小
								 memory_ptr,     	 //指向目标内存指针的指针
								 DEMO_BLOCK_POOL_SIZE//内存块池总字节数
								 );
			 /*分配内存块*/
			 tx_block_allocate(&block_pool_0, //内存块池的指针
					  (VOID **)&memory_ptr,   //指向目标内存指针的指针
							   TX_NO_WAIT //  无论它成功与否,都会立即从该服务返回
			 	 	 	 	   );
			 /*释放*/
			 tx_block_release(memory_ptr);

		}
		count++;

		tx_thread_sleep(20);
	}
}

void trace_thread_input(ULONG entry_input)
{

	while (1)
	{
		/*使能追踪*/
		trace_status = tx_trace_enable(&trace_buffer_start, trace_buffer_size, registry_entries);
		if (trace_status == TX_SUCCESS)
		{
			; //使能成功
		}
		if (trace_status == TX_NOT_DONE)
		{
			; //在追踪
		}
		tx_thread_sleep(10);
	}
}


void tx_application_define(void *first_unused_memory)
{

	/* 追踪 */
	trace_status = tx_trace_enable(&trace_buffer_start, trace_buffer_size, registry_entries);
	/*创建一个内存池用于分配线程栈*/
	tx_byte_pool_create(&byte_pool_0, 		//内存池的指针
					"byte pool 0",		//名称
					first_unused_memory,//分配内存地址
					DEMO_BYTE_POOL_SIZE //分配内存池大小
					);

	/*分配一个栈空间用于trace*/
	 tx_byte_allocate(&byte_pool_0,		   //内存池的指针
			 	 	  (VOID **)&memory_ptr,//指向目标内存指针的指针
					  DEMO_STACK_SIZE,     //分配栈大小
					  TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
					  );

	/*trace 线程*/
	tx_thread_create(&trace_thread,	//线程控制块指针
			"trace_thread",         //线程名字
			trace_thread_input,//线程入口函数
			0,               //线程入口参数
			memory_ptr,		 //线程的起始地址
			DEMO_STACK_SIZE, //内存区域大小K
			1,               //优先级2 (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			1,               //禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START    //线程自动启动
			);

	/*分配一个栈空间用于线程1*/
	 tx_byte_allocate(&byte_pool_0,		   //内存池的指针
			 	 	  (VOID **)&memory_ptr,//指向目标内存指针的指针
					  DEMO_STACK_SIZE,     //分配栈大小
					  TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
					  );

	/*创建线程1*/
	tx_thread_create(&my_thread_1,	//线程控制块指针
			"my_thread1",    //线程名字
			thread1_entry,   //线程入口函数
			0,				 //线程入口参数
			memory_ptr,		 //线程的起始地址
			DEMO_STACK_SIZE, //线程栈大小 K
			3,				 //优先级3  (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			3,				 //禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START    //线程自动启动
			);

	/*分配一个栈空间用于线程2*/
	 tx_byte_allocate(&byte_pool_0,		   //内存池的指针
			 	 	  (VOID **)&memory_ptr,//指向目标内存指针的指针
					  DEMO_STACK_SIZE,     //分配栈大小
					  TX_NO_WAIT		   //无论它是否成功,都会立即从该服务返回
					  );
	/*线程2*/
	tx_thread_create(&my_thread_2,	//线程控制块指针
			"my_thread2",    //线程名字
			thread2_entry,   //线程入口函数
			0,				 //线程入口参数
			memory_ptr,      //线程的起始地址
			DEMO_STACK_SIZE, //线程栈大小 K
			2,               //优先级2 (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			2,               //禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START    //线程自动启动
			);

}
/* USER CODE END 4 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值