• 合作式调度器为各种嵌入式系统提供了一个可预测的平台。
  • 在一些场合,可能需要在合作式调度器结构中加热抢占式调度器的一些特性,并小心地加以控制。

从实用角度出发,合作式调度器也有局限性。尤其是在一些情况下,可能既需要运行长任务(例如每隔1000ms运行100ms时间),同时又需要运行频繁的短任务(例如每隔1ms运行0.1ms)。

image.png
这种结构综合了抢占式调度器和合作式调度器的特性,允许根据需求使(长)任务优先。然而,这种方式始终是受控的,不需要依赖复杂的上下文切换程序,以及任务间复杂的通信机制。
当已经建立了合作式调度器时,只需做非常少的程序改动就可以把它修改为混合式调度器。
在混合式调度器中,已经没有了(纯粹的)合作式的调度特性。这可能对设计过程及最终系统的可靠性带来深远的影响。

这里描述的混合式调度器形式和(纯粹的)合作式调度器区别如下:

  • 不再要求所有任务都必须在时标间隔之间完成。一个(或更多)的合作式任务的运行时间可以大于时标间隔
  • 于前面讨论的合作式调度器一样,可以调度任意个合作式任务。然而,还可以同时调度一个抢占式任务
  • 抢占式任务抢先(中断)合作式任务。
  • 一旦抢占式任务开始运行,将一直运行到完成。即,抢占式任务不能被任何合作式任务打断。抢占式任务可以看作是“最高优先权”的任务。

同时注意:

  • 与完全的抢占式解决方案相比,只有一个抢占式任务,而且该任务连续运行直到完成,这将极大地简化系统结构。尤其是不需要实现上下文切换机制。这意味着:
    • 该结构仍然非常简单
    • 运行环境可以仍然全部由C来实现
  • 和完全的抢占式平台相比,抢占式任务连续运行直到完成同样将简化任务间的通信
  • 应该只有一个短任务(最长的运行时间大约为时标间隔的50%,尽可能的短)可以抢占运行,否则将削弱系统的总体性能。

混合式调度器“刷新”函数
image.png

  1. typedef struct
  2. {
  3. void (*pTaskTypeDef)(void);
  4. tWord Delay;
  5. tWord Period;
  6. tByte Co_op; //合作式任务,设为1; 抢占式任务,设为0
  7. }sTask;
  8. void hSCH_Update(void) interrupt
  9. {
  10. tByte Index;
  11. for(Index=0; Index<HSCH_MAX_TASS; Index++)
  12. {
  13. //检测是否有任务
  14. if(hsch_tasks_G[Index].pTask)
  15. {
  16. if(psch_tasks_G[Index].Delay == 0)
  17. {
  18. //有任务需要运行
  19. if(hsch_tasks_G[Index].Co_op)
  20. {
  21. //合作式任务, RunMe标志加1
  22. hsch_tasks_G[Index].RunMe += 1;
  23. }
  24. else
  25. {
  26. //抢占式任务, 立即运行
  27. (*hsch_tasks_G[Index].pTask)();
  28. if(hsch_tasks_G[Index].period == 0)
  29. {
  30. hsch_tasks_G[Index].pTask = 0;
  31. }
  32. }
  33. if(hsch_tasks_G[Index].period)
  34. {
  35. //再次调度
  36. hsch_tasks_G[Index].Delay = hsch_tasks_G[Index].period;
  37. }
  38. }
  39. else
  40. {
  41. psch_tasks_G[Index].Delay--;
  42. }
  43. }
  44. }
  45. }

image.png

