RTOS学习之旅(五)(ucosIII 支持多优先级)

之前OS还没有优先级,只能任务切换。在这之后加入了优先级,数字优先级越小,逻辑优先级越高。
其实,优先级这里部分就是对之前代码添加一些优先级有关的信息,其他不变。

1.定义优先级相关全局变量

在支持任务多优先级的时候,需要在 os.h 头文件添加两个优先级相关的全局变量。

//当前优先级
OS_EXT     OS_PRIO      OSPrioCur
//最高优先级
OS_EXT     OS_PRIO      OSPrioHighRdy

2.修改OSInit()

刚刚添加的全局变量,需要在OSInit()中进行初始化。

void OSInit(OS_ERR *p_err)
{
	OSRunning = OS_STATE_OS_STOPPED;
	
	OSTCBCurPtr = (OS_TCB *)0;
	OSTCBHighRdyPtr = (OS_TCB *)0;
	
	OSPrioCur      =  (OS_PRIO)0;
	OSPrioHighRdy  =  (OS_PRIO)0;
	
	//初始化优先级表
	OS_PrioInit();

	//初始化就绪列表
	OS_RdyListInit();

	OS_IdleTaskInit(p_err);
	if(*p_err != OS_ERR_NONE)
	{
		return;
	}
}

3.修改任务控制块TCB

在任务的结构体中加入一项优先级Prio的数据类型OS_PRIO。是8位整型,所以只支持255个优先级

struct os_tcb{
	CPU_STK       *StkPtr;
	CPU_STK_SIZE  StkSize;
	
	OS_TICK       TaskDelayTicks;
	//任务优先级
	OS_PRIO       Prio;

	OS_TCB        *NextPtr;
	OS_TCB        *PrevPtr;
};

4.修改OSTaskCreate()函数

viod OSTaskCreate(OS_TCB          *p_tcb,
				  OS_TASK_PTR     P_task,
				  void            *p_arg,
				  OS_PRIO         prio,
				  CPU_STK         *p_stk_base,
				  CPU_STK_SIZE    stk_size,
				  OS_ERR          *p_err)
{
	CPU_STK     *p_sp;
	CPU_SR_ALLOC;

	OS_TaskInitTCB(p_tcb);

	p_sp = OSTaskStkInit(p_task,
						 p_arg,
						 p_stk_base,
						 stk_size);
	p_tcb->Prio = prio;

	p_tcb->StkPtr  = p_sp;
	p_tcb->StkSize  = stk_size;

	OS_CRITICAL_ENTER();

	OS_PrioInsert(p_tcb->Prio);         //(1)
	OS_RdyListInsertTail(p_tcb);

	OS_CRITICAL_EXIT();

	*p_err  =  OS_ERR_NONE;
}

(1)将任务插入就绪列表中,这里分成两步

  • 根据优先级置位优先级表中相应位置
  • 将TCB放到OSRdyList[优先级]中,如果同个优先级有多个任务,那么这些任务TCB就会被放到OSRdyList[优先级]串成一个双向链表。
void OS_TaskInitTCB(OS_TCB *p_tcb)
{
	p_tcb->StkPtr            = (CPU_STK      *)0;
	p_tcb->StkSize           = (CPU_STK_SIZE  )0u;

	p_tcb->TaskDelayTicks    =(OS_TICK       )0u;

	p_tcb->Prio              =(OS_PRIO       )OS_PRIO_INIT;

	p_tcb->NextPtr           =(OS_TCB       *)0;
	P_tcb->PrevPtr           =(OS_TCB       *)0;
}

5.修改OS_IdleTaskInit()函数

这个函数调用了OSTaskCreate(),要给空闲任务分配一个优先级。

