【17】从0到1教你写uC/OS-III————>支持多优先级

  1. 简述:
    1. 本章之前OS还没有用到优先级,只支持两个任务相互切换;
    2. 本章之后加入优先级功能,在uC/OS-III中,数字优先级越小,逻辑优先级越高;
    3. 修改之前如下几个函数:
      1. OSInit();
      2. 任务控制块TCB;
      3. 任务创建OSTaskCreate();
      4. 空闲函数OS_Idle_TaskInit();
      5. 系统启动函数OSStart();
      6. 任务切换PendSV_Handler
      7. 系统延时OSTimeDly();
      8. 任务调度OSSched();
      9. 系统时基OSTimeTick();
  2. 例程:
    1. OSInit();
      /*********************************************************
       * 函数名:void    OSInit(OS_ERR   *p_err); 
       * 描述  :初始化全局变量,就绪列表
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
       void    OSInit(OS_ERR   *p_err)
       {
           //配置OS初始态为停止态
           OSRunning = OS_STATE_OS_STOPPED;   
           
           //初始化两个全局TCB,这两个TCB用于任务切换;
           OSTCBCurPtr = (OS_TCB *)0;
           OSTCBHighRdyPtr = (OS_TCB *)0;
           
           //初始化优先级变量
           OSPrioCur  =   (OS_PRIO)0;
           OSPrioHighRdy  =   (OS_PRIO)0;
           
           //任务就绪列表初始化
           OS_RdyListInit();      
           
           //初始化空闲任务
           OS_IdleTaskInit(p_err);
           
           if(*p_err != OS_ERR_NONE)
           {
              return;
           }
       }

       

    2. 任务控制块TCB;
      /*
      ************************************************************************************************************************
      *                                              OS_TCB 任务控制块
      ************************************************************************************************************************
      */    
      struct os_tcb
      {
          CPU_STK         *StkPtr;
          CPU_STK_SIZE    StkSize;
          
          //任务延时周期个数
          OS_TICK         TaskDelayTicks;
          
          //任务优先级
          OS_PRIO         Prio;
          
          //就绪列表的下一个指针
          OS_TCB          *NextPtr;
          //就绪列表的上一个指针
          OS_TCB          *PrevPtr;
      };

       

    3. 任务创建OSTaskCreate();
      /*********************************************************
       * 函数名:
                   void OSTaskCreate ( OS_TCB          *p_tcb,             //任务控制块指针
                                      OS_TASK_PTR     p_task,               //任务函数名
                                      void            *parg,              //形参,用于传递参数
                                      OS_PRIO         prio,               //任务优先级
                                      CPU_STK         *p_stk_base,        //指向任务堆栈的起始地址
                                      CPU_STK_SIZE    stk_size,           //任务堆栈的大小
                                      OS_ERR          *p_err              //错误码
                                      );
       * 描述  :创建任务
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void 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中断前的状态
          CPU_SR_ALLOC();
          
          //初始化TCB为默认值
          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_sp保存到任务控制块TCB的第一个成员StkPtr中;
          p_tcb ->StkSize = stk_size; //任务堆栈大小保存到任务控制块TCB成员的StkSize中;
          
          //进入临界段
          OS_CRITICAL_ENTER();
          
          //将任务添加到就绪列表
          OS_PrioInsert(p_tcb->Prio);
          OS_RdyListInsertTail(p_tcb);
          
          //退出临界段
          OS_CRITICAL_EXIT();
          
          *p_err = OS_ERR_NONE;       //函数执行到此表示无错误,返回OS_ERR_NONE;
      }

       

    4. 空闲函数OS_Idle_TaskInit();
       /*********************************************************
       * 函数名:OS_IdleTaskInit(OS_ERR  *p_err);                    
       * 描述  :空闲任务初始化,放在系统初始化中
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void    OS_IdleTaskInit(OS_ERR  *p_err)
      {
          //初始化空闲任务计数器,设置为0
          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
                      );
          
      }

       

    5. 系统启动函数OSStart();
        /*********************************************************
       * 函数名: void    OSStrart(OS_ERR *p_err)      
       * 描述  :系列启动 
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
       void    OSStart(OS_ERR *p_err)
       {
           if(OSRunning == OS_STATE_OS_STOPPED)
           {
               //寻找最高优先级
               OSPrioHighRdy = OS_PrioGetHighest();
               OSPrioCur = OSPrioHighRdy;
               
               //找到最高优先级的TCB
               OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;
               OSTCBCurPtr = OSTCBHighRdyPtr;
               
               //标记OS 开始运行
               OSRunning = OS_STATE_OS_RUNNING;
               
               
               //启动任务切换,不会返回
               OSStartHighRdy();
               
               //不会运行到这里,如果运行到这里表示发生了致命错误
               *p_err = OS_ERR_FATAL_RETURN;
           }
           else
           {
              *p_err = OS_STATE_OS_RUNNING;
           }
       }

       

    6. 任务切换PendSV_Handler
      ;************************************************************************************************************************
      ;                                              PendSVHandler异常
      ;************************************************************************************************************************
      PendSV_Handler
      ;任务的保存,即把CPU寄存器的值存储到任务的堆栈中
      	CPSID	I						;关中断,NMI和HardFault除外,防止上、下文切换被中断
      	MRS		R0,	PSP					;将PSP的值加载到R0
      	CBZ		R0, OS_CPU_PendSVHandle_nosave	;判断R0,如果值为0则跳转到OS_CPU_PendSVHandle_nosave
      									;进行第一次任务切换的时候,r0肯定为0
      									
      ;在进入PendSV异常的时候,当前CPU的xPSR,PC(任务入口地址),R14\R12\R3\R2\R1\R0会自动存储到当前任务堆栈,同时递减PSP的值 
      	STMDB	R0!, {R4-R11}			;手动存储CPU寄存器R4-R11的值 到当前任务的堆栈
      	
      	LDR		R1,	= OSTCBCurPtr		;加载OSTCBCurPtr指针的地址到R1,这里LDR属于伪指令
      	LDR		R1,	[R1]				;加载OSTCBCurPtr指针到R1,这里LDR属于ARM指令
      	STR		R0,	[R1]				;存储R0的值到 OSTCBCurPtr->OSTCBStkPtr,这个时候R0存的是任务空闲栈的栈顶
      	
      ;任务的切换,即把下一个要运行任务的堆栈内容加载到CPU寄存器中
      OS_CPU_PendSVHandle_nosave
      	; OSPrioCur   = OSPrioHighRdy;
          LDR     R0, =OSPrioCur                                      
          LDR     R1, =OSPrioHighRdy
          LDRB    R2, [R1]
          STRB    R2, [R0]
      
      	;OSTCBCurPtr	=	OSTCBHighRdyPtr;
      	LDR		R0,	= OSTCBCurPtr		;加载OSTCBCurPtr指针的地址到R0,这里LDR属于伪指令
      	LDR		R1,	= OSTCBHighRdyPtr	;加载OSTCBHighRdyPtr指针的地址到R1,这里LDR属于伪指令
      	LDR		R2,	[R1]				;加载OSTCBHighRdyPtr指针到R2,
      	STR		R2,	[R0]				;存储OSTCBHighRdyPtr到OSTCBCurPtr
      	
      	LDR		R0,	[R2]				;加载OSTCBHighRdyPtr到R0
      	LDMIA	R0!, {R4-R11}			;加载需要手动保存的信息到CPU寄存器R4-R11
      	
      	;更新PSP的值,这时PSP指向下一个要执行的任务的堆栈的栈底(这个栈底已经加上刚刚手动加载到CPU寄存器R4-R11的偏移)
      	MSR		PSP, R0					
      	ORR		LR,	 LR,	#0x04		;确保异常返回使用的堆栈指针是PSP,即LR寄存器的bit2为1
      	CPSIE	I						;开中断
      	
      	;异常返回,这个时候任务堆栈中的剩下内容将会自动加载到xPSR,PC(任务入口地址)R14\R12\R3\R2\R1\R0(任务的形参)
      	;同时PSP的值也将更新,即指向任务堆栈的栈顶,在STM32中,堆栈是由高地址向低地址生长
      	BX		LR						
      
      	NOP								;汇编指令对齐,不然会有警告

       

    7. 系统延时OSTimeDly();
      /*********************************************************
       * 函数名:void OSTimeDly(OS_TICK dly);
       * 描述  :阻塞延时
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OSTimeDly(OS_TICK dly)
      {
          //保存CPU中断状态 
          CPU_SR_ALLOC();
          //进入临界区
          CPU_CRITICAL_ENTER();
          
          //设置延时时间
          OSTCBCurPtr->TaskDelayTicks = dly;
          
          //从就绪列表中移除
          OS_PrioRemove(OSTCBCurPtr->Prio);
          
          //退出临界区
          CPU_CRITICAL_EXIT();
          
          //任务调度
          OSSched();
      }

       

    8. 任务调度OSSched();
       /*********************************************************
       * 函数名: void    OSSched(void);                       
       * 描述  :任务调度
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void    OSSched(void)
      {
          //保存CPU中断状态 
          CPU_SR_ALLOC();
          //进入临界区
          CPU_CRITICAL_ENTER();
          
          //查找最高优先级的任务
          OSPrioHighRdy = OS_PrioGetHighest();
          OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;
          
          //如果最高优先级的任务是当前任务,则直接返回,不进行任务切换
          if(OSTCBHighRdyPtr == OSTCBCurPtr)
          {
              //退出临界区
              OS_CRITICAL_EXIT();
              return;
          }
          
          //退出临界区
          CPU_CRITICAL_EXIT();    
      
          OS_TASK_SW();   //触发PendSV异常
      }

       

    9. 系统时基OSTimeTick();
      /*********************************************************
       * 函数名:OSTimeTick(void)
       * 描述  :调用任务调度函数
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OSTimeTick(void)
      {
          CPU_INT32U  i;
          
          //保存中断CPU状态
          CPU_SR_ALLOC();
          //进入临界区
          OS_CRITICAL_ENTER();	
          
          //扫描就绪列表中所有任务的TaskDelayTicks,如果不为0,则减1
          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表示延时时间到,让任务就绪
                      OS_PrioInsert(i);
                  }
              }
          }
          //退出临界区
          OS_CRITICAL_EXIT();
          OSSched();  //任务调度
      }

       

  3. 总结:本节是对之前功能的修改完善;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值