关于这部分的详细知识
ThreadX应用笔记:内核初始化和任务调度 - 树·哥 - 博客园 (cnblogs.com)
在这两篇博客的基础上,增加了一些具体代码。
api:
1.tx_thread_ create
UINT tx_thread_create(TX_THREAD *thread_ptr,
CHAR *name_ptr,
VOID (*entry_function)(ULONG id),
ULONG entry_input,
VOID *stack_start,
ULONG stack_size,
UINT priority,
UINT preempt_threshold,
ULONG time_slice,
UINT auto_start)
第一个参数是线程控制块(PCB)地址
第二个参数是name_ptr 是线程名
第 3 个参数 entry_function 是线程函数地址
第 4 个参数 entry_input 是传递给线程的形参
第 5 个参数 stack_start 栈基地址
第 6 个参数 stack_size 是栈大小
第 7 个参数 priority 是线程优先级
第 8 个参数 preempt_threshold 是抢占阀值
第 9 个参数 time_slice 是时间片大小
第 10 个参数 auto_start 是指定线程是立即启动还是处于挂起状态
写的时候写入到main函数之前的tx_application_define里,有几个线程写几个,关于tx_application_define,在上一篇文章的引用里有涉及。
2.tx_thread_info_get
UINT tx_thread_info_get( TX_THREAD *thread_ptr,
CHAR **name,
UINT *state,
ULONG *run_count,
UINT *priority,
UINT *preemption_threshold,
ULONG *time_slice,
TX_THREAD **next_thread,
TX_THREAD **next_suspended_thread)
关于其写法可以是下面这样,要写在线程逻辑内部,首先申明各个指针,再声明缓冲区用于格式化字符串,通过函数获得信息后,格式化存入buffer,再通过串口打印出来。
void thread_0_entry(ULONG thread_input)
//线程,串口发送数据
{
TX_THREAD *thread_ptr;
CHAR *name_ptr;
UINT state;
ULONG run_count;
UINT priority;
UINT preemption_threshold;
ULONG time_slice;
UINT suspension_count;
TX_THREAD *next_thread;
TX_THREAD *previous_thread;
// 缓冲区用于格式化输出字符串
char buffer[256];
// 获取当前线程的指针
thread_ptr = tx_thread_identify();
// 获取线程信息
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer, sizeof(buffer),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY);
//UINT status = 8;
tx_thread_sleep(500);
/* This thread simply sits in while-forever-sleep loop. */
}
打印的结果
可以看到线程信息已经打印出来了。
3.tx_thread_delete
UINT tx_thread_delete(TX_THREAD *thread_ptr)
用的时候可以像这样
void thread_0_entry(ULONG thread_input)
//线程,串口发送数据
{
tx_thread_sleep(6000);
/* This thread simply sits in while-forever-sleep loop. */
UINT status = tx_thread_delete(&thread_1);
// 缓冲区用于格式化输出字符串
char buffer2[256];
// 根据 tx_thread_delete 的返回结果构建字符串
if (status == TX_SUCCESS) {
snprintf(buffer2, sizeof(buffer2), "Thread 1 deletion: SUCCESS\n");
} else {
snprintf(buffer2, sizeof(buffer2), "Thread 1 deletion: FAILED, Error Code: %u\n", status);
}
// 通过UART发送结果
HAL_UART_Transmit(&huart1, (uint8_t *)buffer2, strlen(buffer2), HAL_MAX_DELAY);
// 让当前线程休眠一段时间
tx_thread_sleep(500);
}
只能删除处于 Terminated 终止态或者 Completed 完成态的线程。打印结果如下最后一行。Thread 1 deletion: SUCCESS,线程删除成功。
4.tx_thread_identify
一般和tx_thread_info_get配合使用,具体代码在tx_thread_info_get处说明了。
5.tx_thread_preemption_change
preemption的意思是“抢占”,这个函数的意义是更改线程的抢占阈值。
用的时候新的抢占阈值不能比线程优先级大,否则函数返回值会变成0x18,十进制24。
下面代码示例默认线程优先级大于3。
void thread_0_entry(ULONG thread_input)
//线程,串口发送数据
{
TX_THREAD *thread_ptr;
CHAR *name_ptr;
UINT state;
ULONG run_count;
UINT priority;
UINT preemption_threshold;
ULONG time_slice;
UINT suspension_count;
TX_THREAD *next_thread;
TX_THREAD *previous_thread;
// 缓冲区用于格式化输出字符串
char buffer1[256];
// 获取当前线程的指针
thread_ptr = tx_thread_identify();
// 获取当前线程信息
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer1, sizeof(buffer1),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
UINT new_threshold=3;
UINT AA= tx_thread_preemption_change(&thread_0,new_threshold,&preemption_threshold);
char buffer3[256];
snprintf(buffer3, sizeof(buffer3), "AA value: %u\n", AA);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer3, strlen(buffer3), HAL_MAX_DELAY);
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer1, sizeof(buffer1),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
}
可以看到打印结果,线程抢占阈值修改成功。
6.tx_thread_priority_change
调用这个函数的时候,如果同时要更改线程的抢占阈值,要先更改函数优先级再更改抢占阈值,因为每次更改函数优先级都会重置函数的抢占阈值。
可以像下面这样使用
void thread_0_entry(ULONG thread_input)
//线程,串口发送数据
{
TX_THREAD *thread_ptr;
CHAR *name_ptr;
UINT state;
ULONG run_count;
UINT priority;
UINT preemption_threshold;
ULONG time_slice;
UINT suspension_count;
TX_THREAD *next_thread;
TX_THREAD *previous_thread;
// 缓冲区用于格式化输出字符串
char buffer1[256];
// 获取当前线程的指针
thread_ptr = tx_thread_identify();
// 获取当前线程信息
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer1, sizeof(buffer1),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
UINT new_threshold=3;
UINT AA= tx_thread_preemption_change(&thread_0,new_threshold,&preemption_threshold);
char buffer3[256];
snprintf(buffer3, sizeof(buffer3), "AA value: %u\n", AA);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer3, strlen(buffer3), HAL_MAX_DELAY);
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer1, sizeof(buffer1),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
UINT new_priority=3;
//声明新的优先级
UINT BB= tx_thread_priority_change(&thread_0,new_priority,&priority);
//调用优先级更改函数
snprintf(buffer3, sizeof(buffer3), "BB value: %u\n", BB);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer3, strlen(buffer3), HAL_MAX_DELAY);
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer1, sizeof(buffer1),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
}
可以看到打印结果,线程优先级已经更改。
7.tx_thread_relinquish
线程释放控制权,除了将控制权放弃给相同优先级的线程之外,此服务还将控制权放弃给由于当前线程的抢占阈值设置而阻止执行的最高优先级线程。
示例代码:
#define DEMO_STACK_SIZE 1024
TX_THREAD thread_0;
TX_THREAD thread_1;
UCHAR thread_0_stack[DEMO_STACK_SIZE];
UCHAR thread_1_stack[DEMO_STACK_SIZE];
void thread_0_entry(ULONG thread_input);
void thread_1_entry(ULONG thread_input);
void tx_application_define(void *first_unused_memory)
{
// 创建线程0
tx_thread_create(&thread_0, "Thread 0", thread_0_entry, 0,
thread_0_stack, DEMO_STACK_SIZE,
1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
// 创建线程1
tx_thread_create(&thread_1, "Thread 1", thread_1_entry, 0,
thread_1_stack, DEMO_STACK_SIZE,
2, 2, TX_NO_TIME_SLICE, TX_AUTO_START);
}
void thread_0_entry(ULONG thread_input)
{
while (1)
{
// 执行线程0的任务
// ...
// 线程0主动放弃剩余时间片
tx_thread_relinquish();
}
}
void thread_1_entry(ULONG thread_input)
{
while (1)
{
// 执行线程1的任务
// ...
// 线程1主动放弃剩余时间片
tx_thread_relinquish();
}
}
下面的代码是我自己根据上面写的:
void thread_0_entry(ULONG thread_input)
//线程,串口发送数据
{
TX_THREAD *thread_ptr;
CHAR *name_ptr;
UINT state;
ULONG run_count;
UINT priority;
UINT preemption_threshold;
ULONG time_slice;
UINT suspension_count;
TX_THREAD *next_thread;
TX_THREAD *previous_thread;
// 缓冲区用于格式化输出字符串
char buffer1[256];
// 获取当前线程的指针
thread_ptr = tx_thread_identify();
// 获取当前线程信息
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer1, sizeof(buffer1),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
while(1)
{
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
tx_thread_relinquish();
}
}
void thread_1_entry(ULONG thread_input)
//线程1,控制led,只让他亮5s
{
// tx_thread_sleep(500);
// HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
// tx_thread_sleep(500);
// HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);
while(1)
{
char aa[]="线程2";
HAL_UART_Transmit(&huart1, (uint8_t *)aa, strlen(aa), HAL_MAX_DELAY);
tx_thread_relinquish();
}
}
串口打印结果显示,使用了tx_thread_reliquish之后,两个线程循环打印,实现了循环调度。
8.tx_thread_resume&tx_thread_suspend
这个函数用于唤醒之前被挂起的线程(tx_thread_suspend)。
下面是示例,在线程0中,先把自己挂起(tx_thread_suspend),这时候如果不再线程1中写tx_thread_resume函数,会发现线程0中的第二个串口发送函数没有运行。这时候加上tx_thread_resume函数,可以看到刚刚被挂起的线程0会从刚刚没有运行完的地方继续运行。
void thread_0_entry(ULONG thread_input)
//线程,串口发送数据
{
TX_THREAD *thread_ptr;
CHAR *name_ptr;
UINT state;
ULONG run_count;
UINT priority;
UINT preemption_threshold;
ULONG time_slice;
UINT suspension_count;
TX_THREAD *next_thread;
TX_THREAD *previous_thread;
// 缓冲区用于格式化输出字符串
char buffer1[256];
// 获取当前线程的指针
thread_ptr = tx_thread_identify();
// 获取当前线程信息
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer1, sizeof(buffer1),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
UINT aa= tx_thread_suspend(&thread_0);
snprintf(buffer1, sizeof(buffer1), "挂起线程0:%u\n",aa);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
}
void thread_1_entry(ULONG thread_input)
//线程1,控制led,只让他亮5s
{
char cc[]="线程2\n";
HAL_UART_Transmit(&huart1, (uint8_t *)cc, strlen(cc), HAL_MAX_DELAY);
UINT aa= tx_thread_resume(&thread_0);
char buffer1[256];
snprintf(buffer1, sizeof(buffer1), "唤醒线程0:%u\n",aa);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
}
串口打印结果:
9.tx_thread_sleep
这个函数用来让线程在指定的计时器刻度数内挂起,就是睡眠(sleep)一会。也可以理解为暂停指定时间,之后继续运行。
void thread_0_entry(ULONG thread_input)
//线程,串口发送数据
{
TX_THREAD *thread_ptr;
CHAR *name_ptr;
UINT state;
ULONG run_count;
UINT priority;
UINT preemption_threshold;
ULONG time_slice;
UINT suspension_count;
TX_THREAD *next_thread;
TX_THREAD *previous_thread;
// 缓冲区用于格式化输出字符串
char buffer1[256];
// 获取当前线程的指针
thread_ptr = tx_thread_identify();
// 获取当前线程信息
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer1, sizeof(buffer1),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
UINT aa= tx_thread_sleep(1000);
snprintf(buffer1, sizeof(buffer1), "线程0睡眠1s:%u\n",aa);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
}
void thread_1_entry(ULONG thread_input)
//线程1,控制led,只让他亮5s
{
char cc[]="线程2\n";
HAL_UART_Transmit(&huart1, (uint8_t *)cc, strlen(cc), HAL_MAX_DELAY);
}
根据示例代码,线程0 挂起后执行线程1,同时1s后到达睡眠时间,继续执行后续代码,因此打印结果为:
10.tx_thread_terminate
该服务终止指定的应用程序线程,而不管该线程是否被挂起。线程可以调用此服务以终止自身。
如果终止线程后想要重新让线程运行,需要和tx_thread_reset tx_thread_resume配合使用,先重置,再唤醒。下面代码是先将线程0终止,再在线程1中重置线程0,然后恢复线程0。
void thread_0_entry(ULONG thread_input)
//线程,串口发送数据
{
TX_THREAD *thread_ptr;
CHAR *name_ptr;
UINT state;
ULONG run_count;
UINT priority;
UINT preemption_threshold;
ULONG time_slice;
UINT suspension_count;
TX_THREAD *next_thread;
TX_THREAD *previous_thread;
// 缓冲区用于格式化输出字符串
char buffer1[256];
// 获取当前线程的指针
thread_ptr = tx_thread_identify();
// 获取当前线程信息
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer1, sizeof(buffer1),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
UINT aa= tx_thread_terminate(&thread_0);
snprintf(buffer1, sizeof(buffer1), "线程0睡眠1s:%u\n",aa);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
}
void thread_1_entry(ULONG thread_input)
//线程1,控制led,只让他亮5s
{
char cc[]="线程2\n";
HAL_UART_Transmit(&huart1, (uint8_t *)cc, strlen(cc), HAL_MAX_DELAY);
UINT aa=tx_thread_reset(&thread_0);
char buffer1[256];
snprintf(buffer1, sizeof(buffer1), "重置线程0:%u\n",aa);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
UINT bb=tx_thread_resume(&thread_0);
snprintf(buffer1, sizeof(buffer1), "恢复线程0:%u\n",bb);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
}
打印结果显示线程0成功终止,也成功重置并唤起。
11.tx_thread_time_slice_change
该服务更改指定应用程序线程的时间片。为线程选择时间片可确保在相同或更高优先级的其他线程有机会执行之前,它不会执行超过指定数量的计时器滴答。
注意:使用抢占阈值会禁用指定线程的时间片。
下面代码先打印线程信息,再更改线程时间片,再打印线程信息。可以看到结果,线程0的时间片从10变成了20。
void thread_0_entry(ULONG thread_input)
//线程,串口发送数据
{
TX_THREAD *thread_ptr;
CHAR *name_ptr;
UINT state;
ULONG run_count;
UINT priority;
UINT preemption_threshold;
ULONG time_slice;
UINT suspension_count;
TX_THREAD *next_thread;
TX_THREAD *previous_thread;
// 缓冲区用于格式化输出字符串
char buffer1[256];
// 获取当前线程的指针
thread_ptr = tx_thread_identify();
// 获取当前线程信息
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer1, sizeof(buffer1),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
UINT bb=tx_thread_time_slice_change(&thread_0,20,&time_slice);
tx_thread_info_get(thread_ptr, &name_ptr, &state, &run_count, &priority, &preemption_threshold,
&time_slice, &next_thread, &previous_thread);
// 格式化线程信息为字符串
snprintf(buffer1, sizeof(buffer1),
"Thread Name: %s\nState: %u\nRun Count: %lu\nPriority: %u\nPreemption Threshold: %u\nTime Slice: %lu\nSuspension Count: %u\n",
name_ptr, state, run_count, priority, preemption_threshold, time_slice, suspension_count);
// 通过UART发送线程信息
HAL_UART_Transmit(&huart1, (uint8_t *)buffer1, strlen(buffer1), HAL_MAX_DELAY);
}