文档章节

一步步开发自己的OS操作系统

饶军
 饶军
发布于 2015/09/12 00:46
字数 2184
阅读 8923
收藏 143

以MSP430单片机为例控制4个灯以不同频率闪烁, 把原理搞清楚了一通则百通,可以举一返三;注:以下所讲的堆栈即栈,因为堆栈说习惯了 ,堆是堆栈是栈;,MSP430有16个寄存器,R0 (PC)、R1(SP)、R2(SR/CG1)、R3(CG2)、R4~R15(通用寄存器可以给用户存储数据使用)。

开发板上运行的效果视频连接 http://yun.baidu.com/share/link?shareid=2791015146&uk=2317903535

大家都知道切换任务时要保存现场,那现场到底要保存什么呢?今天我主要讲讲任务切换需要保存哪些寄存器,当然我阐述的是最简单的系统,从0开始构建系统,任务切换最重要的一步就是发生定时器中断时保存当前任务的堆栈指针,任务切换时保存现场PUSH / 恢复现场POP,寄存器的弹入弹出都和堆栈指针有关,堆栈指针指向哪就PUSH到哪,堆栈指针指向哪就从那POP,下面重要讲解。

比如 :

定义一个数组unsigned int buf[200]; 

定义一个指针unsigned int* ptr= &buf[200-1]

下面执行3条指令:

mov ptr,sp;修改了堆栈指针,让堆栈指针指向数组的最后一个字节

PUSH R4;把R4寄存器的值保存到数组的最后一个字节

PUSH R5;把R5寄存器的值保存到数组的倒数第二个字节

。。。。。。。

PUSH R15;把R15寄存器的值保存到数组的倒数第11个字节

PUSH PC;把PC寄存器的值保存到数组的倒数第12个字节,PC大家都很熟悉了就是PC里保存着下一个程序要执行的地址,对于任务来说就是下个任务要恢复的断点。

执行完以上汇编指令后buf的存储情况如下:

|-------------------------------------------------------------------------------|

|  R4  |  R5 | ………|  R15  |   PC  |                                                                    |

|-------------------------------------------------------------------------------|

buf[199]  buf[198]………………………...buf[185]

为什么要从数组的最后一个字节开始push呢,因为堆栈从高地址往低地址增长,所以要从数组的最后一个字节开始存储。

 

(一)好了有堆栈操作的基础,下面开始讲创建任务;

创建任务前的准备,首先定义了3个结构体,任务描述结构体,任务控制结构体,系统时间结构体;

//----------------------系统时间------------------------------------------

typedef struct

{

    //unsigned char year : 7;

    //unsigned char month : 4;

    unsigned int day : 5;

    unsigned int hour : 5;

    unsigned int minute : 6;

    unsigned int sec : 6;

    unsigned int ms : 10;

}TaskSysTime,*PTaskSysTime;

//----------------------任务描述结构体------------------------------------------

typedef struct

{

  OSELK_STK *pTaskStk;// 任务堆栈指针指向创建任务的堆栈数字最后一个字节,因为MSP430的栈从高地址往低地址方向增长

  volatile TaskSysTime   runTmr;//定时

  volatile TaskSysTime   runTmr_bak;//定时备份

  unsigned char taskId;

  unsigned char taskMode;//任务模式

  unsigned char taskState;//任务状态

  E_Task_func * func;//任务人口

}TaskFuncType, *PTaskFuncType;

//----------------------任务控制结构体------------------------------------------

typedef struct

{

     unsigned int task_cnt;//任务调度使用的,控制下一个任务调度谁

     unsigned int exec_id;//

     unsigned int CurTaskLens;//获取当前任务列表里的任务数

     OSELK_STK ** ppTaskStk;//这个指针用来修改pTaskStk

}TaskTabCtrlType,*PTaskTabCtrlType;

(二)OK3个结构体构建完了,开始创建任务,创建最重要的必须要传入至少2参数,第一:任务入口地址即函数指针;第二:任务栈底地址。

/**

 ***************************************************************************************************

 * create_task

 *

 * \param          E_Task_func* fun \ unsigned int taskId  \   unsigned int *pStk   \   unsigned char mode    \  TaskSysTime tmr

 * \return          void

 *

 ***************************************************************************************************

 */