void OS_IdleTaskInit(OS_ERR   *p_err)
{
	OSIdleTaskCtr  =  (OS_IDLE_CTR)0;

	OSTaskCreate((OS_TCB      *)&OSIdleTaskTCB,
				 (OS_TASK_PTR  )OS_IdleTask,
				 (void        *)0,
				 (OS_PRIO)(OS_CFG_PRIO_MAX - 1u),
				 (CPU_STK *)OSCfg_IdleTaskStkBasePtr,
				 (CPU_STK_SIZE)OSCfg_IdleTaskStkSize,
				 (OS_ERR)p_err);
}

空闲任务就会被运行,优先级最低。

6.修改OSStart()函数

具体哪个任务先运行,由优先级决定

void OSStart(OS_ERR  *p_err)
{
	if(OSRunning == OS_STATE_OS_STOPPED){
#if 0
		OSTCBHighRdyPtr  =  OSRdyList[0].HeadPtr;
#endif
//寻找最高优先级
	OSPrioHighRdy   = OS_PrioGetHighest();
	OSPrioCur       = OSPrioHighRdy;
//找到最高优先级的TCB
	OSTCBHighRdyPtr  =  OSRdyList[OSPrioHighRdy].HeadPtr;
	OSTCBCurPtr      =  OSTCBHighRdyPtr;

	OSRunning        = OS_STATE_OS_RUNNING;

	OSStartHighRdy();

	*p_err   = OS_ERR_FATAL_RETURN;
	}
	else{
	*p_err   = OS_STATE_OS_RUNNING;
	}
}
  1. 首先调用OS_PrioGetHighest()函数从全局优先级表中获取最高优先级,然后把值赋给当前优先级OSPrioCur中。
  2. 根据OSPrioHighRdy值,在OSRdyList[]下标索引中找到最高优先级任务TCB,赋给OSPrioHighRdyPtr,然后赋给OSPrioCurPtr。

7.修改OSTimeDly()函数

调用OSTimeDly()函数之后,任务就处于阻塞态,需要将任务从就绪列表中移除。

void OSTimeDly(OS_TICK_dly)
{
#if 0
	OSTCBCurPtr->TaskDelayTicks = dly;

	OSSched();
#endif
	CPU_SR_ALLOC();
	OS_CRITICAL_ENTER();

	OSTCBCurPtr->TaskDelayTicks = dly;

	//从就绪列表中移除
	//OS_RdyListRemove(OSTCBCurPtr);
	OS_PrioRemove(OSTCBCurPtr->Prio);

	OS_CRITICAL_EXIT();

	OSSched();
}

将任务从就绪列表中移除,这里只需将任务在优先级表中对应位清除就行。

8.修改OSSched()函数

任务调度函数不再是之前两个任务轮流切换了,需要根据优先级来调度。

void OSSched(void)
{
	#if 0
		if(OSTCBCurPtr == &OSIdleTaskTCB)
		{
			if(OSRdyList[0].HeadPtr->TaskDelayTicks == 0)
			{
				OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;
			}
			else if(OSRdyList[1].HeadPtr->TaskDelayTicks == 0){
				OSTCBHighRdyPtr = OSRdtList[1].HeadPtr;
			}
			else{
				return;			
			}
		}
		else{
			if(OSTCBCurPtr == OSRdyList[0].HeadPtr)
			{
				if(OSRdyList[1].HeadPtr->TaskDelayTicks == 0)
				{
					OSTCBHighRdyPtr = OSRdyList[1].HeadPtr;
				}
			else if(OSTCBCurPtr->TaskDelayTicks != 0)
			{
				OSTCBHighRdyPtr = &OSIdleTaskTCB;
			}
			else {
				return;
				}
			}
			else if(OSTCBCurPtr == OSRdyList[1].HeadPtr)
			{
				if(OSRdyList[0].HeadPtr->TaskDelayTicks == 0)
				{
					OSRdyListPtr = OSRdyList[0].HeadPtr;
				}
				else if(OSTCBCurPtr->TaskDelayTicks != 0)
				{
					OSTCBHighRdyPtr = &OSIdleTaskTCB;
				}
				else {
					return ;				
				}
			}
		}
		OS_TASK_SW();
	#endif

	CPU_SR_ALLOC();

	OS_CRITIACL_ENTER();

	OSPrioHighPtr = 0S_PrioGetHighest();
	OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;

	if(OSTCBHighRdyPtr == OSTCBCurPtr)
	{
		OS_CRITICAL_EXIT();
		return ;
	}
	OS_CRITICAL_EXIT();

	OS_TASK_SW();
}