混合式调度器的最可靠使用方法

  • 建立数量满足要求的合作式任务,很可能因为一个或多个任务的运行时间会大于时标间隔,所以需要使用混合式的调度器。混合式调度器的实现是安全的,然而必须保证任务不重叠
  • 实现一个抢占式任务。该任务一般(并非一定)将在每个时标间隔调用。这种任务的一种不错的应用是用来检查错误或紧急事件。因此,即使系统的主要用途是运行一个1000ms的合作式任务,也可以用抢占式任务来保证系统能够在10ms内响应外部事件。
  • 记住,抢占式任务能够中断合作式任务。如果有关键代码段,需要实现一种简单的锁定机制
  • 抢占式任务必须简短(最长的运行时间大约为时标间隔的50%,应尽可能短),否则将极大削弱系统的总体性能。
  • 在所有运行状态下仔细测试该系统,监测错误

    优缺点

  • 能够同时处理“很少发生的长任务”和“频繁发生的短任务”,而合作式调度器不能同时处理

  • 如果按照指导使用,将是安全并且可预测的
  • 必须小心使用

    相关模式和替代解决方案

  • 合作式调度器

  • SCU调度器(本地)
  • SCC调度器

使用T2的混合式调度器(1ms时标)

  • sch51.c

    //如果不需要错误报告,将这一行注释掉
    //#define SCH_REPORT_ERRORS
    #ifdef SCH_REPORT_ERRORS
    //用来显示错误代码的端口,旨在报告错误时使用
    #define ErrorPort P1
    #endif
    
  • led_hyb.c

    //两个任务公用这个端口P2
    //sbit LED_short_pin P2^2;
    //将把它当作共享资源, 并“锁定”它
    #define    LED_long_port P2
    
  • main.c

    //混合式调度器的演示程序
    //HYBRID SCHEDULER
    #include "main.h"
    #include "2_01_12h.h"
    void main()
    {
      //设置调度器
      hsch_init_t2();
      led_short_init();
      //添加"短"任务
      //这是一个抢占式任务
      hsch_add_task(led_short_update, 0, 1000, 0);
      //添加“长”任务
      //这是一个合作式任务
      hsch_add_task(led_long_update, 0, 20000, 1);
      //启动调度器
      hsch_start();
      while(1)
      {
          hsch_dispatch_task();
      }
    }
    
  • 2_01_12h.c ```c //这是用于标准8051/8052的一种混合式调度器

    include “2_01_12h.h”

    //任务队列(参见SCH51.c) extern sTaskH hSCH_tasks_G[hSCH_MAX_TASKS]; //错误代码 extern tByte Error_code_G;

void hSCH_Init_T2() { tByte i; for(i=0; i<hSCH_MAX_TASKS; i++) { hSCH_Delete_task(i); } //错误变量 Error_code_G = 0; //定时器设置T2 //.. }

void hSCH_Start() { EA = 1; }

void hSCH_Update(void) interrupt xxx { tByte Index; for(Index=0; Index<hSCH_MAX_TASKS; Index++) { //检测是否有任务 if(hsch_tasks_G[Index].pTask) { if(psch_tasks_G[Index].Delay == 0) { //有任务需要运行 if(hsch_tasks_G[Index].Co_op) { //合作式任务, RunMe标志加1 hsch_tasks_G[Index].RunMe += 1; } else { //抢占式任务, 立即运行 (*hsch_tasks_G[Index].pTask)(); // hsch_task_G[Index].RunMe -= 1; if(hsch_tasks_G[Index].period == 0) { hsch_tasks_G[Index].pTask = 0; } }

            if(hsch_tasks_G[Index].period)
            {
                //再次调度
                hsch_tasks_G[Index].Delay = hsch_tasks_G[Index].period;
            }
        }
        else
        {
            psch_tasks_G[Index].Delay--;
        }
    }
}

}


- hSCH51.h
```c
#ifndef HSCH51_H
#define HSCH51_H

//公共数据类型声明
typedef data struct{
    void (*pTask)();
    tWord Delay;
    tWord Period;
    tByte RunMe;
    tByte Co_op;
}sTaskH;