int create_task( func fun ,
                            unsigned int taskId ,
                            unsigned int *pStk ,
                            unsigned char mode)
{
    PTaskFuncType  ptask_fun;
 
    if ((NULL == fun ) || (NULL == pStk))
        return (-1);

    ptask_fun = &Task_funcTab[taskId];//依次往数组里放

    if (taskId < MAX_TASK)
    {
        if (ptask_fun->taskMode == 0)
        {
            ptask_fun->func = fun;
            ptask_fun->taskMode = mode;
            ptask_fun->pTaskStk = LKOS_TaskStackInit(fun,pStk,taskId); //初始化堆栈 
            ptask_fun->taskId = taskId;
            ptask_fun->taskState = READY;
            Task_Ctrl.CurTaskLens = MAX(Task_Ctrl.CurTaskLens , taskId);
            return (ptask_fun->taskId);
        }
  }
  return (-1);
}

调用任务创建函数时,先定义一个任务栈数组如下:

unsigned int  TASK1_STACK_SIZE[200]={0,};//任务栈

void LKOS_Task1(void);

/*----------------------------------------------------------------------------------------

 * MAIN

 *----------------------------------------------------------------------------------------

 */

int main( void )

{

    TaskSysTime taskTmr;

    sys_BspInit();

    LKOS_TaskInit();

    taskTmr.ms = 15;

    create_task(LKOS_Task1,LKOS_Task1_ID,&TASK1_STACK_SIZE[200-1],TMR_MODE,taskTmr);

    LKOS_FirstTaskInit();

    while(1);

    return 0;

}

(三)任务栈初始化

OSELK_STK * LKOS_TaskStackInit(func fun , OSELK_STK*stk,unsigned int taskId)

{

    //memset(stk,0,strlen((char const*)stk));//栈清0,调用库比较慢

    *stk = (unsigned int)fun;//初始化时先把每个任务的入口地址存在栈底,切换任务的时候才能知道下一个任务在哪里

    *(stk-1) = 0x08;//R1 = 0x08; SR 寄存器GIE bit 置1允许中断

*(stk-2) = 0x00;//R2 = 0;

*(stk-3) = 0x00;//R3

*(stk-4) = 0x00;//R4

*(stk-5) = 0x00;//R5

*(stk-6) = 0x00;//R6

*(stk-7) = 0x00;//R7

*(stk-8) = 0x00;//R8

*(stk-9) = 0x00;//R9

*(stk-10) = 0x00;//R10

*(stk-11) = 0x00;//R11

*(stk-12) = 0x00;//R12

*(stk-13) = 0x00;//R13

*(stk-14) = 0x00;//R14

*(stk-15) = 0x00;//R15

    return (stk-13);//初始化完栈让SP 跳过13个字节(sr 和r4-r15) ,因为在调用下一个任务时要POP 13个字节

}

介绍一下我的任务栈怎么用的,在创建任务时定义2个全局数组,TASK1_TACK_SIZE[200]; TASK_TACK_SIZE[200];

创建任务时会初始化数组时如下:

TASK1_TACK_SIZE[200]

|-------------------------------------------------------------------|

|   task1   |   r4=0 |  r5=0 ......   r15=0                                |

|-------------------------------------------------------------------|

高地址   ---->                                                             低地址(栈从高地址往低地址增长)

初始化完后TASK1_TACK_SIZE[199] =(unsigned int)task1//任务入口地址

任务结构体的OSELK_STK *pTaskStk = &TASK1_TACK_SIZE[199-13];此时任务的栈指针指向TASK1_TACK_SIZE[199-13].

 

TASK_TACK_SIZE[200]

|-------------------------------------------------------------------|

|   task2   |   r4=0 |  r5=0 ......   r15=0                                |

|-------------------------------------------------------------------|

高地址   ---->                                                             低地址(栈从高地址往低地址增长)

初始化完后TASK2_TACK_SIZE[199] =(unsigned int)task2//任务入口地址

任务结构体的OSELK_STK *pTaskStk = &TASK_TACK_SIZE[199-13];此时任务的栈指针指向TASK2_TACK_SIZE[199-13].

 

1)任务创建好之后从第一个任务开始运行,第一个任务就是从任务栈里面取出task1并调用,第一个任务就开始跑起来了。

(2)当发生定时器中断时

第一:首先自动把PC和SR压进当前的任务栈里即任务1的栈数组TASK1_TACK_SIZE[200]

|-------------------------------------------------------------------|

| task1   | ...|....|pc|sr | .......  |   task stack......                               |

|---------------| ---   |---------------------------------------------|

高地址      |        |                                   -------->      低地址

                      |        |进入中断服务程序时SP此时栈指针指向这里;

                      |发中断时任务栈可能指向这里,为什么指向这里,因为任务已经跑起来了用了一部分栈。

第二:进入中断服务程序时,要手动保存所有工作寄存器R4~R15,就是用汇编PUSH指令把R4~R15的值到存到数组里,如下:

