前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用KEIL C51实现的简单合作式多任务操作系统内核(单片机实现版本)

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

作者头像
用户4645519
发布2020-09-07 11:00:28
9660
发布2020-09-07 11:00:28
举报
文章被收录于专栏:嵌入式学习

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

基于https://cloud.tencent.com/developer/article/1692860

代码语言:javascript
复制
//使用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();
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/08/12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档