其他分享
首页 > 其他分享> > uCOS-III 学习记录(11)——任务管理

uCOS-III 学习记录(11)——任务管理

作者:互联网

参考内容:《[野火]uCOS-III内核实现与应用开发实战指南——基于STM32》第 15、16 和 21 章。

从本文开始,是 uCOS 的 API 应用。

目录

1 任务状态

在 uCOS 中,任务状态分为以下几种,任务就是在这几种状态中来回变化的:

在 os.h 中宏定义了任务的状态值:

/* 系统状态 */
#define  OS_STATE_OS_STOPPED                    (OS_STATE)(0u)
#define  OS_STATE_OS_RUNNING                    (OS_STATE)(1u)
	
/* 任务状态 */
#define	 OS_TASK_STATE_BIT_DLY					(OS_STATE)(0x01u)	/* 挂起位      				*/
#define	 OS_TASK_STATE_BIT_PEND					(OS_STATE)(0x02u)	/* 等待位      				*/
#define	 OS_TASK_STATE_BIT_SUSPENDED			(OS_STATE)(0x04u)	/* 延时/超时位 				*/
	
#define  OS_TASK_STATE_RDY						(OS_STATE)(   0u)	/* 0 0 0  就绪 				*/
#define  OS_TASK_STATE_DLY						(OS_STATE)(   1u)	/* 0 0 1  延时/超时 			*/
#define  OS_TASK_STATE_PEND						(OS_STATE)(   2u)	/* 0 1 0  等待	 			*/
#define  OS_TASK_STATE_PEND_TIMEOUT				(OS_STATE)(   3u)	/* 0 1 1  等待+超时 			*/
#define  OS_TASK_STATE_SUSPENDED				(OS_STATE)(   4u)	/* 1 0 0  挂起 				*/
#define  OS_TASK_STATE_DLY_SUSPENDED			(OS_STATE)(   5u)	/* 1 0 1  挂起+延时/超时 	*/
#define  OS_TASK_STATE_PEND_SUSPENDED			(OS_STATE)(   6u)	/* 1 1 0  挂起+等待		 	*/
#define  OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED	(OS_STATE)(   7u)	/* 1 1 1  挂起+超时+等待 	*/
#define  OS_TASK_STATE_DEL						(OS_STATE)( 255u)	

2 修改和添加相关代码

2.1 修改 TCB(os.h)

TCB 中增加两个成员:

struct os_tcb{
	CPU_STK			*StkPtr;
	CPU_STK_SIZE	StkSize;
	
	OS_PRIO			Prio;				/* 任务优先级 */
	
	OS_TCB			*NextPtr;			/* 就绪列表双向链表的下一个指针 */
	OS_TCB			*PrevPtr;			/* 就绪列表双向链表的前一个指针 */
	
	OS_TCB			*TickNextPtr;		/* 指向链表的下一个 TCB 节点 */
	OS_TCB			*TickPrevPtr;		/* 指向链表的上一个 TCB 节点 */
	OS_TICK_SPOKE	*TickSpokePtr;		/* 用于回指到链表根部 */
	OS_TICK			TickCtrMatch;		/* 该值等于时基计数器 OSTickCtr 的值加上 TickRemain 的值 */
	OS_TICK			TickRemain;			/* 设置任务还需要等待多少个时钟周期 */
	
	OS_TICK			TimeQuanta;			/* 任务需要多少个时间片 */
	OS_TICK			TimeQuantaCtr;		/* 任务剩余的时间片个数 */
	
	OS_STATE		TaskState;			/* 表示任务的状态 */
	
#if OS_CFG_TASK_SUSPENDED_EN > 0u
	OS_NESTING_CTR	SuspendCtr;			/* 任务挂起函数 OSTaskSuspend() 计数器 */
#endif
};

2.2 添加宏定义和数据类型

在 os_cfg.h 中添加宏定义,用于使能任务挂起和删除功能,这两个功能可以开启也可以关闭:

/* 使能任务挂起功能 */
#define OS_CFG_TASK_SUSPENDED_EN          	1u

/* 使能任务删除功能 */
#define OS_CFG_TASK_DEL_EN					1u

在 os_type.h 中增加数据类型:

typedef   CPU_INT08U      OS_NESTING_CTR;

3 任务管理的函数