(除了中断会自动保存的PC和SR不需要保存其他都要保存,当然有人会问局部变量哪去了,需要保存吗,答应是不需要保存,因为局部变量就在栈里,在压入pc之前的就是局部变量或调用的子程序还没返回时被压栈的信息)

TASK1_TACK_SIZE[200]数组此时的存储信息如下

|-----------------------------------------------------------------------|

| task1    | ...|....|pc|sr |r4|r5|r6|r7|r8|r9| ...r14|r15|....  |   task stack...|

|---------------- -------------------------------- |---------------------|

高地址 -------->                                         |                       低地址

                                                                        |

                                                                        |

                                                                        |SP (此时栈指针指向这里)

此时需要保存当前的SP指针,当前SP指向r15寄存器的位置,把数组中r15的地址保存到任务块里的OSELK_STK *pTaskStk,初始化时OSELK_STK *pTaskStk = TASK_TACK_SIZE[199-13];此时要让OSELK_STK *pTaskStk = r15的位置。下次恢复时只要把OSELK_STK *pTaskStk恢复到SP,即SP又重新指向了数组中r15的位置,这样就可以继续往下执行了。

任务断点恢复时数组变化过程如下,恢复时执行汇编指令POP:

TASK1_TACK_SIZE[200]数组,执行POP指令栈往回走

 |<---------------sp---------------------|

|----------------|-------------------------------------------|--------------------|

| task1    |...|....| pc | sr | r4 | r5 | r6 |r7 | r8 | r9 | ...r14|r15|....      task stack...|

|----------------|----------------------------------------------------------------|

高地址            |                   -----à                                               低地址

                        |

                        |

                        |SP (此时栈指针指向这里)

当弹出最后一个寄存器PC时,汇编执行POP PC时立即会执行被中断的断点程序继续往下执行。

以此类推,每个任务切换都是这个过程,这样分时复用就开始跑起来了。

下面是中断代码:

#include <msp430f5247.h>

;********************************************************************************************************

;********************************************************************************************************

extern LKOS_TaskProcISR

extern pTaskStk

;********************************************************************************************************

;********************************************************************************************************

        RSEG    CODE            ; Program code

;********************************************************************************************************

;Interrupt ISR

;********************************************************************************************************

TIMER1_ISR

 

    dint;关闭中断

    push.w r4;保存工作寄存器

    push.w r5

    push.w r6

    push.w r7

    push.w r8

    push.w r9

    push.w r10

    push.w r11

    push.w r12

    push.w r13

    push.w r14

    push.w r15

    mov.w  sp,pTaskStk;保存当前的任务栈指针

    calla #LKOS_TaskProcISR;调用c语言 取一下任务栈指针

    mov.w pTaskStk,sp;恢复任务栈指针

    pop.w r15

    pop.w r14

    pop.w r13

    pop.w r12

    pop.w r11

    pop.w r10

    pop.w r9

    pop.w r8

    pop.w r7

    pop.w r6

    pop.w r5

    pop.w r4

    eint;开中断

    reti;这条指令中断返回指令,会自动执行 POP SR  POP PC,当POP PC时即任务被切换到下一个断点了

           

;********************************************************************************************************

;  Interrupt vectors

;********************************************************************************************************

            COMMON  INTVEC

 

            ORG     TIMER1_A0_VECTOR

WDT_VEC     DW      TIMER1_ISR                 

 

            END



© 著作权归作者所有

饶军
粉丝 16
博文 3
码字总数 4483
作品 0
长宁
私信 提问
加载中

评论(44)

_Change_
_Change_

引用来自“zheng_chao”的评论

although i don't know what you say,but feel very lihai yangzi.

除了牛逼还能说什么呢。
kolbe
kolbe

引用来自“饶军”的评论

引用来自“翟志军”的评论

Can you explain the theory of that as I am still not understanding it.

OK,I will explain my theory more detail.

引用来自“justintung”的评论

你们俩的english is 非常good

引用来自“子矜”的评论

Sorry I dont understand.

引用来自“谷鹏飞”的评论

coffee,tea or coca-cola?
我瞄了他一眼,哼哼 橙汗!
谷鹏飞
谷鹏飞

引用来自“饶军”的评论

引用来自“翟志军”的评论

Can you explain the theory of that as I am still not understanding it.

OK,I will explain my theory more detail.

引用来自“justintung”的评论

你们俩的english is 非常good

引用来自“子矜”的评论

Sorry I dont understand.
coffee,tea or coca-cola?
草社区榴最新地址1024
草社区榴最新地址1024
信息了
C
Chris-Lee
超级精简版的uC/OS
zheng_chao
zheng_chao
although i don't know what you say,but feel very lihai yangzi.
DRSoul
DRSoul

