51单片机操作系统

[复制链接]
2710|6
 楼主| lhxiuiiang 发表于 2013-8-7 14:12 | 显示全部楼层 |阅读模式
   嵌入式实时操作系统,可以充分的利用单片机的资源,提高CPU使用效率。操作系统最主要就是实现任务的调度、管理。同时对于实时操作系统来说响应时间很重要。
   操作系统编写最主要就是用到了堆栈SP于RET指令。这两个东西怎么用呢?其实在我们每次调用函数的时候都会自动将函数的断点地址(执行函数调用时的PC)压入到SP中,而从函数中返回时其实是利用RET指令将断点弹回到PC(程序指针)中。所以利用堆栈和RET指令就可以实现简单的任务的切换。这么说肯定挺模糊的,接下来一步一步解释。
   首先,要知道任务是一个死循环。如下面所示,可以看出两个任务都是死循环,按照以往的情况,程序是跳不出来的,只能在while(1)中无限执行。那怎么才可以实现从task0到task1的切换呢?

   其实如果我们能够改变PC的值是不是就可以改变程序执行顺序了。任务的调度切换就是利用改变PC的值来改变程序执行顺序的。
   其次,就是要解决如何实现PC值的正确变换问题,如何让PC指向我们需要执行的地方。这就是通过堆栈来实现的。我们可以为每个任务建立一个堆栈用于保存任务PC的值,以及任务寄存器的值。这样每次进行任务切换时只要从相应的堆栈中取出PC和寄存器的值就可以实现任务的调度了。
   一个简单的操作系统如下所示,只能实现简单的任务调度,延时。必须注意,空闲任务(Idle)必须建立,否则会出错。
