51多任务系统,可以运行

时间:2022-07-24
本文章向大家介绍51多任务系统,可以运行,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

根据网上的资料,采用定时器2自动装载模式。每10ms进一次中断。

图中可以看出,调用rtos_wait(100)后,PC=PC+3=0x0163,SP=SP+2;把PC值压栈,可以参考LCALL addr16这条汇编指令

PC是16位,所以需要两个8位的空间,因此SP需要加2。

/*
使用keil4
可运行8个任务
任务从rtos_wait()处切换,在定时时间到后从定时中断中切换回来。

使用定时器2作为系统始终
*/
#include <regx52.H>
#include <INTRINS.H>

typedef unsigned char u8;
typedef unsigned int u16;
sbit LED1 = P2 ^ 0;
sbit LED2 = P2 ^ 1;

//两个宏定义是为了保护现场,不被定时中断打乱。
//主要用于需要一次性运行完毕的代码中。
#define OPEN_SYS_ISR() {EA=1;TR2=1;}
#define CLOSE_SYS_ISR() {EA=0;TR2=0;TF2=0;}

//#define TIMER_RELOAD()  {TL0=0xF0;TH0=0xD8;}	//使能T/C	  初始10ms
#define MAX_TASKS 2 //任务槽最大个数.

//任务堆栈.  PC指针为16位,需2个字节task_stack[][0]L  task_stack[][1]H。
u8 idata task_stack[MAX_TASKS][2];
//定时时间 (0-255)*10ms
u8 idata task_time[MAX_TASKS];

//当前活动任务号
u8 task_id = 0;

/*******************************************************************************
* 函 数 名         : Timer2 Init
* 函数功能		   : 定时器0初始化,用于系统时钟
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void timer2_init()
{
    RCAP2H = (0xFFFF - 10000) / 256;
    RCAP2L = (0xFFFF - 10000) % 256;	//12MHz晶振下定10ms,自动重装
    TH2 = RCAP2H;
    TL2 = RCAP2L;					//定时器2赋初值
    T2CON = 0;					//配置定时器2控制寄存器,这里其实不用配置,T2CON上电默认就是0,这里赋值只是为了演示这个寄存器的配置
    T2MOD = 0;					//配置定时器2工作模式寄存器,这里其实不用配置,T2MOD上电默认就是0,这里赋值只是为了演示这个寄存器的配置

    IE = 0xA0;	//1010 0000开总中断,开外定时器2中断,可按位操作:EA=1; ET2=1;
    TR2 = 1;		//启动定时器2
}


void rtos_wait(u8 time)
{
    static u8 i;

    task_time[task_id] = time;   	//保存当前任务时间

    //保存当前断点 并把SP=SP-2,任务切换到下一任务;
    task_stack[task_id][1] = *((u8 data*) SP);
    SP--;

    task_stack[task_id][0] = *((u8 data*) SP);
    SP--;

    //任务号设为最大,越过最大则回到0
    for (i = 0; i < MAX_TASKS; i++)
    {
        if (task_time[i] == 0)		//根据定时时间判断
        {
            task_id = i;
            break;
        }
    }
}
void task_sw()		 //任务时间是否到,任务时间到 实时切换回
{
    //从把定时时间减1 ,找看哪个任务到	,任务时间到 实时切换回
    static u8 i;
    for (i = 0; i < MAX_TASKS; i++)
    {
        if (task_time[i])			//time[0] =  10    if(10)
        {
            task_time[i]--;			 //10--
            if (task_time[i] == 0)	//多个定时时间同时到,任务越靠后,越优先执行。
            {
                task_id = i;			//当前任务号

                //保存的PC指针调出来
                SP++;
                (*((u8 data*)(SP))) = task_stack[i][0];
                SP++;
                (*((u8 data*)(SP))) = task_stack[i][1] ;
            }
        }
    }
}


void tm2_isr() interrupt 5 //using 1
{
	//EA=0;
    //ET2=0;
    //TF2=0;
    //!!!注意!!!定时器2必须由软件对溢出标志位清零,TF2=0;硬件不能清零,
    //这里与定时器0和定时器1不同!!!
    CLOSE_SYS_ISR();

    task_sw();			//任务时间是否到,任务时间到,则实时切换回
		
	OPEN_SYS_ISR();
}


//****************************************************************示例
void task_test()
{
    while (1)
    {
        LED1 = ~LED1;
        rtos_wait(100);		 //1sec 执行完后,记录下一步地址,返回
    }
}

void task_test2()
{
    while (1)
    {
        LED2 = ~LED2;
        rtos_wait(200);		//2sec 执行完后,记录下一步地址,返回
    }
}

void sys_init()
{
    _nop_();
    _nop_();
    _nop_();
    _nop_();
}

void main()
{
    sys_init();
    timer2_init();
    /* while(1)
    {
    	task_test();
    	task_test2();
    } */
    task_test();
    task_test2();
    while (1);
}