引用来自“饶军”的评论

引用来自“翟志军”的评论

Can you explain the theory of that as I am still not understanding it.

OK,I will explain my theory more detail.

引用来自“justintung”的评论

你们俩的english is 非常good

引用来自“子矜”的评论

Sorry I dont understand.
what are you say?I can't understand!
边缘如刀
边缘如刀

引用来自“饶军”的评论

引用来自“翟志军”的评论

Can you explain the theory of that as I am still not understanding it.

OK,I will explain my theory more detail.

引用来自“justintung”的评论

你们俩的english is 非常good

引用来自“子矜”的评论

Sorry I dont understand.
橙!!汁!!!
饶军
饶军 博主

引用来自“leo-H”的评论

尽量排版一下吧,这样更吸引读者。毕竟这个教程不错,比起CPU的分段/分页,实模式/保护模式一大堆设置,单片机更容易吧任务切换的原理说清楚。
谢谢,你的建议很好!
leo-H
leo-H
尽量排版一下吧,这样更吸引读者。毕竟这个教程不错,比起CPU的分段/分页,实模式/保护模式一大堆设置,单片机更容易吧任务切换的原理说清楚。
Mozilla考虑终结Firefox对Leopard的支持

编者:Firefox 正在一步步的走向不归路,市场份额首度跌至第三,其产品的快速版本策略一度让业界质疑,用户的反应也不佳。 据国外媒体报道,据火狐开发团队的讨论显示,Mozilla公司正在考虑终...

红薯
2011/12/02
1K
12
软件工程第一次作业(2019-04-07)

一、 回顾你过去将近3年的学习经历. (1)当初你报考的时候,是真正喜欢计算机这个专业吗?. (2)你现在后悔选择了这个专业吗?. (3)你认为你现在最喜欢的领域是什么(可以是计算机的也可...

冯俊鹏
04/07
0
0
12个优秀的云计算操作系统

如果你还仅是认为云只是个被大肆炒作的概念,那么你可能OUT了,可能已经漠视了IT发展的前沿动向。 当然每个相关人士都在谈论云计算厂商的云产品和服务,云最终是要落地的,也就是说终端的革新...

老古董
2011/04/24
25.3K
26
谈谈 COS 中国自主知识产权智能手机操作系统

2014年1月16日,CCTV13 新闻频道发布了一篇关于“COS中国智能手机操作系统面世”的报道,据悉COS是中国科学院软件研究所与上海联彤网络通讯技术有限公司联合发布的具有自主知识产权的操作系统...

快乐大米
2014/01/16
8K
114
Google Chrome OS 更新至 67.0.3396.26 版本

Chrome OS 开发者频道发布 67.0.3396.26 版本(平台版本:10575.22.0)。 此次更新包含大量 bug 修复、安全更新和性能提升,详情见更新日志。 Chrome OS 是一款 Google 开发的基于PC的操作系统...

h4cd
2018/05/02
2.8K
7

没有更多内容

加载失败,请刷新页面

加载更多

为什么要在网站中应用CDN加速?

1. 网页加载速度更快 在网站中使用CDN技术最直接的一个好处就是它可以加快网页的加载速度。首先,CDN加速的内容分发是基于服务器缓存的,由于CDN中缓存了不少数据,它能够给用户提供更快的页...

云漫网络Ruan
13分钟前
2
0
亚玛芬体育(Amer Sports)和信必优正式启动合作开发Movesense创新

亚玛芬体育和信必优正式启动合作开发Movesense创新,作为亚玛芬体育的完美技术搭档,信必优利用Movesense传感器技术为第三方开发移动应用和服务。 Movesense基于传感器技术和开放的API,测量...

symbiochina88
24分钟前
2
0
创龙TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA核心板规格书

SOM-TL437xF是一款广州创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA芯片设计的核心板,采用沉金无铅工艺的10层板设计,适用于高速数据采集和处理系统、汽车导航、工业自动化等领...

Tronlong创龙
25分钟前
2
0
好程序员Java学习路线分享MyBatis之线程优化

  好程序员Java学习路线分享MyBatis之线程优化,我们的项目存在大量用户同时访问的情况,那么就会出现大量线程并发访问数据库,这样会带来线程同步问题,本章我们将讨论MyBatis的线程同步问...

好程序员官方
31分钟前
6
0
IDEA 自定义方法注解模板

IDEA 自定义方法注解模板 1、使用效果 /*** 计算交易费用* @Author wangjiafang* @Date 2019/9/11* @param feeComputeVo* @return*/@PostMapping("/v1/fee_compute")public ApiResp......

小白的成长
31分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部