#include<reg52.h>
#define OSEnterCritical() EA=0
#define OSExitCritical()  EA=1
#define EnterInt() EA=0;
#define uint unsigned short int
#define uchar unsigned char
#define MAX_Tasks 3
#define False 0
#define Ture  1
#define MaxPrio 2
#define IdlePrio MaxPrio
#define OS_Task_Create_Error 1
#define OS_Delet_Task_Error 2
#define OS_Delet_Task_Not_Exit 3
#define OS_Resume_Idle_Error 4
#define OS_Resume_Task_Error 5
typedef struct
{
        uchar OSStackTop;          //SP
        uchar OSSuspend;
        uchar OSTCBDly;          //delay time
}OSTCB;       
uchar code OSMapTbl[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
OSTCB OSTCBTbl[MAX_Tasks];
volatile uchar OSRdyTbl;
volatile uchar OSIntNesting;          //用于中断锁死
volatile uchar OSSchNesting;          //任务切换上锁
volatile uchar OSRuning=False;
volatile uchar OSStartStack[MAX_Tasks][20];
volatile uchar OSPoint[MAX_Tasks][2];
volatile uchar OSPrioCur;
//volatile uchar OSTaskPend;
OSInit()
{
//        uchar i;
        EA=0;
        ET0=1;
        TMOD=0x01;
        TH0=0xB1;
        TL0=0xE0;
        OSRdyTbl=0;
        OSIntNesting=0;
        OSSchNesting=0;
}
//PCLPCHACC        ,B,DPL,DPH,PSW,R0-R7
uchar *OSStackInit(uint task,uchar *ptr,uchar OSPrio)
{
        uchar* stk;
        stk=ptr;
        OSPoint[OSPrio][0]=task;
        OSPoint[OSPrio][1]=task>>8;
        *(stk++)= OSPoint[OSPrio][0];
        *(stk++)= OSPoint[OSPrio][1];
        *(stk++)= 0x00;                //ACC
        *(stk++)= 0x00;
        *(stk++)= 0x00;
        *(stk++)= 0x00;
        *(stk++)= 0x00;
        *(stk++)= 0x00;
        *(stk++)= 0x00;
        *(stk++)= 0x00;
        *(stk++)= 0x00;
        *(stk++)= 0x00;
        *(stk++)= 0x00;
        *(stk++)= 0x00;          
        *(stk)  = 0x00;          
        return stk;
}
uchar OSTaskCreate(uint task,uchar *ptr,uchar OSPrio)
{
        uchar* psp;
        OSEnterCritical();
        if(OSPrio<=MaxPrio)        //创建的任务优先级有效
        {
                psp=OSStackInit(task,ptr,OSPrio);                //初始化堆栈
                OSRdyTbl|=OSMapTbl[OSPrio];       
                OSTCBTbl[OSPrio].OSStackTop=psp;
                OSTCBTbl[OSPrio].OSSuspend=0;
                OSTCBTbl[OSPrio].OSTCBDly=0;
        }
        else
        {
                OSExitCritical();
                return OS_Task_Create_Error;
        }
        OSExitCritical();                 
}
/*=====================================================
任务调度函数
入口参数:无
函数说明:进入函数后,先进行堆栈保护,然后查找最高优先
级任务运行
======================================================*/
void OSSchedule()
{
        uchar i;
        OSEnterCritical();
        #pragma asm
                PUSH ACC
                PUSH B
                PUSH DPH
                PUSH DPL
                PUSH PSW
                PUSH 0
                PUSH 7
       
                PUSH 1
                PUSH 2
                PUSH 3
                PUSH 4
                PUSH 5
                PUSH 6
        #pragma endasm
        OSTCBTbl[OSPrioCur].OSStackTop=SP;
        if(OSRdyTbl)                //如果就续表中有任务
        {
                for(i=0; i<MAX_Tasks;i++)
                {
                        if((OSRdyTbl & OSMapTbl)&&(!OSTCBTbl.OSSuspend)) //任务优先级最高且未被挂起
                        {
                                OSPrioCur=i;
                                break;
                        }
                }
               
               
        }
        SP=OSTCBTbl[OSPrioCur].OSStackTop;
        #pragma        asm         
                 POP 6;
                 POP 5;
                 POP 4;
                 POP 3;
                 POP 2;
                 POP 1;
                 
                 POP 7;
                 POP 0;         
                 POP PSW;
                 POP DPL;
                 POP DPH;
                 POP B;
                 POP ACC;
        #pragma endasm
        OSExitCritical();
}
void OSStart()
{
        TR0=1;
        EA=1;
        while(1);
       
}
/*=========================================================
延时若干个系统时钟
入口参数:延时系统时间个数
===========================================================*/
void OSDelay(uchar time)
{
        if(time==0)//延时为0,返回
        return;
        OSEnterCritical();
        OSTCBTbl[OSPrioCur].OSTCBDly=time;
        OSTCBTbl[OSPrioCur].OSSuspend=1;
        OSExitCritical();
        OSSchedule();
}
/*=========================================================
任务删除函数
入口参数:为被删除任务优先级
函数说明:将任务从就绪表中删除
===========================================================*/
uchar OSTaskDelet(uchar priority)
{
        OSEnterCritical();
        if(priority>=IdlePrio)
        {
                OSExitCritical();
                return OS_Delet_Task_Error;
        }
        if(!(OSRdyTbl & OSMapTbl[priority]))
        {
                OSExitCritical();
                return OS_Delet_Task_Not_Exit;
        }
        OSRdyTbl &= ~(OSMapTbl[priority]);
        OSExitCritical();
        if(priority<OSPrioCur)
        {
                OSSchedule();
        }
}
/*=========================================================
任务恢复函数
入口参数:恢的任务优先级
函数说明:恢复被OSTaskDelet()删除的任务
===========================================================*/
uchar OSTaskResume(uchar priority)
{
        OSEnterCritical();
        if(priority==IdlePrio)//恢复的任务不能为空闲任务,为空闲任务返回错误标志
        {
                OSExitCritical();
                return OS_Resume_Idle_Error;               
        }
        if((!(OSRdyTbl & OSMapTbl[priority])) && (priority>=0))
        {
                OSRdyTbl |= (OSMapTbl[priority]);       
        }
        else                                                //返回的任务不存在,返回错误标志
        {
                OSExitCritical();
                return OS_Resume_Task_Error;
        }
        OSExitCritical();
        if(priority<OSPrioCur)
        {
                OSSchedule();
        }
}
/*========================================================================
定时器0用于产生系统时钟,这里每过20ms中断一次。
==========================================================================*/
void timer0() interrupt 1
{
        uchar i;
        EA=0;
        TR0=0;
        TH0=0xB1;
        TL0=0xE0;
        if(OSRuning==Ture)        //如果不是第一次调度则保护任务堆栈 ,有部分堆栈进入中断时自动完成
        {                                        //定时器自动保护的堆栈可通过反汇编查看,以此确定堆栈的安排顺序
               
                __asm PUSH 1
                __asm PUSH 2
                __asm PUSH 3
                __asm PUSH 4
                __asm PUSH 5
                __asm PUSH 6
                OSTCBTbl[OSPrioCur].OSStackTop=SP;
        }
        else        //第一次进入则将OSRuning置位
        {
                OSRuning=Ture;       
        }
        if(OSRuning==Ture)//如在运行,
        {
                for(i=0;i<MAX_Tasks;i++)
                {
                        if((OSRdyTbl & OSMapTbl) && OSTCBTbl.OSTCBDly)//如果任务需要延时,将任务延时参数减一
                        {
                                OSTCBTbl.OSTCBDly--;       
                                if(OSTCBTbl.OSTCBDly==0)
                                {
                                        OSTCBTbl.OSSuspend=0;
                                }
                        }
                }
                for(i=0;i<MAX_Tasks;i++)
                {
                        if((OSRdyTbl & OSMapTbl) && (!OSTCBTbl.OSSuspend))//查找优先级最高的且未挂起的任务
                        {       
                                OSPrioCur=i;
                                break;
                        }
                }
                       
                SP=OSTCBTbl[OSPrioCur].OSStackTop;
               
                #pragma        asm
                         
                         POP 6;
                         POP 5;
                         POP 4;
                         POP 3;
                         POP 2;
                         POP 1;
                  
                        /* POP 7;
                         POP 0;         
               
                         POP PSW;
                         POP DPH;
                         POP DPL;
                         POP ACC; */
                #pragma endasm
               
        }
        TR0=1;
        EA=1;               
}
void Idle()
{
        while(1);
}
void task0()
{
        uchar temp=0x01,i;
        while(1)
        {
                for(i=0;i<8;i++)
                {
                        P1=~temp;
                        temp<<=1;
                        OSDelay(10);
                }
                temp=0x01;               
        }
}
void task1()
{
        uchar i=0,j=0;
        while(1)
        {
                for(i=0;i<16;i++)
                {
                        P3=j;
                        OSDelay(20);
                        j++;
                }
                j=0;
        }
}
int main()
{
        OSInit();
        OSTaskCreate(&Idle,&OSStartStack[2],IdlePrio);
        OSTaskCreate(&task1,&OSStartStack[1],1);
        OSTaskCreate(&task0,&OSStartStack[0],0);
        OSStart();
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
aw50420181750 发表于 2013-8-8 17:55 | 显示全部楼层
学习要是没人解释估计也要自己扒半天理解。。。
lovejin11 发表于 2013-8-8 20:23 | 显示全部楼层
看不懂 又是C 又是汇编的
 楼主| lhxiuiiang 发表于 2013-8-9 13:35 | 显示全部楼层
这是在C中嵌入汇编,汇编主要是用来进行对堆栈的保护的,因为C语言中不能直接对寄存器进行操作,只能在汇编中进行。主要是保存寄存器组、PC,DPTR、PSW、ACC、B寄存器的值。
crazy2012 发表于 2013-8-9 13:41 | 显示全部楼层
lhxiuiiang 发表于 2013-8-9 13:35
这是在C中嵌入汇编,汇编主要是用来进行对堆栈的保护的,因为C语言中不能直接对寄存器进行操作,只能在汇编 ...

楼主,嵌入汇编,C语言混编有什么规则么,想学习下C语言和汇编的混编。
 楼主| lhxiuiiang 发表于 2013-8-9 13:49 | 显示全部楼层
crazy2012 发表于 2013-8-9 13:41
楼主,嵌入汇编,C语言混编有什么规则么,想学习下C语言和汇编的混编。

KEIL  C语言与汇编混合编程
一、 C语言中嵌套汇编
这部分较为简单,只需要你将你的汇编代码置于#pragma asm……….#pragma endasm之间,下面为一段实例代码:
#include"reg51.h"
void main()
{
        #pragma asm
                CLR P2.3//点亮STC89C51P2.3控制的LED
        #pragma endasm
}
要通过编译还需要对含有汇编代码的文件file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-17706.png做一些操作,如图1
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-28756.png
将图中标记的俩项勾上(注:勾为黑色时为有效状态)。勾上后选择“OK”即可。这之后project窗口中含有汇编代码的文件的左边会出现三个红色的点。file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-16750.png
其仿真结果如下图:
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-29961.png
二、 C语言中调用汇编
1、无参调用
        方法一:同样以点亮LED灯为例说明,下面是C程序代码,单独建立一个C文件
include"reg51.h"
extern void CC();
void main()
{
        while(1)
        {
                CC();
        }
}
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-18800.png
代码中extern void CC()为将被调用的汇编函数。extern是对函数的声明。
接下来是汇编代码同样是单独建立一个ASM文件,
?PR?CC        SEGMENT CODE
        PUBLIC CC

        RSEG  ?PR?CC
CC:
CLR P2.3

        RET         
        END
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-4806.png
注:C文件和ASM文件的文件名不要相同。

?PR?CC SEGMENT CODE;作用是在程序存储区中定义段,DELAY100为段名,?PR?表示段位于程序存储区内

PUBLIC CC;           作用是声明函数为公共函数

RSEG ?PR?CC;          表示函数可被连接器放置在任何地方,RSEG是段名的属性


完成之后进行编译即可。其仿真结果如下图:
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-26260.png



 楼主| lhxiuiiang 发表于 2013-8-9 13:51 | 显示全部楼层
kan fu jian

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×

评分

参与人数 1威望 +3 收起 理由
crazy2012 + 3 赞一个!

查看全部评分

您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

22

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部