• 使用KEIL C51实现的简单合作式多任务操作系统内核(单片机实现版本)


    基于网上网友的代码,自己在单片机上实现, 特此记录分享之。

    基于https://blog.csdn.net/yyx112358/article/details/78877523

    //使用KEIL C51实现的简单合作式多任务操作系统内核
    
    #include <regx52.H>
    #include <INTRINS.H>
    
    typedef unsigned char u8;
    typedef unsigned int u16;
    
    sbit LED1 = P2 ^ 0;
    sbit LED2 = P2 ^ 1;
    sbit LED3_idle = P2 ^ 3;
    
    //两个宏定义是为了保护现场,不被定时中断打乱。
    //主要用于需要一次性运行完毕的代码中。
    #define OPEN_SYS_ISR() {EA=1;TR2=1;}
    #define CLOSE_SYS_ISR() {EA=0;TR2=0;TF2=0;}
    
    #define OS_TASK_STACK_SIZE (2+13+2*3)//存放断点2B,中断函数可能压栈13B,子程序每嵌套一层2B
    
    #define OS_TASK_NUM 2
    
    typedef struct OS_TASK_ST
    {
        u8  delay;		//当前延时剩余时间
        u8  stack[OS_TASK_STACK_SIZE]; //私有堆栈
        u8  sp;			//私有堆栈指针
    } OS_TASK; 			//任务工作块。
    
    data OS_TASK os_task[OS_TASK_NUM];	//必须定义为data(因堆栈只能在data区)
    data u8 os_idle_stack[15];
    
    
    void os_switch(void);
    void os_idle(void);
    //void os_update_time(void);
    void os_load(u8 id, void(*func));
    void os_delay(u8 id, u8 delay);
    void LED_Driver();
    void os_task_0(void);
    void sys_init(void);
    void delay(u16 i);
    
    /*******************************************************************************
    * 函 数 名         : 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 LED_Driver()
    {
        LED1 = ~LED1;
    }
    
    void os_idle(void)
    {
        while (1)
        {
            LED3_idle = ~LED3_idle;
            os_switch();
        }
    }
    
    /*
     *任务调度,转向当前延时时间到且优先级最高(id较小)任务
     而在一般的应用中,我们往往需要一个软件延时。例如:按键去抖、周期性采样等等。
     所以,这就要求有一个软件定时器功能。因此,修改调度器如下:
    首先定义任务控制器数据结构,加入一个延时记录:
     */
    void os_switch(void)//任务切换
    {
        u8 i = OS_TASK_NUM;
        do
        {
            i--;
            if (os_task[i].delay == 0)//如果有任务延时时间到,则跳转至相应任务
                SP = os_task[i].sp;
        }
        while (i); //否则不改变SP,继续执行os_idle()
    }
    
    /*
     *    更新任务延时表
     *    注:应定时更新,最好放入定时器中断
    
    void os_update_time(void)
    {
        u8 i = OS_TASK_NUM;
        do
        {
            i--;
            if(os_task[i].delay)
                os_task[i].delay--;
        }
        while(i);
    }
     */
    
    //修改任务工作块并跳转入os_idle()进行任务切
    void os_delay(u8 id, u8 delay)
    {
        TR2 = 0;//关中断
        {
            os_task[id].delay = delay;      //延时设定
            os_task[id].sp = SP;             //保存SP
            SP = os_idle_stack + 1;            //SP指向os_idle_stack[1]
            //os_delay()结束后跳转os_idle()
        }
        TR2 = 1;
    }
    
    void os_load(u8 id, void(*func))
    {
        os_task[id].sp = os_task[id].stack + 1;	//私有堆栈指针指向私有堆栈
        os_task[id].stack[0] = (u16)func & 0xFF;//私有堆栈栈底存放任务函数入口
        os_task[id].stack[1] = (u16)func >> 8;
    }
    
    
    void os_task_0(void)
    {
    #define OS_CUR_ID  (0)
    
        //static u8 i=0;
    
        //KEIL一般分配临时变量在RAM不在堆栈
        //因此为了防止任务之间改写凡是作用域跨越os_delay()应作为static
    
        while (1)
        {
            LED1 = ~ LED1;
            os_delay(OS_CUR_ID, 1);
        }
    
    #undef OS_CUR_ID
    }
    
    void os_task_1(void)
    {
    #define OS_CUR_ID  (1)
    
        //static u8 i=0;
    
        //KEIL一般分配临时变量在RAM不在堆栈
        //因此为了防止任务之间改写凡是作用域跨越os_delay()应作为static
    
        while (1)
        {
            //LED_Driver();
            LED2 = ~ LED2;
            os_delay(OS_CUR_ID, 1);
        }
    
    #undef OS_CUR_ID
    }
    
    /*******************************************************************************
    * 函 数 名         : delay
    * 函数功能		   : 延时函数,i=1时,大约延时10us
    *******************************************************************************/
    void delay(u16 i)
    {
        while (i--);
    }
    
    void sys_init()
    {
        u8 i;
        for (i=0; i<4; i++)
        {
            LED_Driver();
            delay(10000);
        }
    }
    
    void main()
    {
        //…初始化外设
        sys_init();
    
        //…初始化os所用定时器
    
        Timer2_init();
    
        //…初始化其它任务控制器
        os_load(0, os_task_0);
        os_load(1, os_task_1);
    
    
        os_idle_stack[0] = (u16)os_idle & 0xFF;
        os_idle_stack[1] = (u16)os_idle >> 8;
        SP = os_task[0].sp;	//运行任务0
    
        return;	//跳转,永不返回。
    }
    
    
    
    //12MHz晶振下定10ms,自动重装
    void timer2() interrupt 5
    {
        u8 i = OS_TASK_NUM;
        //EA=0;
        //ET2=0;
        //TF2=0;
        //!!!注意!!!定时器2必须由软件对溢出标志位清零,TF2=0;硬件不能清零,
        //这里与定时器0和定时器1不同!!!
        CLOSE_SYS_ISR();
    
        do
        {
            i--;
            if (os_task[i].delay)
                os_task[i].delay--;
        }
        while (i);
    
        OPEN_SYS_ISR();
    }
    
  • 相关阅读:
    《全体育&#183;瑜伽》
    PowerDesigner使用教程
    Android基础之——startActivityForResult启动界面并返回数据,上传头像
    数据仓库与数据挖掘的一些基本概念
    php实现求二进制中1的个数(右移、&、int32位)(n = n & (n
    批量发短信的平台浏览总结
    php资源集
    js进阶正则表达式5几个小实例(原样匹配的字符在正则中原样输出)(取反^)
    js进阶正则表达式方括号(方括号作用)(js正则是在双正斜杠之中:/[a-z]/g)
    js进阶正则表达式修饰符(i、g、m)(var reg2=/html/gi)
  • 原文地址:https://www.cnblogs.com/CodeWorkerLiMing/p/12007466.html
Copyright © 2020-2023  润新知