dragonnk 发表于 2014-10-10 14:11:13

任务调度程序

下面是我编写的一个比较粗糙的任务调度程序的几个重要的函数,有兴趣的朋友可供参考。
/*
注:在进行任务调度前要设置好处理器各个模式的堆栈,设置中断向量由VIC决定,开中断,进入USR模式,各任务必须在   USR模式下运行。定义了有4个成员的任务控制块结构体数组,也就是说最多能运行四个任务。这里设置的是静态任务,任   务和系统一起编译,不能删除任务(太坑了)。呵呵,以后再慢慢修改。
*/      
#define Max_tasks 4      //定义最大任务数
struct PCB{                     //任务控制块结构体,用于保存任务上下文信息
unsigned int r0;
unsigned int r1;
unsigned int r2;
unsigned int r3;
unsigned int r4;
unsigned int r5;
unsigned int r6;
unsigned int r7;
unsigned int r8;
unsigned int r9;
unsigned int r10;
unsigned int r11;
unsigned int r12;
unsigned int sp_usr;
unsigned int lr_usr;
unsigned int pc_usr;         //IRQ模式下的lr-4
unsigned int cpsr_usr;       //IRQ模式下的spsr
unsigned int state;             //任务状态
unsigned int pid;                //任务编号
};

extern struct PCB PCB_table;   //任务控制块结构体数组
extern struct PCB *cur_PCB;                            //当前任务控制块结构指针
/*
   创建任务,将任务起始地址,堆栈指针,及spsr值写入控制块
*/
void create(void (*fun)())
{
unsigned int i;
struct PCB *foke_pcb=PCB_table;
for(i=0;i<Max_tasks;i++)
{
if(foke_pcb->state==0)
{
foke_pcb->pc_usr=(unsigned int )fun;
foke_pcb->sp_usr=(0x54000000+0x200000*i);          /*人工分配的堆栈*/
foke_pcb->cpsr_usr=0x150;                        /*返回到USR模式*/
foke_pcb->state=1;                                     /*只有两个状态0代表不能运行,1代表能运行*/
break;
}
foke_pcb++;
}
}
/*查找下一个可运行的程序,顺序执行*/
void ToNext()
{
while(1)
{
if(cur_PCB->pid==(Max_tasks-1))
cur_PCB=PCB_table;
else
cur_PCB++;
if(cur_PCB->state!=0)
break;
}
}
/*
   下面是任务切换程序, 是至关重要的一个函数,作为定时器中断的服务函数,进入中断后处理器进入
    IRQ模式,执行此函数。总的步骤是先保存当前任务的内核寄存器信息和当前任务返回地址,再调用任何调度程序,
    确定下一个要执行的任务,从下一个任务的PCB中恢复寄存器信息,中断返回时PC装载下一任务的返回地址。
   说简单点就是在当前任务进入中断,然后中断返回进入另一个任务。
*/
TaskSwitch:
    SUB lr,lr,#4                   /*计算返回地址pc_usr*/
    LDR sp,=cur_PCB      /*IRQ堆栈指向当前PCB结构开头,标号为指针变量的地址*/
    LDR sp,
    STMIA sp,{r0-r14}^    /*保存usr寄存器r0-r14(lr)到PCB结构体中,sp不能回写,后面不能有!*/
    STR lr,!      /*保存返回地址pc_usr到结构体,IRQ模式下lr-4*/
    MRS r0,spsr
    STR r0,            /*保存spsr*/

    LDR r0,=0x7F006044      /*清除定时器中断,S3C6410芯片*/
    LDR r1,
    ORR r1,r1,#0x80
    STR r1,

    LDR sp,=IRQ_StackBase /*恢复中断堆栈指针*/

    BL ToNext                        /*调用任务调度程序,确定下一要运行任务,cur_PCB 指向下一任务PCB*/
    BL interrupt_isrend          /*清零中断地址寄存器,以便下次中断能重新进入中断服务程序*/


    LDR sp,=cur_PCB          /*指向下一任务PCB*/
    LDR sp,
    LDR r0,
    MSR spsr,r0                   /*恢复spsr*/
    LDR lr,             /*恢复IRQ模式lr作为返回地址*/
    LDMFD sp,{r0-r14}^    /*恢复USR寄存器,不能与PC同时恢复*/
    movs pc,lr                      /*中断返回*/



/*
   创建主函数时,要开启定时器(定时器计数值决定每一任务运行多久再切换到下一任务,设置好周期性的中断,并设置
   中断处理函数的入口地址为TaskSwitch。先要对PCB结构体初始化 ,状态置0,依次给结构体任务编号赋值。主函数可以
   创建一个空任务(空循环),再调用创建任务函数以任务函数名作参数,创建任务,创建好后在主函数里调用空任务函数
   即可。中断到来时将会切换到第一个创建的任务,然后各任务依次循环运行。
*/

飞凌-unix 发表于 2014-10-13 09:02:34

多谢楼主在这里分享自己的成果,^_^,赞一个
页: [1]
查看完整版本: 任务调度程序