【STM32F429】第9章 ThreadX任务管理
论坛原始地址(持续更新):http://www.armbbs.cn/forum.php?mod=viewthread&tid=99514
第9章 ThreadX任务管理
对于初学者,特别是对于没有RTOS基础的同学来说,了解ThreadX的任务管理非常重要,了解任务管理的目的就是让初学者从裸机的,单任务编程过渡到带OS的,多任务编程上来。搞清楚了这一点,那么ThreadX学习就算入门了。
9.1 单任务系统
9.2 多任务系统
9.3 ThreadX的任务栈设置
9.4 ThreadX的系统栈设置
9.5 ThreadX的任务状态
9.6 ThreadX启动流程图示
9.7 ThreadX的空闲任务
9.8 ThreadX的启动函数tx_kernal_enter
9.9 ThreadX的任务创建函数tx_threadx_create
9.10 ThreadX的任务删除函数tx_threadx_delete
9.11 ThreadX的任务挂起函数tx_threadx_suspend
9.12 ThreadX的任务恢复函数tx_threadx_resume
9.13 ThreadX的任务复位函数tx_threadx_reset
9.14 ThreadX的任务终止函数tx_threadx_terminate
9.15 实验例程说明
9.16 总结
9.1 单任务系统
学习多任务系统之前,我们先来回顾下单任务系统的编程框架,即裸机时的编程框架。裸机编程主要是采用超级循环(super-loops)系统,又称前后台系统。应用程序是一个无限的循环,循环中调用相应的函数完成相应的操作,这部分可以看做后台行为;中断服务程序处理异步事件,这部分可以看做是前台行为。后台也可以叫做任务级,前台也叫作中断级。
对于前后台系统的编程思路主要有以下两种方式:
9.1.1 查询方式
对于一些简单的应用,处理器可以查询数据或者消息是否就绪,就绪后进行处理,然后再等待,如此循环下去。对于简单的任务,这种方式简单易处理。但大多数情况下,需要处理多个接口数据或者消息,那就需要多次处理,如下面的流程图所示:
用查询方式处理简单的应用,效果比较好,但是随着工程的复杂,采用查询方式实现的工程就变得很难维护,同时,由于无法定义查询任务的优先级,这种查询方式会使得重要的接口消息得不到及时响应。比如程序一直在等待一个非紧急消息就绪,如果这个消息后面还有一个紧急的消息需要处理,那么就会使得紧急消息长时间得不到执行。
9.1.2 中断方式
对于查询方式无法有效执行紧急任务的情况,采用中断方式就有效地解决了这个问题,下面是中断方式简单的流程图:
采用中断和查询结合的方式可以解决大部分裸机应用,但随着工程的复杂,裸机方式的缺点就暴露出来了:
1、 必须在中断(ISR)内处理时间关键运算:
- ISR 函数变得非常复杂,并且需要很长执行时间。
- ISR 嵌套可能产生不可预测的执行时间和堆栈需求。
2、 超级循环和ISR之间的数据交换是通过全局共享变量进行的:
- 应用程序的程序员必须确保数据一致性。
3、 超级循环可以与系统计时器轻松同步,但:
- 如果系统需要多种不同的周期时间,则会很难实现。
- 超过超级循环周期的耗时函数需要做拆分。
- 增加软件开销,应用程序难以理解。
4、 超级循环使得应用程序变得非常复杂,因此难以扩展:
- 一个简单的更改就可能产生不可预测的副作用,对这种副作用进行分析非常耗时。
- 超级循环概念的这些缺点可以通过使用实时操作系统 (RTOS) 来解决。
9.2 多任务系统
针对这些情况,使用多任务系统就可以解决这些问题了。下面是一个多任务系统的流程图:
多任务系统或者说RTOS的实现,重点就在这个调度器上,而调度器的作用就是使用相关的调度算法来决定当前需要执行的任务。如上图所示的那样,创建了任务并完成OS初始化后,就可以通过调度器来决定任务A,任务B和任务C的运行,从而实现多任务系统。另外需要初学者注意的是,这里所说的多任务系统同一时刻只能有一个任务可以运行,只是通过调度器的决策,看起来像所有任务同时运行一样。为了更好的说明这个问题,再举一个详细的运行例子,运行条件如下:
- 使用抢占式调度器。
- 1个空闲任务,优先级最低。
- 2个应用任务,一个高优先级和一个低优先级,优先级都比空闲任务优先级高。
- 中断服务程序,含USB中断,串口中断和系统滴答定时器中断。
下图所示是任务的运行过程,其中横坐标是任务优先级由低到高排列,纵坐标是运行时间,时间刻度有小到大。
(1) 启动RTOS,首先执行高优先级任务(tx_kernel_enter)。
(2) 高优先级任务等待事件标志(tx_event_flags_gets)被阻塞,低优先级任务得到执行。
(3) 低优先级任务执行的过程中产生USB中断,进入USB中断服务程序。
(4) 退出USB中断复位程序,回到低优先级任务继续执行。
(5) 低优先级任务执行过程中产生串口接收中断,进入串口接收中断服务程序。
(6) 退出串口接收中断复位程序,并发送事件标志设置消息(tx_event_flags_set),被阻塞的高优先级任务就会重新进入就绪状态,这个时候高优先级任务和低优先级任务都在就绪态,抢占式调度器就会让高优先级的任务先执行,所以此时就会进入高优先级任务。
(7) 高优先级任务由于等待事件标志(tx_event_flags_gets)会再次被阻塞,低优先级任务开始继续执行。
(8) 低优先级任务调用函数tx_thread_sleep,低优先级任务被挂起,从而空闲任务得到执行。
(9) 空闲任务执行期间发生滴答定时器中断,进入滴答定时器中断服务程序。
(10) 退出滴答定时器中断,由于低优先级任务延时时间到,低优先级任务继续执行。
(11) 低优先级任务再次调用延迟函数tx_thread_sleep,低优先级任务被挂起,从而切换到空闲任务。空闲任务得到执行。
通过上面实例的讲解,大家应该对多任务系统完整的运行过程有了一个全面的认识。随着教程后面对调度器,任务切换等知识点的讲解,大家会对这个运行过程有更深刻的理解。
9.3 ThreadX的任务栈设置
不管是裸机编程还是RTOS编程,栈的分配大小都非常重要。局部变量,函数调用时的现场保护和返回地址,函数的形参,进入中断函数前和中断嵌套等都需要栈空间,栈空间定义小了会造成系统崩溃。
裸机的情况下,用户可以在这里配置栈大小:
不同于裸机编程,在RTOS下,每个任务都有自己的栈空间。对于ThreadX来说,支持动态内存分配方式和静态分配方式,本教程全部是静态分配方式。
具体每个任务的栈大小是在创建ThreadX的任务时进行设置的:
/* ********************************************************************************************************* * 任务栈大小,单位字节 ********************************************************************************************************* */ #define APP_CFG_TASK_START_STK_SIZE 4096u #define APP_CFG_TASK_MsgPro_STK_SIZE 4096u #define APP_CFG_TASK_COM_STK_SIZE 4096u #define APP_CFG_TASK_USER_IF_STK_SIZE 4096u #define APP_CFG_TASK_IDLE_STK_SIZE 1024u #define APP_CFG_TASK_STAT_STK_SIZE 1024u /* ********************************************************************************************************* * 函 数 名: tx_application_define * 功能说明: ThreadX专用的任务创建,通信组件创建函数 * 形 参: first_unused_memory 未使用的地址空间 * 返 回 值: 无 ********************************************************************************************************* */ void tx_application_define(void *first_unused_memory) { /**************创建启动任务*********************/ tx_thread_create(&AppTaskStartTCB, /* 任务控制块地址 */ "App Task Start", /* 任务名 */ AppTaskStart, /* 启动任务函数地址 */ 0, /* 传递给任务的参数 */ &AppTaskStartStk[0], /* 堆栈基地址 */ APP_CFG_TASK_START_STK_SIZE, /* 堆栈空间大小 */ APP_CFG_TASK_START_PRIO, /* 任务优先级*/ APP_CFG_TASK_START_PRIO, /* 任务抢占阀值 */ TX_NO_TIME_SLICE, /* 不开启时间片 */ TX_AUTO_START); /* 创建后立即启动 */ /**************创建统计任务*********************/ tx_thread_create(&AppTaskStatTCB, /* 任务控制块地址 */ "App Task STAT", /* 任务名 */ AppTaskStat, /* 启动任务函数地址 */ 0, /* 传递给任务的参数 */ &AppTaskStatStk[0], /* 堆栈基地址 */ APP_CFG_TASK_STAT_STK_SIZE, /* 堆栈空间大小 */ APP_CFG_TASK_STAT_PRIO, /* 任务优先级*/ APP_CFG_TASK_STAT_PRIO, /* 任务抢占阀值 */ TX_NO_TIME_SLICE, /* 不开启时间片 */ TX_AUTO_START); /* 创建后立即启动 */ /**************创建空闲任务*********************/ tx_thread_create(&AppTaskIdleTCB, /* 任务控制块地址 */ "App Task IDLE", /* 任务名 */ AppTaskIDLE, /* 启动任务函数地址 */ 0, /* 传递给任务的参数 */ &AppTaskIdleStk[0], /* 堆栈基地址 */ APP_CFG_TASK_IDLE_STK_SIZE, /* 堆栈空间大小 */ APP_CFG_TASK_IDLE_PRIO, /* 任务优先级*/ APP_CFG_TASK_IDLE_PRIO, /* 任务抢占阀值 */ TX_NO_TIME_SLICE, /* 不开启时间片 */ TX_AUTO_START); /* 创建后立即启动 */ }
实际应用中给任务开辟多大的堆栈空间合适呢,在后面章详细跟大家进行讲解。
9.4 ThreadX的系统栈设置
上面跟大家讲解了什么是任务栈,这里的系统栈又是什么呢?裸机的情况下,凡是用到栈空间的地方都是在这里配置的栈空间:
在RTOS下,上面截图中设置的栈大小有了一个新的名字叫系统栈空间,而任务栈是不使用这里的空间的。任务栈不使用这里的栈空间,哪里使用这里的栈空间呢?答案就在中断函数和中断嵌套。
对于这个问题,简单的描述如下,更详细的内容待我们讲解ThreadX任务切换和双堆栈指针时再细说。
1、 由于Cortex-M3,M4,M7内核具有双堆栈指针,MSP主堆栈指针和PSP进程堆栈指针,或者叫PSP任务堆栈指针也是可以的。在ThreadX操作系统中,主堆栈指针MSP是给系统栈空间使用的,进程堆栈指针PSP是给任务栈使用的。也就是说,在ThreadX任务中,所有栈空间的使用都是通过PSP指针进行指向的。一旦进入了中断函数以及可能发生的中断嵌套都是用的MSP指针。这个知识点要记住它,当前可以不知道这是为什么,但是一定要记住。
2、实际应用中系统栈空间分配多大,主要是看可能发生的中断嵌套层数,下面我们就按照最坏执行情况进行考虑,所有的寄存器都需要入栈,此时分为两种情况:
- 64字节
对于Cortex-M3内核和未使用FPU(浮点运算单元)功能的Cortex-M4/M7内核在发生中断时需要将16个通用寄存器全部入栈,每个寄存器占用4个字节,也就是16*4 = 64字节的空间。
可能发生几次中断嵌套就是要64乘以几即可。当然,这种是最坏执行情况,也就是所有的寄存器都入栈。
(注:任务执行的过程中发生中断的话,有8个寄存器是自动入栈的,这个栈是任务栈,进入中断以后其余寄存器入栈以及发生中断嵌套都是用的系统栈)
- 200字节
对于具有FPU(浮点运算单元)功能的Cortex-M4/M7内核,如果在任务中进行了浮点运算,那么在发生中断的时候除了16个通用寄存器需要入栈,还有34个浮点寄存器也是要入栈的,也就是(16+34)*4 = 200字节的空间。当然,这种是最坏执行情况,也就是所有的寄存器都入栈。
(注:任务执行的过程中发送中断的话,有8个通用寄存器和18个浮点寄存器是自动入栈的,这个栈是任务栈,进入中断以后其余通用寄存器和浮点寄存器入栈以及发生中断嵌套都是用的系统栈)
9.5 ThreadX的任务状态
ThreadX有五种不同的线程状态:Ready State就绪态,Suspended State挂起状态,Executing State执行态,Terminated State终止执行态和Complete State完成态。:
- Executing State执行态
当任务处于实际运行状态被称之为执行态,即CPU的使用权被这个任务占用。
- Ready State就绪态
处于就绪态的任务是指那些能够运行(没有被挂起),但是当前没有运行的任务,因为同优先级或更高优先级的任务正在运行。
- Suspended State挂起态
ThreadX的挂起包含了阻塞,即由于等待信号量,消息队列,事件标志组等而处于的状态也是挂起态,
任务调用延迟函数或者对任务进行挂起操作(有专门的挂起函数)也会处于挂起状态。
- Completed State完成态
任务返回的状态称之为完成态,正常情况下每个任务是死循环,独立执行,不会返回。
- Terminated State终止态
终止任务执行的状态称之为终止态。
9.6 ThreadX的启动流程图示
ThreadX启动流程如下:
9.7 ThreadX的空闲任务
ThreadX中没有空闲任务,不过用户可以自己创建一个。使用空闲任务主要有以下几个作用:
- 用户不能让系统一直在执行各个应用任务,这样的话系统利用率就是 100%,系统就会一直超负荷运行,所以空闲任务很有必要。
- 为了更好的实现低功耗,空闲任务也很有必要,用户可以在空闲任务中实现睡眠,停机等低功耗措施。
9.8 ThreadX的启动函数tx_kernel_enter
函数原型:
#define tx_kernel_enter _tx_initialize_kernel_enter VOID _tx_initialize_kernel_enter(VOID)
函数描述:
函数tx_kernel_enter用于启动ThreadX调度器,即启动ThreadX的多任务执行。 此函数是ThreadX初始化阶段第1个调用的函数。
此函数依次调用了下面四个主要函数:
- _tx_initialize_low_level :主要用于初始化滴答定时器,使能PendSV,SVC和Systick中断。
- _tx_initialize_high_level:主要用于初始化信号量,事件标志组,消息队列等。另外会根据是否使能了宏定义TX_NO_TIMER来创建一个定时器任务。
- tx_application_define:应用程序回调函数,用户可以在里面创建任务,创建各种通信机制。
- _tx_thread_scheduler:启动ThreadX调度器。
注意事项:
- 正常情况下这个函数是不会返回的,如果返回了,说明启动失败。
使用举例:
/* ********************************************************************************************************* * 函 数 名: main * 功能说明: 标准c程序入口。 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ int main(void) { /* HAL库,MPU,Cache,时钟等系统初始 */ System_Init(); /* 内核开启前关闭HAL的时间基准 */ HAL_SuspendTick(); /* 进入ThreadX内核 */ tx_kernel_enter(); while(1); }
9.9 ThreadX的任务创建函数tx_thread_create
函数原型:
#define tx_thread_create(t,n,e,i,s,l,p,r,c,a) _txe_thread_create((t),(n),(e),(i),(s),(l),(p),(r),(c),(a),(sizeof(TX_THREAD))) UINT _txe_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, UINT thread_control_block_size)
函数描述:
函数tx_thread_create用于实现ThreadX操作系统的任务创建,并且还可以自定义任务栈的大小。
函数形参:
- 第1个参数thread_ptr是任务控制块地址。
- 第2个参数name_ptr是任务名,这个参数主要是用于调试目的,调试的时候方便看是哪个任务。
- 第3个参数entry_function是任务函数地址。当任务从此入口函数返回时,它将处于Complete State完成态,并无限挂起。
- 第4个参数entry_input是传递给任务的形参。
- 第5个参数stack_start栈基地址。
- 第6个参数stack_size是栈大小,单位字节,主要被函数嵌套调用和局部变量使用。
- 第7个参数priority是任务优先级,范围0到(TX_MAX_PRIORITES-1),其中0表示最高优先级。
- 第8个参数preempt_threshold是抢占阀值,范围0到(TX_MAX_PRIORITES-1)。只有高于此级别的优先级才可以抢占该任务。此数值必须小于或等于该任务的优先级数值。如果设置为等于该任务的优先级数值,将禁用抢占阈值。
- 第9个参数time_slice是时间片大小。
- 第10个参数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)无效的auto_start参数。
- TX_CALLER_ERROR(0x13)无效调用。
注意事项:
- 不允许在中断服务程序中调用,只可以在初始化和任务中调用。
- 使用抢占阈值将禁用时间片。合法的时间片值范围是1到0xFFFFFFFF(包括0)。值为TX_NO_TIME_SLICE(值为0)将禁用此任务的时间切片。
- 使用时间分片会导致少量系统开销。由于时间片仅在多个线程共享相同优先级的情况下才有用,因此,具有唯一优先级的任务不要分配时间片。
使用举例:
/* ********************************************************************************************************* * 任务优先级,数值越小优先级越高 ********************************************************************************************************* */ #define APP_CFG_TASK_START_PRIO 2u /* ********************************************************************************************************* * 任务栈大小,单位字节 ********************************************************************************************************* */ #define APP_CFG_TASK_START_STK_SIZE 4096u /* ********************************************************************************************************* * 静态全局变量 ********************************************************************************************************* */ static TX_THREAD AppTaskStartTCB; static uint64_t AppTaskStartStk[APP_CFG_TASK_START_STK_SIZE/8]; tx_thread_create(&AppTaskStartTCB, /* 任务控制块地址 */ "App Task Start", /* 任务名 */ AppTaskStart, /* 启动任务函数地址 */ 0, /* 传递给任务的参数 */ &AppTaskStartStk[0], /* 堆栈基地址 */ APP_CFG_TASK_START_STK_SIZE, /* 堆栈空间大小 */ APP_CFG_TASK_START_PRIO, /* 任务优先级*/ APP_CFG_TASK_START_PRIO, /* 任务抢占阀值 */ TX_NO_TIME_SLICE, /* 不开启时间片 */ TX_AUTO_START); /* 创建后立即启动 */
9.10 ThreadX的任务删除函数tx_thread_delete
函数原型:
#define tx_thread_delete _txe_thread_delete UINT _txe_thread_delete(TX_THREAD *thread_ptr)
函数描述:
函数tx_thread_delete用于实现ThreadX操作系统的任务删除。
- 第1个参数填要删除的任务控制块地址。
- 返回值:
- TX_SUCCESS(0x00)成功删除任务。
- TX_THREAD_ERROR(0x0E)无效的任务控制块指针。
- TX_DELETE_ERROR(0x11)指定的任务未处于Terminated终止态或者Completed完成态。
- TX_CALLER_ERROR(0x13)无效调用者。
注意事项:
- 不允许在中断服务程序中调用,只可以在任务和定时器组中调用。
- 只能删除处于Terminated终止态或者Completed完成态的任务。因此也就不可以在要删除的任务中调用此函数,也就是自己删除自己。
使用举例:
/* 任务控制块 */ static TX_THREAD AppTaskStartTCB; tx_thread_delete(&AppTaskCOMTCB);
9.11ThreadX的任务挂起函数tx_thread_suspend
函数原型:
#define tx_thread_suspend _txe_thread_suspend UINT _txe_thread_suspend(TX_THREAD *thread_ptr)
函数描述:
函数tx_thread_suspend用于实现ThreadX操作系统的任务挂起。任务也可以挂起自己。挂起后,可以通过tx_thread_resume恢复。
函数形参:
- 第1个参数填要挂起任务的任务控制块。
- 返回值:
- TX_SUCCESS(0x00)成功的任务挂起。
- TX_THREAD_ERROR(0x0E)无效的任务控制块指针。
- TX_SUSPEND_ERROR(0x14)指定的线程处于终止或完成状态。
- TX_CALLER_ERROR(0x13)无效调用者。
注意事项:
- 允许在中断,任务,定时器组和初始化中调用。
- 如果指定的任务由于其它原因已经挂起,则本次挂起将被保存,直到之前的挂起已经恢复。当发生这种情况时,将执行指定任务的无条件挂起,之后的无条件挂起请求将无效。
使用举例:
/* 任务控制块 */ static TX_THREAD AppTaskStartTCB; tx_thread_suspend(&AppTaskCOMTCB);
9.12 ThreadX的任务恢复函数tx_thread_resume
函数原型:
#define tx_thread_resume _txe_thread_resume UINT _txe_thread_resume(TX_THREAD *thread_ptr)
函数描述:
函数tx_thread_resume用于实现ThreadX操作系统的任务恢复。另外,此任务还将恢复在没有自动启动的情况下创建的任务。
函数形参:
- 第1个参数填要恢复的任务控制块地址。
- 返回值:
- TX_SUCCESS(0x00)成功的恢复任务。
- TX_SUSPEND_LIFTED(0x19)先前设置的延迟暂停已取消。
- TX_THREAD_ERROR(0x0E)无效的任务控制地址。
- TX_RESUME_ERROR(0x12)指定的任务没有被挂起,或者之前被tx_thread_suspend以外的服务挂起。
注意事项:
- 允许在中断,任务,定时器组和初始化中调用。
使用举例:
/* 任务控制块 */ static TX_THREAD AppTaskStartTCB; tx_thread_resume(&AppTaskCOMTCB);
9.13 ThreadX的任务复位函数tx_thread_reset
函数原型:
#define tx_thread_resume _txe_thread_resume UINT _txe_thread_resume(TX_THREAD *thread_ptr) #define tx_thread_reset _txe_thread_reset
函数描述:
函数tx_thread_reset用于实现ThreadX操作系统的任务复位。任务必须处于TX_COMPLETED完成态或TX_TERMINATED终止态才能复位。
函数形参:
- 第1个参数填要恢复的任务控制块地址。
- 返回值:
- TX_SUCCESS(0x00)成功复位任务。
- TX_NOT_DONE(0x20)指定的线程未处于TX_COMPLETED或TX_TERMINATED状态。
- TX_THREAD_ERROR(0x0E)无效的任务控制块指针。
- TX_CALLER_ERROR(0x13)无效调用者。
注意事项:
- 不允许在中断中调用,仅可以在任务中调用。
使用举例:
/* 任务控制块 */ static TX_THREAD AppTaskStartTCB; tx_thread_reset(&AppTaskCOMTCB);
9.14 ThreadX的任务终止函数tx_thread_terminate
函数原型:
#define tx_thread_terminate _txe_thread_terminate UINT _txe_thread_terminate(TX_THREAD *thread_ptr)
函数描述:
函数tx_thread_terminate用于实现ThreadX操作系统的任务终止。该函数终止指定任务,而不管该任务是否被挂起。任务可以调用此函数以终止自身。
函数形参:
- 第1个参数填要恢复的任务控制块地址。
- 返回值:
- TX_SUCCESS(0x00)成功终止任务。
- TX_THREAD_ERROR(0x0E)无效的任务控制块指针。
- TX_CALLER_ERROR(0x13)无效调用者。
注意事项:
- 不允许在中断中调用,仅可以在任务和定时器组中调用。
- 终止后,必须调用函数tx_thread_reset复位任务以使其再次执行。
- 应用程序有责任确保任务处于适合终止的状态。例如,任务不应在关键应用程序处理期间或在其他中间件组件内部终止,否则可能会使这种处理处于未知状态。
使用举例:
/* 任务控制块 */ static TX_THREAD AppTaskStartTCB; tx_thread_terminate(&AppTaskCOMTCB);
9.15 实验例程
配套例子:
V6-3004_ThreadX Task Control
实验目的:
- 学习hreadX任务管理。
实验内容:
1、共创建了如下几个任务,通过按下按键K1可以通过串口或者RTT打印任务堆栈使用情况
===================================================
OS CPU Usage = 1.94%
=======================================================
Prio StackSize CurStack MaxStack Taskname
2 4092 383 391 App Task Start
3 4092 543 659 App Msp Pro
4 4092 391 391 App Task UserIF
5 4092 543 659 App Task COM
30 1020 519 519 App Task STAT
31 1020 143 71 App Task IDLE
0 1020 391 391 System Timer Thread
串口软件可以使用SecureCRT或者H7-TOOL RTT查看打印信息。
App Task Start任务 :启动任务,这里用作BSP驱动包处理。
App Task MspPro任务 :消息处理,这里未使用。
App Task UserIF任务 :按键消息处理。
App Task COM任务 :这里用作LED闪烁。
App Task STAT任务 :统计任务
App Task IDLE任务 :空闲任务
System Timer Thread任务:系统定时器任务
2、(1) 凡是用到printf函数的全部通过函数App_Printf实现。
(2) App_Printf函数做了信号量的互斥操作,解决资源共享问题。
3、默认上电是通过串口打印信息,如果使用RTT打印信息
(1) MDK AC5,MDK AC6或IAR通过使能bsp.h文件中的宏定义为1即可
#define Enable_RTTViewer 1
(2) Embedded Studio继续使用此宏定义为0, 因为Embedded Studio仅制作了调试状态RTT方式查看。
实验操作:
- K1按键按下打印任务执行情况。
- K2按键按下挂起任务App Task COM。
- K3按键按下恢复任务App Task COM。
- 摇杆OK按键按下复位任务App Task COM。
串口打印信息方式(AC5,AC6和IAR):
波特率 115200,数据位 8,奇偶校验位无,停止位 1
RTT打印信息方式(AC5,AC6和IAR):
Embedded Studio仅支持调试状态RTT打印:
由于Embedded Studio不支持中文,所以中文部分显示乱码,不用管。
程序执行框图:
9.16 总结
本章节主要为大家讲解了ThreadX的任务管理,此章节比较重要,望初学者用心掌握。