任务的挂起与恢复函数在很多时候都是很有用的,比如我们想暂停某个任务运行一段时间,但是我们又需要在其恢复的时候继续工作,那么删除任务是不可能的,因为删除了任务的话,任务的所有的信息都是不可能恢复的了,删除是完完全全删除了,里面的资源都被系统释放掉,但是挂起任务就不会这样。调用挂起任务函数,仅仅是将任务进入挂起态,其内部的资源都会保留下来,同时也不会参与系统中任务的调度,当调用恢复函数的时候,整个任务立即从挂起态进入就绪态,并且参与任务的调度,如果该任务的优先级是当前就绪态优先级最高的任务,那么立即会按照挂起前的任务状态继续执行该任务。也就是说,挂起任务之前是什么状态,都会被系统保留下来,在恢复的瞬间,继续执行。

删除任务是说任务将返回并处以删除(休眠)状态,任务的代码不再被 uCOS 调用,删除任务不是删除代码。删除任务和挂起任务有些相似,但最大的不同就是删除任务 TCB 的操作。我们知道在任务创建的时候,需要给每个任务分配一个 TCB,TCB 存储有关这个任务重要的信息,对任务间有至关重要的作用,挂起任务根本不会动 TCB,但删除任务就会把 TCB 进行初始化,这样关于任务的任何信息都被抹去。注意,删除任务并不会释放任务的栈空间。

以上所提及的三个函数,都属于 uCOS 的 API 函数,方便用户进行调用。

3.1 任务挂起函数 OSTaskSuspend()(os_task.c)

该函数用于将一个任务挂起,被挂起的任务就位于挂起态了,它的 TCB 将会被移出就绪列表,而且不会参与任何任务调度,除非有其他任务主动将这个任务恢复,即从挂起态转为就绪态,否则它没有机会获得运行权。

该函数完成的工作是:

接下来根据任务的不同状态,执行不同的操作:

/* 任务挂起函数 */
#if OS_CFG_TASK_SUSPENDED_EN > 0u
void OSTaskSuspend (OS_TCB *p_tcb, OS_ERR *p_err)
{
	CPU_SR_ALLOC();	
	CPU_CRITICAL_ENTER();
	
	if (p_tcb == (OS_TCB *)0)
	{
		p_tcb = OSTCBCurPtr;
	}
	
	if (p_tcb == OSTCBCurPtr)
	{
		if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0)	/* 如果调度器锁住则不能挂起自己 */
		{
			CPU_CRITICAL_EXIT();
			*p_err = OS_ERR_SCHED_LOCKED;
			return;
		}
	}
	
	*p_err = OS_ERR_NONE;
	
	switch (p_tcb->TaskState)
	{
		case OS_TASK_STATE_RDY:
			OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
			p_tcb->TaskState  = OS_TASK_STATE_SUSPENDED;
			p_tcb->SuspendCtr = (OS_NESTING_CTR)1;
			OS_RdyListRemove (p_tcb);
			OS_CRITICAL_EXIT_NO_SCHED();
			break;
		
		case OS_TASK_STATE_DLY:
			p_tcb->TaskState  = OS_TASK_STATE_DLY_SUSPENDED;
			p_tcb->SuspendCtr = (OS_NESTING_CTR)1;
			CPU_CRITICAL_EXIT();
			break;
		
		case OS_TASK_STATE_PEND:
			p_tcb->TaskState  = OS_TASK_STATE_PEND_SUSPENDED;
			p_tcb->SuspendCtr = (OS_NESTING_CTR)1;
			CPU_CRITICAL_EXIT();
			break;
		
		case OS_TASK_STATE_PEND_TIMEOUT:
			p_tcb->TaskState  = OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED;
			p_tcb->SuspendCtr = (OS_NESTING_CTR)1;
			CPU_CRITICAL_EXIT();
			break;
		
		case OS_TASK_STATE_SUSPENDED:
		case OS_TASK_STATE_DLY_SUSPENDED:
		case OS_TASK_STATE_PEND_SUSPENDED:
		case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
			p_tcb->SuspendCtr++;
			CPU_CRITICAL_EXIT();
			break;
		
		default:
			CPU_CRITICAL_EXIT();
			*p_err = OS_ERR_STATE_INVALID;
			return;
	}
	
	OSSched();	/* 任务切换 */
	
	CPU_CRITICAL_EXIT();
}
#endif

3.2 任务恢复函数 OSTaskResume()(os_task.c)

该函数用于恢复一个处在挂起态的任务。比如,A 任务处在挂起加延时态,B 任务处在挂起加等待态,C 任务处于挂起加等待加延时态,那么当别的任务恢复它们后,A 任务处在延时态,B 任务处在等待态,C 任务处于等待加延时态。言下之意,就是把挂起态给去掉了。