//公共函数声明
void hSCH_Dispatch_Tasks();
tByte hSCH_Add_Task(void(*)(), tWord, tWord, tByte);
tByte hSCH_Delete_Task(tByte);
void hSCH_Report_Status();
//公共常数
#define hSCH_MAX_TASKS    (2)    //根据项目需求调整
#endif
  • hSCH51.c ```c //混合式调度器内核 //任务队列 sTaskH hSCH_tasks_G[hSCH_MAX_TASKS]; //错误代码 tByte Error_code_G; // static void hSCH_GoTo_Sleep(); //记录上一次错误以来的时间 static tWord Error_tick_count_G; //上一次的错误代码 static tByte Last_error_code_G;

void hSCH_Dispatch_Tasks() { tByte Index; for(Index=0; Index0)) { (*hSCH_tasks_G[Index].pTask)(); hSCH_tasks_G[Index].RunMe -= 1; //单次任务, 从队列中删除 if(hSCH_tasks_G[Index].Period==0) { hSCH_tasks_G[Index].pTask = 0; //比通过调用删除任务函数更快, 直接将任务函数删除 } } } //报告系统状态 hSCH_Report_Status(); //调度器进入空闲模式 hSCH_GoTo_Sleep(); }

tByte hSCH_Add_Task(void (*Fn_p)(), tWord Del, tWord Per, tByte co_op) { tByte Index=0; //在队列中找到一个空隙 while(hSCH_tasks_G[Index].pTask!=0 && Index<hSCH_MAX_TASKS) { Index++; } //是否到达队列尾部 if(Index==hSCH_MAX_TASKS) { //队列满 Error_code_G = ERROR_SCH_TOO_MANY_TASKS; return hSCH_MAX_TASKS; } //队列中有空隙 hSCH_tasks_G[Index].pTask = Fn_p; hSCH_tasks_G[Index].Delay = Del; hSCH_tasks_G[Index].Period = Per; hSCH_tasks_G[Index].Co_op = co_op; hSCH_tasks_G[Index].RunMe = 0; return Index; //返回任务的位置(以便以后删除) }

tByte hSCH_Delete_Task(tByte Task_index) { tByte Return_code; if(hSCH_tasks_G[Task_index].pTask == 0) { //没有任务 Error_code_G = ERROR_SCH_CANNOT_DELETE_TASKS; Return_code = RETURN_ERROR; } else { Return_code = RETURN_NORMAL; } //删除任务 hSCH_tasks_G[Index].pTask = 0; hSCH_tasks_G[Index].Delay = 0; hSCH_tasks_G[Index].Period = 0; hSCH_tasks_G[Index].Co_op = 0; hSCH_tasks_G[Index].RunMe = 0; return Return_code; }

void hSCH_Report_Status() {

ifdef SCH_REPORT_ERRORS

//只在需要报告错误时适用
if(Error_code_G != Last_error_code_G)
{
    //检查新的错误代码
    Last_error_code_G = Error_code_G;
    if(Error_code_G != 0)
        Error_tick_count_G = 60000;
    else
        Error_tick_count_G = 0;
}
else
{
    if(Error_tick_count_G != 0)
    {
        if(--Error_tick_count_G == 0)
        {
            //复位错误代码
            Error_code_G = 0;
        }
    }
}

endif

}

void hSCH_GoTo_Sleep() { PCON |= 0x01; //进入空闲模式 }


- LED_Hyb.c
```c
//用于混合式调度器的简单的“闪烁LED”的测试函数
//具有锁定机制

//用于锁定机制
#define LOCKED    1
#define UNLOCKED    0
//
static tByte LED_short_state_G;
//锁定标志
static tByte LED_lock_G = UNLOCKED;

void LED_Flash_Init()
{
    //准备闪烁LED
}

void Flash()
{
    if(LED_lock_G == LOCKED)
    {
        return;
    }
    //端口空闲--锁定它
    LED_lock_G = LOCKED;
    for(i=0; i<5; i++)
    {
        LED_long_port = 0x0f;
        Hardware_Delay_T0(1000);
        LED_long_Port = 0xf0;
        Hardware_Delay_T0(1000);
    }
    //放开端口
    LED_lock_G = UNLOCKED;
}