9.修改OSTimeTick()函数

OSTimeTick()函数在SysTicks中断服务函数中被调用,用于扫描就绪列表OSRdyList[],判断任务延时是否到期,若到期则将任务在优先级表中相应位置位。

void OSTimeTick(void)
{
	unsigned int i;
	CPU_SR_ALLOC();

	OS_CRITICAL_ENTER();

	for(i = 0;i < OS_CFG_PRIO_MAX;i++)
	{
		if(OSRdyList[i].HeadPtr->TaskDelayTicks > 0)
		{
			OSRdyList[i].HeadPtr->TaskDelayTicks--;
		}
		if(OSRdyList[i].HeadPtr->TaskDelayTicks == 0)
		{
			//为0表示延迟时间到,让任务就绪
			//OSRdyListInsert(OSRdyList[i].HeadPtr);
			OS_PrioInsert(i);
		}
	}
}

10.Main函数

#include "os.h"
#include "ARMCU3.h"

uint32_t flag1;
uint32_t flag2;

//TCB,STACK声明
#define TASK1_STK_SIZE 20
#define TASK2_STK_SIZE 20
#define TASK3_STK_SIZE 20

static CPU_STK TASK1Stk[TASK1_STK_SIZE];
static CPU_STK TASK2Stk[TASK2_STK_SIZE];
static CPU_STK TASK3Stk[TASL3_STK_SIZE];

static OS_TCB TASK1TCB;
static OS_TCB TASK2TCB;

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

int main(void)
{
	OS_ERR err;
	CPU_Init();

	//关闭中断
	CPU_IntDis();

	//配置10ms中断一次
	OS_CPU_SysTickInit(10);

	//初始化全局变量
	OSInit(&err);
	
	OSTaskCreate((OS_TCB *)     &Task1TCB,
				 (OS_TASK_PTR)  Task1,
				 (void * )      0,
				 (OS_PRIO)      1,
				 (CPU_STK *)    &Task1Stk[0],
				 (CPU_STK_SIZE) Task1_STK_SIZE,
				 (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_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_ERR *)     &err);
	#if 0
	//将任务假如到就绪列表
	OSRdyList[0].HeadPtr = &Task1TCB;
	OSRdyList[1].HeadPtr = &Task2TCB;
	#endif
	
	//启动OS,不再返回
	OSStart(&err);

	void Task1(void *p_arg)
	{
		while(1)
		{
			flag1 =1;
			delay(100);
			flag1 = 0;
			delay(100);
			//手动切换任务
			OSOched();
		}
	}
	void Task2(void *p_arg)
	{
		while(1)
		{
			flag2 =1;
			delay(100);
			flag2 = 0;
			delay(100);
			//手动切换任务
			OSOched();
		}
	}
}

11.分析

可以看到三个任务波形是同步的,就好像CPU在同时干三件事。
在这里插入图片描述
在这里插入图片描述
实际放大看,还是有点差距的。
当任务1开始运行,运行0.26ms调用OSTimeDly(1)进入延时,然后进行任务切换,切换到任务2开始运行,切换时间为0.01ms。
以此类推,,当任务都切换时,3个任务均进入了延时阻塞状态,但是没到延时的10ms,所以系统就切换到空闲任务。延时未到期之前,系统都进行空闲任务。
当第一个SysTick中断产生,中断调用OSTimeTick()扫描每个任务是否到期,如果到期之后,任务1,2,3依次进入就绪状态。就可以重复上面的过程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值