需要注意的是,任务可以挂其自身,但不能恢复自身,因为自己都被挂起了,没有机会被运行了,又怎么能自己恢复自己呢!只有当别的任务进行恢复操作时,任务才能从挂起态恢复过来。

该函数根据任务的不同状态,执行不同的操作:

/* 任务恢复函数 */
#if OS_CFG_TASK_SUSPENDED_EN > 0u
void OSTaskResume (OS_TCB *p_tcb, OS_ERR *p_err)
{
	CPU_SR_ALLOC();
	CPU_CRITICAL_ENTER();
	
	*p_err = OS_ERR_NONE;
	
	switch (p_tcb->TaskState)
	{
		case OS_TASK_STATE_RDY:
		case OS_TASK_STATE_DLY:
		case OS_TASK_STATE_PEND:
		case OS_TASK_STATE_PEND_TIMEOUT:
			CPU_CRITICAL_EXIT();
			*p_err = OS_ERR_TASK_NOT_SUSPENDED;
			break;
		
		case OS_TASK_STATE_SUSPENDED:
			OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
			p_tcb->SuspendCtr--;
			if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0)
			{
				p_tcb->TaskState = OS_TASK_STATE_RDY;
				OS_TaskRdy (p_tcb);
			}
			OS_CRITICAL_EXIT_NO_SCHED();
			break;
		
		case OS_TASK_STATE_DLY_SUSPENDED:
			p_tcb->SuspendCtr--;
			if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0)
			{
				p_tcb->TaskState = OS_TASK_STATE_DLY;
			}
			CPU_CRITICAL_EXIT();
			break;
			
		case OS_TASK_STATE_PEND_SUSPENDED:
			p_tcb->SuspendCtr--;
			if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0)
			{
				p_tcb->TaskState = OS_TASK_STATE_PEND;
			}
			CPU_CRITICAL_EXIT();
			break;
			
		case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
			p_tcb->SuspendCtr--;
			if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0)
			{
				p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT;
			}
			CPU_CRITICAL_EXIT();
			break;
			
		default:
			CPU_CRITICAL_EXIT();
			*p_err = OS_ERR_STATE_INVALID;
			return;
	}
	
	OSSched();	/* 任务切换 */
	
	CPU_CRITICAL_EXIT();
}
#endif

3.3 任务删除函数 OSTaskDel()(os_task.c)

任务通常会运行在一个死循环中,也不会退出,如果一个任务不再需要,可以调用 uCOS 中的任务删除 API 函数接口显式地将其删除。

该函数用于将任务删除。所谓删除,就是将被删除的任务 TCB 移出就绪列表,同时清空该任务 TCB 的所有信息。

该函数完成的工作有:

然后根据任务的不同状态,执行不同的操作:

最后:

/* 任务删除函数 */
#if OS_CFG_TASK_DEL_EN > 0u
void OSTaskDel (OS_TCB *p_tcb, OS_ERR *p_err)
{
	CPU_SR_ALLOC();
	
	if (p_tcb == &OSIdleTaskTCB)	/* 不能删除空闲任务 */
	{
		*p_err = OS_ERR_TASK_DEL_IDLE;
		return;
	}
	
	if (p_tcb == (OS_TCB *)0)
	{
		CPU_CRITICAL_ENTER();
		p_tcb = OSTCBCurPtr;
		CPU_CRITICAL_EXIT();
	}
	
	OS_CRITICAL_ENTER();
	
	switch (p_tcb->TaskState)
	{
		case OS_TASK_STATE_RDY:
			OS_RdyListRemove (p_tcb);
			break;
		
		case OS_TASK_STATE_SUSPENDED:
			break;
		
		case OS_TASK_STATE_DLY:
		case OS_TASK_STATE_DLY_SUSPENDED:
			OS_TickListRemove (p_tcb);
			break;
		
		case OS_TASK_STATE_PEND:
		case OS_TASK_STATE_PEND_TIMEOUT:
		case OS_TASK_STATE_PEND_SUSPENDED:
		case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
			OS_TickListRemove (p_tcb);
		
		default:
			OS_CRITICAL_EXIT();
			*p_err = OS_ERR_STATE_INVALID;
			return;
	}
	
	OS_TaskInitTCB (p_tcb);
	p_tcb->TaskState = OS_TASK_STATE_DEL;
	
	OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
	
	OSSched();	/* 任务切换 */
	
	*p_err = OS_ERR_NONE;
}
#endif

