打印

51单片机操作系统

[复制链接]
2298|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();
}

图片1.png (8.65 KB )

图片1.png

QQ截图20130807141027.png (4.14 KB )

QQ截图20130807141027.png

相关帖子

沙发
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寄存器的值。

使用特权

评论回复
5
crazy2012| | 2013-8-9 13:41 | 只看该作者
lhxiuiiang 发表于 2013-8-9 13:35
这是在C中嵌入汇编,汇编主要是用来进行对堆栈的保护的,因为C语言中不能直接对寄存器进行操作,只能在汇编 ...

楼主,嵌入汇编,C语言混编有什么规则么,想学习下C语言和汇编的混编。

使用特权

评论回复
6
lhxiuiiang|  楼主 | 2013-8-9 13:49 | 只看该作者
crazy2012 发表于 2013-8-9 13:41
楼主,嵌入汇编,C语言混编有什么规则么,想学习下C语言和汇编的混编。

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

[size=12.0000pt]        [size=12.0000pt]RSEG  ?PR?CC
[size=12.0000pt]CC:
[size=12.0000pt]CLR P2.3

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

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

[size=12.0000pt]PUBLIC CC;           作用是声明函数为公共函数

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


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



使用特权

评论回复
7
lhxiuiiang|  楼主 | 2013-8-9 13:51 | 只看该作者
kan fu jian

KEILC语言与汇编混合编程.zip

426.59 KB

使用特权

评论回复
评分
参与人数 1威望 +3 收起 理由
crazy2012 + 3 赞一个!
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

22

帖子

1

粉丝