4 任务管理的应用

4.1 主函数 main()(app.c)

修改两个任务:

#include "ARMCM3.h"
#include "os.h"

#define  TASK1_STK_SIZE       128
#define  TASK2_STK_SIZE       128
#define  TASK3_STK_SIZE       128

static   CPU_STK   Task1Stk[TASK1_STK_SIZE];
static   CPU_STK   Task2Stk[TASK2_STK_SIZE];
static   CPU_STK   Task3Stk[TASK3_STK_SIZE];

static   OS_TCB    Task1TCB;
static   OS_TCB    Task2TCB;
static   OS_TCB    Task3TCB;

uint32_t flag1;
uint32_t flag2;
uint32_t flag3;

void Task1 (void *p_arg);
void Task2 (void *p_arg);
void Task3 (void *p_arg);

/* 软件延时 */
void delay(uint32_t count);

int main (void)
{
	OS_ERR err;
	
	/* 初始化相关的全局变量,创建空闲任务 */
	OSInit(&err);
	
	/* CPU 初始化:初始化时间戳 */
	CPU_Init();
	
	/* 关中断,因为此时 OS 未启动,若开启中断,那么 SysTick 将会引发中断 */
	CPU_IntDis();
	
	/* 初始化 SysTick,配置 SysTick 为 10ms 中断一次,Tick = 10ms */
	OS_CPU_SysTickInit(10);
	
	/* 创建任务 */
	OSTaskCreate ((OS_TCB*)      &Task1TCB, 
	              (OS_TASK_PTR)  Task1, 
	              (void *)       0,
				  (OS_PRIO)		 1,
	              (CPU_STK*)     &Task1Stk[0],
	              (CPU_STK_SIZE) TASK1_STK_SIZE,
				  (OS_TICK)		 0,
	              (OS_ERR *)     &err);

	OSTaskCreate ((OS_TCB*)      &Task2TCB, 
	              (OS_TASK_PTR)  Task2, 
	              (void *)       0,
				  (OS_PRIO)		 2,
	              (CPU_STK*)     &Task2Stk[0],
	              (CPU_STK_SIZE) TASK2_STK_SIZE,
				  (OS_TICK)		 0,
	              (OS_ERR *)     &err);
				  
	OSTaskCreate ((OS_TCB*)      &Task3TCB, 
	              (OS_TASK_PTR)  Task3, 
	              (void *)       0,
				  (OS_PRIO)		 3,
	              (CPU_STK*)     &Task3Stk[0],
	              (CPU_STK_SIZE) TASK3_STK_SIZE,
				  (OS_TICK)		 0,
	              (OS_ERR *)     &err);
	
	/* 启动OS,将不再返回 */				
	OSStart(&err);
}

/* 软件延时 */
void delay (uint32_t count)
{
	for(; count!=0; count--);
}


void Task1 (void *p_arg)
{
	OS_ERR	err;
	
	for (;;)
	{
		flag1 = 1;
		OSTaskSuspend (&Task1TCB, &err);
		flag1 = 0;
		OSTaskSuspend (&Task1TCB, &err);
	}
}

void Task2 (void *p_arg)
{
	OS_ERR	err;
	
	for (;;)
	{
		flag2 = 1;
		OSTimeDly (2);		
		flag2 = 0;
		OSTimeDly (2);
		OSTaskResume (&Task1TCB, &err);
	}
}

void Task3 (void *p_arg)
{
	for (;;)
	{
		flag3 = 1;
		OSTimeDly (2);		
		flag3 = 0;
		OSTimeDly (2);
	}
}

4.2 运行过程

4.2.1 在主函数中

4.2.2 第一次在 Task1 中

4.2.3 第一次在 Task2 中

4.2.4 第一次在 Task3 中

4.2.5 在空闲任务中 SysTick 发起中断

4.2.6 第二次在 Task2 中

4.2.7 第二次在 Task3 中

4.2.8 在空闲任务中 SysTick 发起中断

4.2.9 第三次在 Task2 中

4.2.10 第二次在 Task1 中

如此反复,不再赘述。

4.3 实验现象

实验现象如下图所示,符合以上分析:

image

标签:11,TASK,uCOS,III,STATE,任务,TCB,OS,CPU
来源: https://www.cnblogs.com/Mount256/p/15895836.html