原创 UCOS-II移植ARM的读书笔记(12.11--12.25)

2007-10-31 22:31 5962 0 4 分类: MCU/ 嵌入式

 

UCOS-II移植ARM的读书笔记(12.11)

 

真是很郁闷,昨天晚上边看移植代码边记下来的笔记不知道怎么回事在保存的时候竟然不见了。。。5555。。。一个晚上工作的结果啊,关键是我是第一次也是正式开始移植的学习之路啊。。。真是够倒霉的。。。。今天在工作之前先把昨天的笔记重新回顾一下,其实后来想想也许是件好事,可以让我今天在不借助其他的帮助的情况下自己看代码自己跟自己讲一遍,其实很多看起来是倒霉看起来是灰心的事情把我们的观点换一下那么就是一件好事。。这样的情况发生在我的身上已经挺多次了。。。。好啦,废话不说,开始补昨天的日记

UCOS-II的移植需要提供2,3个文件分别介绍如下:

一:OS_CPU.H

1 与编译器有关的数据类型

   只是按照不同的编译器编写对应的数据类型的typedef

   对应于ARM7的数据类型的编写如下

typedef unsigned char  BOOLEAN;                 /* 布尔变量                                 */
typedef unsigned char  INT8U;                   /* 无符号8位整型变量                        */
typedef signed   char  INT8S;                   /* 有符号8位整型变量                        */
typedef unsigned short INT16U;                  /* 无符号16位整型变量                       */
typedef signed   short INT16S;                  /* 有符号16位整型变量                       */
typedef unsigned int   INT32U;                  /* 无符号32位整型变量                       */
typedef signed   int   INT32S;                  /* 有符号32位整型变量                       */
typedef float          FP32;                    /* 单精度浮点数(32位长度)                 */
typedef double         FP64;                    /* 双精度浮点数(64位长度)                 */

 

   在上面定义的各种数据类型中按照ARM7的堆栈宽度选择INT32U

typedef INT32U         OS_STK;                  /* 堆栈是32位宽度                           */

 

   接下来一部分是为了兼容低版本UCOS的数据类型所编写的代码,在UCOS-II中暂不考虑

 

2 与处理器相关的代码

    先定义中断的实现方式,预先设定的中断方式有三种,在ARM7中设置为方式2

#define     OS_CRITICAL_METHOD     2            /* 选择开、关中断的方式 */

 

    接下来的一段是我暂时还没有完全搞懂的一部分,只知道是设定了12个软件中断的函数,当调用这些函数之前都会执行对应中断号的事情。。具体的看到后面应该能完全搞懂软件中断的实现方式,该段代码在后面的文件中会有具体的解释,这里暂时不看


    定义堆栈的生长方式,ARM7内核支持两种生长方式,但是ADS的C语言编译器只支持从上往下的生长方式,因此:

#define OS_STK_GROWTH    1                      /*  堆栈是从上往下长的,0-从下往上的生长方式         */

 

    最后几行分别定义了用户模式01和系统模式1f以及IRQ中断禁止的指令80三个立即数,方便调用。

    还有两个预定义往后看应该知道作用,暂不考虑,不是很重要。

 

 

二:OS_CPU_C.C

    个文件中要求用户编写10个简单的C函数,但是只有1个函数是必要的,其余的函数必须声明,但不一定要包含任何代码,大致看了一下作用好像是用来调试之类的。唯一要编写的是OSTaskStkInit()

    OSTaskStkInit()函数的功能是初始化任务的栈结构,任务的堆栈结构与CPU的体系结构、编译器有密切的关联。从ARM的结构可以写出如下的栈结构:程序计数器PC,程序链接器LR,R12-R1,R0用于传递第一个参数pdata,CPSR/SPSR,关中断计数器(用于计算关中断的次数,这样就实现了中断的嵌套),返回的地址指针是指向的最后一个存入的数据,而不是一个空地址。

    软件中断异常SWI服务程序C语言部分

    void SWI_Exception(int SWI_Num, int *Regs):参数SWI_Num对应前面文件中定义的功能号,其中0、1号的功能在后面的文件中定义,这里只定义了其他10个功能。

    2、3分别对应关中断和开中断

    关中断:MRS     R0, SPSR         //在软件中断的时候直接对程序状态保存寄存器SPSR操作也就是对CPSR的操作
                   ORR     R0, R0, #NoInt    //在汇编语言中对寄存器的对应位置位用ORR,清零用BIC
                   MSR     SPSR_c, R0        //SPSR_c表示的是只改变SPSR的控制段的8位代码,其他三段_f,_s,_x中标志位在_f段,其他为保留位

    开中断:MRS     R0, SPSR       //在开中断中基本与上面相同,只是ORR改成BIC清零
                   BIC     R0, R0, #NoInt
                   MSR     SPSR_c, R

    由于需要实现中断嵌套,所以只有当关中断的计数器减为0的时候才能够开中断,而且每次关中断的时候该计数器都应该加1。另外,插入汇编语言时用_asm指令。

 

    80、81、82、83分别对应系统模式、用户模式、ARM指令集、THUMB指令集

    系统模式:MRS     R0, SPSR
                        BIC     R0, R0, #0x1f     //先将控制模式的低5位清零
                        ORR     R0, R0, #SYS32Mode       //设置成系统模式的1F
                        MSR     SPSR_c, R0

    用户模式:MRS     R0, SPSR
                        BIC     R0, R0, #0x1f
                        ORR     R0, R0, #USR32Mode     //设置成用户模式的10
                        MSR     SPSR_c, R0

    ARM指令集与THUMB指令集的代码如下:

    ptcb = OSTCBPrioTbl[Regs[0]];
                if (ptcb != NULL)
                {
                    ptcb -> OSTCBStkPtr[1] &= ~(1 << 5);
                }

    ptcb = OSTCBPrioTbl[Regs[0]];
                if (ptcb != NULL)
                {
                    ptcb -> OSTCBStkPtr[1] |= (1 << 5);
                }

    昨天就是看到这里,出现了一个意识到是不能忽悠的地方就是UCOS里面的任务控制块OS_TCB的概念,因此今天的任务就是把这部分看看。。。

------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------

UCOS-II移植ARM的读书笔记(12.13) 

 

 


一点一点来,什么不会就学什么,都不会就都学。。。没有问题只要你肯努力。。。。。。

__OSStartHighRdy
        MSR     CPSR_c, #(NoInt | SYS32Mode)  ;MSR:在ARM中只有MSR能够直接设置状态寄存器CPSR或SPSR,可以是立即数或者源寄存器,NoInt是禁止中断,SYS32Mode是系统模式
                                                ;告诉uC/OS-II自身已经运行
        LDR     R4, =OSRunning  ;OSRunning正在运行多任务的标志,=OSRunning是把OSRunning的地址加载到R4,R4里存的是一个地址。。。
        MOV     R5, #1  
        STRB    R5, [R4]   ;将R5存储到R4存的地址的变量即OSRunning中,也就是将OSRunning置1

        BL      OSTaskSwHook                    ;调用钩子函数,OSTaskSwHook 是用于扩展的,在任务切换的时候执行用户自己定义的功能。

        LDR     R6, =OSTCBHighRdy    ;OSTCBHighRdy指向最高优先级任务的控制块TCB的指针!!!将放指针的地址放到R6中。
        LDR     R6, [R6]  ;将R6地址处的数据读出即OSTCBHighRdy的地址放到R6中
        B       OSIntCtxSw_1   ;跳转到OSIntCtxSw_1 

        AREA    SWIStacks, DATA, NOINIT,ALIGN=2
SvcStackSpace      SPACE   SVC_STACK_LEGTH * 4  ;管理模式堆栈空间

 

继续昨天没有看完的代码

OSIntCtxSw
                                                    ;下面为保存任务环境
        LDR     R2, [SP, #20]                       ;获取PC,放入R2
        LDR     R12, [SP, #16]                      ;获取R12,//R12存的什么东西啊???
        MRS     R0, CPSR

        MSR     CPSR_c, #(NoInt | SYS32Mode)   ;进入系统模式并禁止中断
        MOV     R1, LR    ;R1放LR值
        STMFD   SP!, {R1-R2}                        ;保存LR,PC,将R1,R2存入SP
        STMFD   SP!, {R4-R12}                       ;保存R4-R12,将R4-12存入SP

        MSR     CPSR_c, R0   ;再回到之前的模式
        LDMFD   SP!, {R4-R7}                        ;获取R0-R3
        ADD     SP, SP, #8                          ;出栈R12,PC
       
        MSR     CPSR_c, #(NoInt | SYS32Mode)
        STMFD   SP!, {R4-R7}                        ;保存R0-R3
       
        LDR     R1, =OsEnterSum                     ;获取OsEnterSum
        LDR     R2, [R1]
        STMFD   SP!, {R2, R3}                       ;保存CPSR,OsEnterSum

                                                    ;保存当前任务堆栈指针到当前任务的TCB
        LDR     R1, =OSTCBCur
        LDR     R1, [R1]
        STR     SP, [R1]

        BL      OSTaskSwHook                        ;调用钩子函数
                                                    ;OSPrioCur <= OSPrioHighRdy
        LDR     R4, =OSPrioCur
        LDR     R5, =OSPrioHighRdy
        LDRB    R6, [R5]
        STRB    R6, [R4]
                                                    ;OSTCBCur <= OSTCBHighRdy
        LDR     R6, =OSTCBHighRdy
        LDR     R6, [R6]
        LDR     R4, =OSTCBCur
        STR     R6, [R4]
OSIntCtxSw_1
                                                    ;获取新任务堆栈指针
        LDR     R4, [R6]      ;把OSTCBHighRdy指向最高优先级任务的控制块TCB的指针给R4
        ADD     SP, R4, #68                         ;17寄存器:CPSR,OsEnterSum,R0-R12,LR,SP
        LDR     LR, [SP, #-8]   ;取出LR放到LR
        MSR     CPSR_c, #(NoInt | SVC32Mode)        ;进入管理模式并且保持禁止中断
        MOV     SP, R4                              ;设置堆栈指针

        LDMFD   SP!, {R4, R5}                       ;CPSR,OsEnterSum。LDMFD数据出栈,放入R4,R5
                                                    ;恢复新任务的OsEnterSum
        LDR     R3, =OsEnterSum   ;OsEnterSum的地址存入R3
        STR     R4, [R3]   ;把R4的值赋给OsEnterSum
    
        MSR     SPSR_cxsf, R5                       ;恢复CPSR;在管理模式里是修改SPSR
        LDMFD   SP!, {R0-R12, LR, PC }^             ;运行新任务  ,恢复现场,异常处理返回

 


------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------

UCOS-II移植ARM的读书笔记(12.15) 


------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------

 

移植的工作难以分析下去,先来几个UCOS的使用范例看看吧
第一个范例:

void  main (void)
{
    PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK);      /* PC的清屏函数,PC_DispClrScr(INT8U  color),这里调用时是前景色为白色,背景色为黑色*/

    OSInit();                                              /* Initialize uC/OS-II ,初始化UCOS-II,在使用任何功能之前必须调用OSInit函数,建立两个任务:空闲任务-所有其他任务均未就绪时运行,统计任务-计算CPU的利用率*/

    PC_DOSSaveReturn();                                    /* Save environment to return to DOS 允许程序在正式开始多任务前,保存重要的寄存器的值,以保证UCOS能够正常地返回DOS       */
    PC_VectSet(uCOS, OSCtxSw);                             /* Install uC/OS-II's context switch vector ,用于设定中断向量表的内容。*/

    RandomSem   = OSSemCreate(1);                          /* Random number semaphore,建立一个信号量,并制定信号量的初值为1,OSSemCreate(1)返回一个指向信号量的指针,那么之后对该信号量的操作全部通过这个指针来实现                  */

    OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 0);   /*至少建立一个任务,TaskStart为指向该任务运行代码的指针,第二个参数是一个指向任务初始化数据的指针,第三个是任务的堆栈栈顶,当堆栈是从上到下,必须把堆栈高地址传给该函数,最后一个参数指定建立的任务的优先级,数值越小优先级越高,每个任务的优先级都是介于0-62之间独一无二的。*/
    OSStart();                                             /* Start multitasking,调用该函数将控制权交给内核,开始运行多任务    */
}

 

OSStart函数会让优先级最高的就绪任务开始运行,即TaskStart

void  TaskStart (void *pdata)
{
#if OS_CRITICAL_METHOD == 3                                /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr;
#endif
    char       s[100];
    INT16S     key;


    pdata = pdata;                                         /* Prevent compiler warning,这是这个参数是当任务建立时传递过来的一个指针                 */

    TaskStartDispInit();                                   /* Initialize the display 初始化屏幕显示                  */

    OS_ENTER_CRITICAL();               /*关中断*/
    PC_VectSet(0x08, OSTickISR);                           /* Install uC/OS-II's clock tick ISR        */
    PC_SetTickRate(OS_TICKS_PER_SEC);                      /* Reprogram tick rate                      */
    OS_EXIT_CRITICAL();               /*开中断*/

    OSStatInit();                                          /* Initialize uC/OS-II's statistics测试所使用的处理器的速度,得知处理器在运行所有应用任务时实际的CPU使用率         */

    TaskStartCreateTasks();                                /* Create all the application tasks建立更多任务,10个显示不同字符的任务,在每次建立一个新任务的时候,UCOS都会判断新建立的任务是否比建立它们的任务优先级更高,如果更高,这个新建立的任务将立刻开始运行。         */

    for (;;) {
        TaskStartDisp();                                  /* Update the display                       */


        if (PC_GetKey(&key) == TRUE) {                     /* See if key has been pressed              */
            if (key == 0x1B) {                             /* Yes, see if it's the ESCAPE key          */
                PC_DOSReturn();                            /* Return to DOS                            */
            }
        }

        OSCtxSwCtr = 0;                                    /* Clear context switch counter  每秒都将记录任务切换次数的清零      */
        OSTimeDlyHMSM(0, 0, 1, 0);                         /* Wait one second  将自身挂起1s,1s是通过四个参数传送的,小时,分钟,秒,毫秒                        */
    }
}

 

static  void  TaskStartCreateTasks (void)
{
    INT8U  i;


    for (i = 0; i < N_TASKS; i++) {                        /* Create N_TASKS identical tasks           */
        TaskData = '0' + i;                             /* Each task will display its own letter    */
        OSTaskCreate(Task, (void *)&TaskData, &TaskStk[TASK_STK_SIZE - 1], i + 1);
    }
}

 

上面的函数循环建立了十个Task任务,下面是Task任务的代码

void  Task (void *pdata)
{
    INT8U  x;
    INT8U  y;
    INT8U  err;


    for (;;) {
        OSSemPend(RandomSem, 0, &err);           /* 获取信号量*/
        x = random(80);                          /* 获得随机数x*/
        y = random(16);                          /*获得随机数y*/
        OSSemPost(RandomSem);                    /* 释放信号量*/
                                                 /* Display the task number on the screen              */
        PC_DispChar(x, y + 5, *(char *)pdata, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
        OSTimeDly(1);                            /* Delay 1 clock tick 通知ucos该任务本次运行已经结束,可以让其他低优先级的任务运行了,参数1代表该任务延时1个时钟节拍,在200Hz的情况下就是5ms                                */
    }
}


------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------

UCOS-II移植ARM的读书笔记(12.16) 


------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------

 


范例二:

void main (void)
{
    OS_STK *ptos;
    OS_STK *pbos;
    INT32U  size;


    PC_DispClrScr(DISP_FGND_WHITE);                        /* Clear the screen                         */

    OSInit();                                              /* Initialize uC/OS-II                      */

    PC_DOSSaveReturn();                                    /* Save environment to return to DOS        */
    PC_VectSet(uCOS, OSCtxSw);                             /* Install uC/OS-II's context switch vector */

    PC_ElapsedInit();                                      /* Initialized elapsed time measurement 初始化时间测量功能,用来精确地记录PC_ElapsedStart()和PC_ElapsedStop()的函数调用时刻,通过这两个时刻的差值可以很容易得到这两个时刻之间的执行代码的运行时间   */

    ptos        = &TaskStartStk[TASK_STK_SIZE - 1];        /* TaskStart() will use Floating-Point      */
    pbos        = &TaskStartStk[0];
    size        = TASK_STK_SIZE;
    OSTaskStkInit_FPE_x86(&ptos, &pbos, &size);           
    OSTaskCreateExt(TaskStart,
                   (void *)0,
                   ptos,
                   TASK_START_PRIO,
                   TASK_START_ID,    /*任务标志符,范例二中没有使用它*/
                   pbos,
                   size,
                   (void *)0,
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* 使用了OSTaskCreate的扩展函数,支持对堆栈的修改和运行时对堆栈容量的检查。最后一个参数的设置表明允许堆栈检查并且需要在任务建立时将堆栈清零*/

    OSStart();                                             /* Start multitasking 让最高优先级的任务运行即TaskStart*/
}

 

TaskStart的代码如下:

void  TaskStart (void *pdata)
{
#if OS_CRITICAL_METHOD == 3                                /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr;
#endif
    INT16S     key;


    pdata = pdata;                                         /* Prevent compiler warning                 */

    TaskStartDispInit();                                   /* Setup the display  初始化屏幕,在该函数内部设定屏幕初始化的图像*/

    OS_ENTER_CRITICAL();                                   /* 关中断    */
    PC_VectSet(0x08, OSTickISR);
    PC_SetTickRate(OS_TICKS_PER_SEC);                      /* Reprogram tick rate 设定时钟节拍大小         */
    OS_EXIT_CRITICAL();

    OSStatInit();                                          /* Initialize uC/OS-II's statistics  测试所使用的处理器的速度,得知处理器在运行所有应用任务时实际的CPU使用率        */

    AckMbox = OSMboxCreate((void *)0);                     /* Create 2 message mailboxes在范例二中涉及到了消息的概念,任务4将向任务5发送消息,并且任务5会回复一个应答消息,因此这里建立了两个通信工具即邮箱,它允许任务或中断向另一个任务发送指针变量*/
    TxMbox  = OSMboxCreate((void *)0);

    TaskStartCreateTasks();                                /* Create all other tasks  创建所有其他的任务 */

    for (;;) {
        TaskStartDisp();                                   /* Update the display  更新各项统计数据并显示*/

        if (PC_GetKey(&key)) {                             /* See if key has been pressed              */
            if (key == 0x1B) {                             /* Yes, see if it's the ESCAPE key          */
                PC_DOSReturn();                            /* Yes, return to DOS                       */
            }
        }

        OSCtxSwCtr = 0;                                    /* Clear context switch counter             */
        OSTimeDly(OS_TICKS_PER_SEC);                       /* Wait one second 挂起1s                         */
    }
}

创建其他六个任务,暂不执行,只是创建,等待CPU被放出

static  void  TaskStartCreateTasks (void)
{
    OSTaskCreateExt(TaskClk,
                   (void *)0,
                   &TaskClkStk[TASK_STK_SIZE - 1],
                   TASK_CLK_PRIO,
                   TASK_CLK_ID,
                   &TaskClkStk[0],
                   TASK_STK_SIZE,
                   (void *)0,
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

    OSTaskCreateExt(Task1,
                   (void *)0,
                   &Task1Stk[TASK_STK_SIZE - 1],
                   TASK_1_PRIO,
                   TASK_1_ID,
                   &Task1Stk[0],
                   TASK_STK_SIZE,
                   (void *)0,
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

    OSTaskCreateExt(Task2,
                   (void *)0,
                   &Task2Stk[TASK_STK_SIZE - 1],
                   TASK_2_PRIO,
                   TASK_2_ID,
                   &Task2Stk[0],
                   TASK_STK_SIZE,
                   (void *)0,
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

    OSTaskCreateExt(Task3,
                   (void *)0,
                   &Task3Stk[TASK_STK_SIZE - 1],
                   TASK_3_PRIO,
                   TASK_3_ID,
                   &Task3Stk[0],
                   TASK_STK_SIZE,
                   (void *)0,
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

    OSTaskCreateExt(Task4,
                   (void *)0,
                   &Task4Stk[TASK_STK_SIZE-1],
                   TASK_4_PRIO,
                   TASK_4_ID,
                   &Task4Stk[0],
                   TASK_STK_SIZE,
                   (void *)0,
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

    OSTaskCreateExt(Task5,
                   (void *)0,
                   &Task5Stk[TASK_STK_SIZE-1],
                   TASK_5_PRIO,
                   TASK_5_ID,
                   &Task5Stk[0],
                   TASK_STK_SIZE,
                   (void *)0,
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
}

 

分别看其他几个任务的代码如下:

void  Task1 (void *pdata)
{
    INT8U       err;
    OS_STK_DATA data;                       /* Storage for task stack data                             */
    INT16U      time;                       /* Execution time (in uS)                                  */
    INT8U       i;
    char        s[80];


    pdata = pdata;
    for (;;) {
        for (i = 0; i < 7; i++) {
            PC_ElapsedStart();
            err  = OSTaskStkChk(TASK_START_PRIO + i, &data);  /*该函数是用来检查任务堆栈使用情况*/
            time = PC_ElapsedStop();   /*测量上面的OSTaskStkChk函数的运行时间,方法是将这个函数放在PC_ElapsedStart()和PC_ElapsedStop()之间即可,它会返回以ms计量的时间间隔*/
            if (err == OS_NO_ERR) {
                sprintf(s, "%4ld        %4ld        %4ld        %6d",
                        data.OSFree + data.OSUsed,
                        data.OSFree,
                        data.OSUsed,
                        time);/*data是上面检查任务堆栈使用情况的函数的第二个参数,也是它的返回值之一*/
                PC_DispStr(19, 12 + i, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);/*把统计结果打印出来*/
            }
        }
        OSTimeDlyHMSM(0, 0, 0, 100);                       /* Delay for 100 mS 100ms挂起一次                       */
    }
}

 

void  Task2 (void *data)
{
    data = data;
    for (;;) {
        PC_DispChar(70, 15, '|',  DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDly(10);
        PC_DispChar(70, 15, '/',  DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDly(10);
        PC_DispChar(70, 15, '-',  DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDly(10);
        PC_DispChar(70, 15, '\\', DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDly(10);/*由四个字符轮流显示的任务*/
    }
}

 

void  Task3 (void *data)
{
    char    dummy[500];
    INT16U  i;


    data = data;
    for (i = 0; i < 499; i++) {        /* Use up the stack with 'junk'                                 */
        dummy = '?';
    }
    for (;;) {
        PC_DispChar(70, 16, '|',  DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDly(20);
        PC_DispChar(70, 16, '\\', DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDly(20);
        PC_DispChar(70, 16, '-',  DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDly(20);
        PC_DispChar(70, 16, '/',  DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDly(20);
    }
}

 

void  Task4 (void *data)
{
    char   txmsg;
    INT8U  err;


    data  = data;
    txmsg = 'A';
    for (;;) {
        OSMboxPost(TxMbox, (void *)&txmsg);      /* Send message to Task #5向邮箱TxMbox发送一个字符    */
        OSMboxPend(AckMbox, 0, &err);            /* Wait for acknowledgement from Task #5等待应答,第二个参数指定了等待超时的时限,单位为时钟节拍              */
        txmsg++;                                 /* Next message to send更新消息                               */
        if (txmsg == 'Z') {
            txmsg = 'A';                         /* Start new series of messages                       */
        }
    }
}

void  Task5 (void *data)
{
    char  *rxmsg;
    INT8U  err;


    data = data;
    for (;;) {
        rxmsg = (char *)OSMboxPend(TxMbox, 0, &err);                  /* Wait for message from Task #4无限期等待邮箱消息 */
        PC_DispChar(70, 18, *rxmsg, DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDlyHMSM(0, 0, 1, 0);                                    /* Wait 1 second  挂起1s               */
        OSMboxPost(AckMbox, (void *)1);                               /* Acknowledge reception of msg 给邮箱AckMbox发送消息 */

 

最后一个任务

void  TaskClk (void *data)
{
    char s[40];


    data = data;
    for (;;) {
        PC_GetDateTime(s);          /*得到PC当前的日期和时间*/
        PC_DispStr(60, 23, s, DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDly(OS_TICKS_PER_SEC);
    }
}
    }
}

--------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------


范例三:

在该范例中首先定义了一个用户任务数据结构,在这个结构中有该范例所需要的数据参数,和每个任务相关的,因此定了一个该数据结构类型的变量数组,分配给7个自建的任务

另外使用了消息队列的通讯方式,可以容纳多则消息。建立消息队列需要两个元素即OS_EVENT的数据结构和一串指针。

void  main (void)
{
    PC_DispClrScr(DISP_BGND_BLACK);                        /* Clear the screen                         */

    OSInit();                                              /* Initialize uC/OS-II                      */

    PC_DOSSaveReturn();                                    /* Save environment to return to DOS        */

    PC_VectSet(uCOS, OSCtxSw);                             /* Install uC/OS-II's context switch vector */

    PC_ElapsedInit();                                      /* Initialized elapsed time measurement     */

    strcpy(TaskUserData[TASK_START_ID].TaskName, "StartTask");
    OSTaskCreateExt(TaskStart,
                    (void *)0,
                    &TaskStartStk[TASK_STK_SIZE - 1],
                    TASK_START_PRIO,
                    TASK_START_ID,
                    &TaskStartStk[0],
                    TASK_STK_SIZE,
                    &TaskUserData[TASK_START_ID],/*在UCOS中,每个任务的TCB控制块都可以保存一个用户定义的数据结构的指针*/
                    0);
    OSStart();                                             /* Start multitasking                       */
}


void  TaskStart (void *pdata)
{
#if OS_CRITICAL_METHOD == 3                                /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr;
#endif
    INT16S     key;


    pdata = pdata;                                         /* Prevent compiler warning                 */

    TaskStartDispInit();                                   /* Setup the display                        */

    OS_ENTER_CRITICAL();                                   /* Install uC/OS-II's clock tick ISR        */
    PC_VectSet(0x08, OSTickISR);
    PC_SetTickRate(OS_TICKS_PER_SEC);                      /* Reprogram tick rate                      */
    OS_EXIT_CRITICAL();

    OSStatInit();                                          /* Initialize uC/OS-II's statistics         */

    MsgQueue = OSQCreate(&MsgQueueTbl[0], MSG_QUEUE_SIZE); /* Create a message queue  建立了一个消息队列*/

    TaskStartCreateTasks();

    for (;;) {
        TaskStartDisp();                                  /* Update the display                       */

        if (PC_GetKey(&key)) {                             /* See if key has been pressed              */
            if (key == 0x1B) {                             /* Yes, see if it's the ESCAPE key          */
                PC_DOSReturn();                            /* Yes, return to DOS                       */
            }
        }

        OSCtxSwCtr = 0;                                    /* Clear the context switch counter         */
        OSTimeDly(OS_TICKS_PER_SEC);                       /* Wait one second                          */
    }
}


void  TaskStartCreateTasks (void)
{
    strcpy(TaskUserData[TASK_CLK_ID].TaskName, "Clock Task");
    OSTaskCreateExt(TaskClk,
                    (void *)0,
                    &TaskClkStk[TASK_STK_SIZE - 1],
                    TASK_CLK_PRIO,
                    TASK_CLK_ID,
                    &TaskClkStk[0],
                    TASK_STK_SIZE,
                    &TaskUserData[TASK_CLK_ID],
                    0);

    strcpy(TaskUserData[TASK_1_ID].TaskName, "MsgQ Rx Task");
    OSTaskCreateExt(Task1,
                    (void *)0,
                    &Task1Stk[TASK_STK_SIZE - 1],
                    TASK_1_PRIO,
                    TASK_1_ID,
                    &Task1Stk[0],
                    TASK_STK_SIZE,
                    &TaskUserData[TASK_1_ID],
                    0);

    strcpy(TaskUserData[TASK_2_ID].TaskName, "MsgQ Tx Task #2");
    OSTaskCreateExt(Task2,
                    (void *)0,
                    &Task2Stk[TASK_STK_SIZE - 1],
                    TASK_2_PRIO,
                    TASK_2_ID,
                    &Task2Stk[0],
                    TASK_STK_SIZE,
                    &TaskUserData[TASK_2_ID],
                    0);

    strcpy(TaskUserData[TASK_3_ID].TaskName, "MsgQ Tx Task #3");
    OSTaskCreateExt(Task3,
                    (void *)0,
                    &Task3Stk[TASK_STK_SIZE - 1],
                    TASK_3_PRIO,
                    TASK_3_ID,
                    &Task3Stk[0],
                    TASK_STK_SIZE,
                    &TaskUserData[TASK_3_ID],
                    0);

    strcpy(TaskUserData[TASK_4_ID].TaskName, "MsgQ Tx Task #4");
    OSTaskCreateExt(Task4,
                    (void *)0,
                    &Task4Stk[TASK_STK_SIZE - 1],
                    TASK_4_PRIO,
                    TASK_4_ID,
                    &Task4Stk[0],
                    TASK_STK_SIZE,
                    &TaskUserData[TASK_4_ID],
                    0);

    strcpy(TaskUserData[TASK_5_ID].TaskName, "TimeDlyTask");
    OSTaskCreateExt(Task5,
                    (void *)0,
                    &Task5Stk[TASK_STK_SIZE - 1],
                    TASK_5_PRIO,
                    TASK_5_ID,
                    &Task5Stk[0],
                    TASK_STK_SIZE,
                    &TaskUserData[TASK_5_ID],
                    0);
}


void  Task1 (void *pdata)
{
    char  *msg;
    INT8U  err;


    pdata = pdata;
    for (;;) {
        msg = (char *)OSQPend(MsgQueue, 0, &err);  /*无限期等待消息队列的消息*/
        PC_DispStr(70, 13, msg, DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDlyHMSM(0, 0, 0, 100);
    }
}


void  Task2 (void *pdata)
{
    char  msg[20];


    pdata = pdata;
    strcpy(&msg[0], "Task 2");
    for (;;) {
        OSQPost(MsgQueue, (void *)&msg[0]);  /*向消息队列发送一个字符串的指针*/
        OSTimeDlyHMSM(0, 0, 0, 500);
    }
}


void  Task3 (void *pdata)
{
    char  msg[20];


    pdata = pdata;
    strcpy(&msg[0], "Task 3");
    for (;;) {
        OSQPost(MsgQueue, (void *)&msg[0]);  
        OSTimeDlyHMSM(0, 0, 0, 500);
    }
}


void  Task4 (void *pdata)
{
    char  msg[20];


    pdata = pdata;
    strcpy(&msg[0], "Task 4");
    for (;;) {
        OSQPost(MsgQueue, (void *)&msg[0]);
        OSTimeDlyHMSM(0, 0, 0, 500);
    }
}

Task3和4都是发送字符串,间隔半秒


void  Task5 (void *pdata)
{
    pdata = pdata;
    for (;;) {
        OSTimeDlyHMSM(0, 0, 0, 100);
    }
}

void  TaskClk (void *pdata)
{
    char  s[40];


    pdata = pdata;
    for (;;) {
        PC_GetDateTime(s);
        PC_DispStr(60, 23, s, DISP_FGND_YELLOW + DISP_BGND_BLUE);
        OSTimeDlyHMSM(0, 0, 0, 500);
    }
}


关于定义的接口函数的修改

OSTaskSwHook函数是在一个低优先级的任务切换到一个高优先级的任务时被调用

void  OSTaskSwHook (void)
{
    INT16U           time;
    TASK_USER_DATA  *puser;


    time  = PC_ElapsedStop();                    /* This task is done可以得到当前被切换的任务的运行时间 */
    PC_ElapsedStart();                           /* Start for next task开始记录当前任务的运行时间 */
    puser = OSTCBCur->OSTCBExtPtr;               /* Point to used data这个函数被调用的时候高优先级的任务还没有执行,因此OSTCBCur指向当前任务的任务控制块,而OSTCBHighRdy指向即将被调用运行的任务的任务控制块,OSTCBCur->OSTCBExtPtr是当前任务在建立时指定的数据结构的指针!!!*/
    if (puser != (TASK_USER_DATA *)0) {
        puser->TaskCtr++;                        /* Increment task counter 记录切换次数*/
        puser->TaskExecTime     = time;          /* Update the task's execution time记录本次执行时间                   */
        puser->TaskTotExecTime += time;          /* Update the task's total execution time 执行时间总和            */
    }
}


当允许使用统计任务时即OS_TASK_STAT_EN配置常数设为1,统计任务将首先调用定义的OSTaskStatHook接口函数

void  OSTaskStatHook (void)
{
    char    s[80];
    INT8U   i;
    INT32U  total;
    INT8U   pct;


    total = 0L;                                          /* Totalize TOT. EXEC. TIME for each task */
    for (i = 0; i < 7; i++) {
        total += TaskUserData.TaskTotExecTime;
        DispTaskStat(i);                                 /* Display task data  计算所有任务的执行时间                    */
    }
    if (total > 0) {
        for (i = 0; i < 7; i++) {                        /* Derive percentage of each task         */
            pct = 100 * TaskUserData.TaskTotExecTime / total;
            sprintf(s, "%3d %%", pct);
            PC_DispStr(62, i + 11, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
        }
    }
    if (total > 1000000000L) {                           /* Reset total time counters at 1 billion */
        for (i = 0; i < 7; i++) {
            TaskUserData.TaskTotExecTime = 0L;
        }
    }


------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------

UCOS-II移植ARM的读书笔记(12.17) 


------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------

 

实时系统概念
1 前后台系统

   不复杂的小系统通常选择前后台系统,应用程序是一个无限循环。在循环中调用相应的函数完成相应的操作,这部分可以看成后台行为。中断服务程序处理异步事件,可以看成前台行为。

 

2 代码的临界段

    需要在前后关开中断的代码,不能被打断的代码

 

3 资源

   输入输出设备,各种变量,结构,数组

 

4 共享资源

   可以被多个任务使用的资源

 

5多任务

   通过CPU在许多任务之间转换和调度

 

6任务

  每个任务都是一个无限循环,都可能处于五种状态之一:休眠,就绪,运行,挂起,被中断。UCOS中提供各种函数使任务能从一个状态变为另一个状态

  每个任务都有自己的CPU寄存器和栈空间以及TCB任务控制块

 

7任务切换

  任务切换过程是将正在运行任务的CPU寄存器全部内容保存到任务自己的栈区中,再将下一个要运行的任务的当前状况从该任务的栈中重新装入CPU的寄存器,并开始下一个任务的运行

 

8内核

  负责管理各个任务,为每个任务分配CPU时间,并负责任务间的通信

 

9调度

  决定该轮到哪个任务运行。主要是通过优先级来决定。总是让处于就绪态的优先级最高的任务先运行。

 

10不可剥夺型内核

  每个任务主动放弃CPU的使用权,放弃的方法可以使用多种函数定时或者定事件的放弃。异步事件还是由中断服务来处理。中断处理结束之后还是回到被中断的那个任务直到该任务主动放弃CPU的使用权。在任务级,不可剥夺型内核允许使用不可重入函数函数不必担心被重复调用,因为每个时刻都只有一个任务在运行。当然,该函数不能具有放弃CPU使用权的功能。

 

11可剥夺型内核

  当系统响应时间很重要,就需要使用可剥夺型内核。最高优先级任务一旦就绪,总能得到CPU的使用权。当运行的任务使一个更高优先级的任务进入就绪态,当前任务的CPU使用权就被剥夺或挂起,更高优先级的任务获得CPU的使用权。如果是中断服务子程序造成的,中断完成后被中断的任务被挂起,执行更高优先级的任务。

  在可剥夺型内核中,要谨慎使用不可重入函数。因为低优先级的和高优先级的任务有可能都调用该函数。

  总是让就绪态的高优先级的任务先运行,中断服务程序可以抢占CPU

 

12可重入函数

  可重入函数或者只使用局部变量,要么就是使用全局变量并且对全局变量予以保护。

 

13时间片轮番调度法

  UCOS中各个任务的优先级都是不同的。。不支持时间片轮番调度

 

14任务优先级

  从0开始,越小的优先级越高

 

15静态优先级

  在执行过程中优先级是不变的

 

16动态优先级

  优先级在运行过程中可以变化

 

17优先级反转

  避免优先级反转的方法是使占有共享资源的任务的优先级升到最高。

 

18任务优先级分配


19互斥条件

  满足互斥条件的一般方法:

  关中断

  使用测试并置位指令,获得共享资源时置位,只有为零才有权获得共享资源

  禁止作任务切换:用给任务切换上锁然后开锁的方法

  利用信号量:信号量的操作:初始化create 挂起pend 发送post

  UCOS中是等待信号量任务中优先级最高的


20死锁

  死锁也称为抱死指两个任务无限期地互相等待对方控制着的资源。

 

21同步

  可以单向同步也可以双向同步

 

22事件标志

  当某个任务要与多个事件同步时,须使用事件标志。

 

23任务间通信

  任务间或中断服务与任务间的通信。有两种途径:通过全程变量或者发消息给另一个任务

 

24消息邮箱

  把一则消息放到邮箱,通过内核也可以接收这则消息。

 

25消息队列

  用于给任务发消息。

 

26中断


27中断延迟


28中断响应


29中断恢复时间

  对于可剥夺型内核,中断的恢复要复杂一些。在中断服务子程序的末尾,要调用一个由实时内核提供的函数。这个函数用于判断中断是否脱离了所有嵌套。如果脱离了嵌套就判断是否中断让一个更高优先级的任务进入就绪。

 

30中断延迟、响应及恢复


31中断处理时间


32非屏蔽中断


33时钟节拍


34对存储器的需求

  使用多任务内核时内核本身需要额外的代码空间ROM。因为每个任务都是独立运行的,必须给每个任务提供单独的栈空间RAM。


------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------

 

内核结构
1临界段,OS_ENTER_CRITICAL和OS_EXIT_CRITICAL

  开关中断的实现方法分三种:

  1)直接用处理器指令

  2)在堆栈中保存中断的开关状态,然后再关中断。

  3)通过编译器提供的c函数来保存处理器状态字的值。

 

2任务


3任务状态

  睡眠态:在ROM或RAM中,交给UCOS要调用下面两个函数之一:OSTaskCreate或者OSTaskCreateExt,调用之后告诉了UCOS任务的起始地址,优先级,要使用多少栈空间。

  就绪态:建立了任务之后,就进入就绪态。如果是由任务建立的新任务且优先级更高,那么新建的任务将立即得到CPU使用权。通过OSTaskDel将一个任务返回到睡眠态。

  运行态:调用OSStart可以启动多任务,该函数运行用户初始化代码中已建的优先级最高的任务。

  等待态:正在运行的任务通过两个函数将自己延迟即进入等待状态。OSTimeDly或者OSTimeDlyHMSM。这两个函数将会立即执行强制任务切换,让下一个优先级最高且进入就绪态的任务运行。当延时时间到后,系统服务函数OSTimeTick将会让等待的任务进入就绪态。在运行中的任务如果调用OSFlagPend、OSSemPend、OSMutexPend、OSMboxPend或者OSQPend时时间并没有发生,那么该任务就进入等待态,此时最高优先级的就绪态任务进入运行态。当等待事件发生或者等待超时,该任务就从等待态进入就绪态。

  中断服务态:正在运行的任务可以被中断。被中断了的任务进入中断服务态。响应中断时该任务被挂起,中断服务子程序可能报告多个事件发生,从而使得更高优先级的任务进入就绪态,当中断返回时会重新判断优先级,让最高优先级的任务运行,如果由更高优先级的任务那么先执行,直到被挂起的任务的优先级最高才会返回到被中断挂起的任务。

 

4任务控制块TCB

  当任务建立的时候一个任务控制块TCB就被赋值,任务控制块能确保任务从被中断的那一点丝毫不差的继续运行。OS_TCB全部驻留在RAM中。OS_TCB中包括:指向当前任务堆栈栈顶的指OSTCBStkPtr、指向用户定义的任务控制块扩展OSTCBExtPtr(该扩展数据结构包含任务名字、跟踪某个任务的执行事件、跟踪切换到某个任务的次数)、指向任务堆栈栈底的指针OSTCBStkBottom(如果堆栈指针递减则指向任务使用的栈空间的最低地址)、存有栈中可容纳的指针元数目OSTCBStkSize(是指针元数目,乘以地址宽度即为实际栈容量)、选择项OSTCBOpt(传给OSTaskCreateExt)、用于存储任务的识别码OSTCBId、用于任务控制块双向链表的前后链接OSTCBNext/OSTCBPrev、指向事件控制块的指针OSTCBEventPtr、指向传递给任务的消息的指针OSTCBMsg、指向事件标志节点的指针OSTCBFlagNode、使任务进入就绪态的事件标志OSTCBFlagsRdy、延时或者挂起一段事件时用来计时的变量OSTCBDly、任务的状态字OSTCBStat(OS_STAT_READY-就绪态)、任务的优先级OSTCBPrio、用于加速任务进入就绪态的过程或进入等待事件发生状态的过程OSTCBX/OSTCBY/OSTCBBitX/OSTCBBitY、一个表示该任务是否须删除的布尔量OSTCBDelReq。所有的任务控制块OS_TCB都是放在任务控制块列表数组OSTCBTbl【】中。所有任务控制块OS_TCB都被链接成单向空任务链表。任务一旦建立,空任务控制块指针OSTCBFreeList指向的任务控制块便赋给了该任务。任务建立时,任务建立函数调用任务控制块初始化函数OS_TCBInit。在初始化OS_TCB的时候调用了用户自定义的函数OSTCBInitHook和OSTaskCreateHook函数。两个函数都是扩展作用的。当新建的任务块要插入表中时先要关中断,新任务的控制块总是插在OSTCBList表的开头。

 

5就绪表

  每个就绪的任务都放在就绪表中,就绪表中有2个变量OSRdyGrp和OSRdyTbl【】。OSRdyGrp中任务按优先级分组,8个任务一组,8位表示8组任务中每组是否有进入就绪态的任务。进入就绪态后,就绪表OSRdyTbl【】中相应元素的相应位置1。因此,使任务进入就绪态的程序为:

OSRdyGrp                 |=  OSMapTbl[prio>>3];

OSRdyTbl[prio>>3]  |=  OSMapTbl[prio & 0x07];  OSMapTbl[]是屏蔽字,将0-7的下标转换成各自位置1的8位值

  通过优先级判定表OSUnMapTbl查找任务的优先级,即OSUnMapTbl[OSRdyGrp]×8+OSUnMapTbl[OSRdyTbl[OSUnMapTbl[OSRdyGrp]]]。得到优先级后,查任务控制块优先级表OSTCBPrioTbl【】得到指向相应任务的任务控制块OS_TCB。

 

6任务调度

  任务级的调度是由OSSched完成,中断级的调度是由函数OSIntExt完成的。任务调度函数将找出优先级最高的进入就绪态的任务,检查该任务是否是当前正在运行的任务,如果不是才进行任务调度。为了实现任务的切换将该任务的控制块从任务控制块优先级表中取出并赋给OSTCBHighRdy,将统计切换次数的变量加1来跟踪任务切换次数。最后就可以使用宏调用OS_TASK_SW完成实际上的任务切换

  任务切换:将被挂起任务的寄存器推入堆栈再将准备运行的任务的寄存器从栈中恢复到寄存器。因此UCOS运行就绪态任务要做的就是恢复所有的CPU寄存器并运行中断返回指令。这里是一段重点,为了实现任务切换,运行OS_TASK_SW人为模仿了一次中断。在ARM中由软中断指令来实现上述操作。必须给汇编语言函数OSCtxSw提供中断向量。OSCtxSw除了需要OS_TCBHighRdy指向即将被挂起的任务,还需让当前任务控制块OSTCBCur指向即将被挂起的任务。OSCtxSw挂起了正在执行的任务而让CPU执行更重要的任务。

 

7任务级的任务切换OSCtxSw

  OSCtxSw是宏调用通常含有软中断指令,切换是靠中断级代码完成的。UCOS将与实际处理器相关的软件中断机制封装起来易于移植。P93-P95详细介绍了整个过程。恩。。大概明白了任务切换的实现方式,为我理解移植工作打下了不小的基础啊。。。。

 

--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

UCOS-II移植ARM的读书笔记(12.20)

--------------------------------------------------------------------------------

--------------------------------------------------------------------------------


郁闷,昨天写的东西又忘记存起来了。。。今天又要返工

简单回顾一下昨天看的

8 给调度器上锁和开锁

   上锁函数OSSchedlock,调用该函数可以禁止任务调度,保持该任务对CPU的使用权,不过中断还是可以识别,中断服务也能得到,因为中断是开着的,中断和调度是两个意思。其中变量OSLockNesting跟踪OSSchedLock函数被调用的次数所以允许嵌套函数。如果OSLockNesting=0调度重新得到允许。

 

9空闲任务

    UCOS-II中总要建立一个空闲任务,主要是计数,然后有一个用户定义的函数OSTaskIdleHook。

 

10统计任务

   除了空闲任务还有一个统计运行时间的任务OSTaskStat,它告诉用户应用程序使用了多少CPU时间,用百分比表示。

 

11UCOS-II中的中断

   中断服务子程序的编写:保存全部CPU寄存器;调用OSIntEnter或者OSIntNesting直接加1;如果是中断的第一层,立即将堆栈指针保存到这个任务;如果需要重新允许中断,必须清中断源,重新开中断;用户设定;调用脱离中断函数OSIntExit,标志着中断服务子程序的结束。

   OSIntExit是使中断离开的函数,当中断嵌套层数计数器和锁定嵌套计数器都为0才重新开始调度,然后选择一个优先级最高的任务。最后应该调用中断切换函数OSIntCtxSw而不应该调用任务切换函数OS_TASK_SW。因为在中断之前已经把CPU寄存器存入到中断了的任务堆栈中不需要再用。这部分也牵涉到后面的移植部分。

 

12时钟节拍

   在调用OSStart之后应做的第一件事情就是初始化定时器中断。时钟节拍服务是通过在中断服务子程序中调用OSTimeTick实现的。

   时钟节拍中断服务子程序:

OSTickISR(void)

{

   保存CPU寄存器的值

   调用OSIntEnter或是将OSIntNesting加1

   如果OSIntNesting等于1则将当前堆栈指针存入当前任务控制块的堆栈中

   调用OSTimeTick

   清发出中断设备的中断

   重新允许中断

   调用OSIntExit

   恢复处理器寄存器的值

   执行中断返回指令

}

其中OSTimeTick通过OSTimeTickHook函数进行扩展。除此之外最大的任务就是给每个用户任务控制块OS_TCB中的时间延时项OSTCBDly减1

 

13UCOS-II初始化

    OSInit函数

void  OSInit (void)
{
#if OS_VERSION >= 204
    OSInitHookBegin();                                           /* Call port specific initialization code   */
#endif

    OS_InitMisc();                                               /* Initialize miscellaneous variables       */

    OS_InitRdyList();                                            /* Initialize the Ready List                */
    OS_InitTCBList();                                            /* Initialize the free list of OS_TCBs      */
    OS_InitEventList();                                          /* Initialize the free list of OS_EVENTs    */

#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
    OS_FlagInit();                                               /* Initialize the event flag structures     */
#endif

#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
    OS_MemInit();                                                /* Initialize the memory manager            */
#endif

#if (OS_Q_EN > 0) && (OS_MAX_QS > 0)
    OS_QInit();                                                  /* Initialize the message queue structures  */
#endif

    OS_InitTaskIdle();                                           /* Create the Idle Task                     */
#if OS_TASK_STAT_EN > 0
    OS_InitTaskStat();                                           /* Create the Statistic Task                */
#endif

#if OS_VERSION >= 204
    OSInitHookEnd();                                             /* Call port specific init. code            */
#endif
}

初始化中会建立两个任务,并且初始化5个空的数据结构缓冲区:任务控制缓冲池、事件控制块缓冲池、消息队列缓冲池、标志控制块缓冲池、存储控制块缓冲池,缓冲池的容量在OS_CFG.H中定义

 

14UCOS-II的启动

    OSInit初始化UCOS-II;

    通过调用OSTaskCreate或者OSTaskCreateExt创建至少一个任务;

   OSStart开始多任务调度,永远不会返回;

 

OSStart的主要任务:

   从任务就绪表中找到用户建立的优先级最高任务的任务控制块;

   调用高优先级就绪任务启动函数OSStartHighRdy。OSStartHighRdy函数与选择的微处理器相关,也就是与移植相关,实际上,函数OSStartHighRdy是将任务栈中保存的值弹回到CPU寄存器中,然后执行一条中断返回指令,中断返回指令强制执行该任务代码,该函数永远不会返回到OSStart

 

内核终于裹完了一遍,也明朗了很多,感觉可以开始继续上个星期的移植工作了,恩。。。加油加油


--------------------------------------------------------------------------------

--------------------------------------------------------------------------------


之前刚开始的时候是直接看移植代码,后来看到后面实在看不下去了,转过头回去看了一个星期的内核结构,以前也看过一遍内核结构,但是有点晕晕的,现在重新看了一次清楚多了,相信回过头来看移植部分也应该更清楚了。

现在先来掌握一下关于软件中断swi的内容,这是我比较发晕的源泉

 

软中断:

中断不返回形式:void _swi(swi_num)  swi_name(arguments)

返回一个结果到R0中  int _swi(swi_num)  swi_name(arguments);最多可以返回四个结果R0-R3到一个结构struct type{ int a,b,c,d}中 type(返回类型) _value_in_regs(返回多个结果的修饰符) _swi(swi_num) swi_name(arguments);

 

在ARM中实现软中断的方法我在blog里面搜了很多文章也没有看到讲的通俗一点的,还是自己看ARM的移植代码吧

首先定义了一堆软中断的中断号,其中0和1的中断服务子程序是用汇编编写的,其他的都是在c语言编写的中断服务子程序SWI_Exception中。

__swi(0x00) void OS_TASK_SW(void);              /*  任务级任务切换函数          */
__swi(0x01) void _OSStartHighRdy(void);         /*  运行优先级最高的任务        */
__swi(0x02) void OS_ENTER_CRITICAL(void);       /*  关中断                      */
__swi(0x03) void OS_EXIT_CRITICAL(void);        /*  开中断                      */

__swi(0x40) void *GetOSFunctionAddr(int Index); /*  获取系统服务函数入口        */
__swi(0x41) void *GetUsrFunctionAddr(int Index);/*  获取自定义服务函数入口      */
__swi(0x42) void OSISRBegin(void);              /*  中断开始处理                */
__swi(0x43) int  OSISRNeedSwap(void);           /*  判断中断是否需要切换        */

__swi(0x80) void ChangeToSYSMode(void);         /*  任务切换到系统模式          */
__swi(0x81) void ChangeToUSRMode(void);         /*  任务切换到用户模式          */
__swi(0x82) void TaskIsARM(INT8U prio);         /*  任务代码是ARM代码           */
__swi(0x83) void TaskIsTHUMB(INT8U prio);       /*  任务代码是THUMB             */

比如在程序运行到调用OS_TASK_SW(void)函数时,就产生软件中断,然后就进入中断服务子程序,按照什么指令走呢?恩,就按照下面这个代码,这个代码是将软件中断异常处理程序挂接到内核的作用的,是在启动代码中实现的:

LDR   PC,SWI_Addr

 

SWI_Addr   DCD     SoftwareInterrupt

因此当产生软中断之后PC就跳到了SoftwareInterrupt,这时就算真正进入了软件异常中断处理部分了,然后就是执行下面的汇编代码

SoftwareInterrupt
        LDR     SP, StackSvc            ; 重新设置堆栈指针
        STMFD   SP!, {R0-R3, R12, LR}
        MOV     R1, SP                  ; R1指向参数存储位置

        MRS     R3, SPSR
        TST     R3, #T_bit              ; 中断前是否是Thumb状态,判断SPSR的T位是不是为零

        LDRNEH  R0, [LR,#-2]            ; 不为零即THUMB指令集: 取得Thumb状态SWI号
        BICNE   R0, R0, #0xff00          ;在THUMB指令集中软中断功能号为8位,所以取低八位即为功能号
        LDREQ   R0, [LR,#-4]            ; 为零即ARM指令集: 取得arm状态SWI号
        BICEQ   R0, R0, #0xFF000000         ;在ARM指令集中软中断功能号为24位,所以取低6位即为功能号
                                        ; r0 = SWI号,R1指向参数存储位置
        CMP     R0, #1
        LDRLO   PC, =OSIntCtxSw      ;功能号为0到OSIntCtxSw执行中断任务切换函数
        LDREQ   PC, =__OSStartHighRdy   ; SWI 0x01为第一次任务切换

        BL      SWI_Exception           ;否则进入c编写的中断服务函数
       
        LDMFD   SP!, {R0-R3, R12, PC}^
       
StackSvc           DCD     (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)

怎么进入c编写的中断服务子程序SWI_Exception呢?通过下面的申明

IMPORT  SWI_Exception               ;软中断异常处理程序,表示将c程序中的该函数挂接到此段汇编代码中

同样的道理

EXPORT  __OSStartHighRdy            
EXPORT  OSIntCtxSw                  ;中断退出时的入口,参见startup.s中的IRQ_Handler

EXPORT  SoftwareInterrupt           ;软中断入口

上面的申明是将该段汇编代码挂接到外面,因此在外部可以直接调用函数名

 

继续看OS_CPU_A.S的其他部分代码,就是两个软件异常中断处理函数OSIntCtxSw和OSStarHighRdy

OSIntCtxSw代码是中断服务子程序使得更高优先级的任务进入就绪状态后,中断返回后需要切换到该任务时调用的,这是被切换的任务的CPU寄存器的值已经在响应中断后存入了堆栈中,因此,这里不需要重复保存了直接切换任务即可,具体过程看代码

OSIntCtxSw
                                                    ;下面为保存任务环境

                                                    ;当响应软件异常中断后进入了系统模式,在上面的代码中我们可以看到,进入系统模式时保存的堆栈结构从顶到底依次是:R0,R1,R2,R3,R12,LR,而在用户模式中任务的堆栈结构应该是:OsEnterSum,CPSR,RO-12,LR,PC,所以在进行软件中断任务切换之前先要保存原来任务的堆栈结构。
        LDR     R2, [SP, #20]                       ;获取PC
        LDR     R12, [SP, #16]                      ;获取R12
        MRS     R0, CPSR

        MSR     CPSR_c, #(NoInt | SYS32Mode)
        MOV     R1, LR
        STMFD   SP!, {R1-R2}                        ;保存LR,PC
        STMFD   SP!, {R4-R12}                       ;保存R4-R12

        MSR     CPSR_c, R0
        LDMFD   SP!, {R4-R7}                        ;获取R0-R3
        ADD     SP, SP, #8                          ;出栈R12,PC
       
        MSR     CPSR_c, #(NoInt | SYS32Mode)
        STMFD   SP!, {R4-R7}                        ;保存R0-R3
       
        LDR     R1, =OsEnterSum                     ;获取OsEnterSum
        LDR     R2, [R1]
        STMFD   SP!, {R2, R3}                       ;保存CPSR,OsEnterSum

                                                    ;保存当前任务堆栈指针到当前任务的TCB
        LDR     R1, =OSTCBCur
        LDR     R1, [R1]
        STR     SP, [R1]

        BL      OSTaskSwHook                        ;调用钩子函数
                                                    ;OSPrioCur <= OSPrioHighRdy
        LDR     R4, =OSPrioCur
        LDR     R5, =OSPrioHighRdy
        LDRB    R6, [R5]
        STRB    R6, [R4]
                                                    ;OSTCBCur <= OSTCBHighRdy
        LDR     R6, =OSTCBHighRdy
        LDR     R6, [R6]
        LDR     R4, =OSTCBCur
        STR     R6, [R4]
OSIntCtxSw_1
                                                    ;获取新任务堆栈指针
        LDR     R4, [R6]
        ADD     SP, R4, #68                         ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP
        LDR     LR, [SP, #-8]
        MSR     CPSR_c, #(NoInt | SVC32Mode)        ;进入管理模式
        MOV     SP, R4                              ;设置堆栈指针

        LDMFD   SP!, {R4, R5}                       ;CPSR,OsEnterSum
                                                    ;恢复新任务的OsEnterSum
        LDR     R3, =OsEnterSum
        STR     R4, [R3]
   
        MSR     SPSR_cxsf, R5                       ;恢复CPSR
        LDMFD   SP!, {R0-R12, LR, PC }^             ;运行新任务



--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

UCOS-II移植ARM的读书笔记(12.25) 


--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

__OSStartHighRdy
        MSR     CPSR_c, #(NoInt | SYS32Mode)    ;调整到管理模式
                                                ;告诉uC/OS-II自身已经运行
        LDR     R4, =OSRunning
        MOV     R5, #1
        STRB    R5, [R4]   ;标记多任务运行标记为真

        BL      OSTaskSwHook                    ;调用钩子函数,可以运行用户自定义的函数


        LDR     R6, =OSTCBHighRdy    ;R6存有最高优先级的就绪任务的控制块地址
        LDR     R6, [R6]
        B       OSIntCtxSw_1     ;转到前面编写的中断返回函数块的任务跳转部分的代码,因为这两个函数都要用到这部分代码,进入这段代码之前高优先级的就绪任务的任务控制快地址存在R6中。


        AREA    SWIStacks, DATA, NOINIT,ALIGN=2
SvcStackSpace      SPACE   SVC_STACK_LEGTH * 4  ;管理模式堆栈空间 


OSIntCtxSw_1的代码:

OSIntCtxSw_1
                                                    ;获取新任务堆栈指针
        LDR     R4, [R6] ;任务控制块的堆栈指针放在R6中,现在放在R4中
        ADD     SP, R4, #68                         ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP
        LDR     LR, [SP, #-8]
        MSR     CPSR_c, #(NoInt | SVC32Mode)        ;进入管理模式
        MOV     SP, R4                              ;设置堆栈指针,R4存有没有改动过的堆栈指针

        LDMFD   SP!, {R4, R5}                       ;CPSR,OsEnterSum
                                                    ;恢复新任务的OsEnterSum
        LDR     R3, =OsEnterSum
        STR     R4, [R3]
   
        MSR     SPSR_cxsf, R5                       ;恢复CPSR
        LDMFD   SP!, {R0-R12, LR, PC }^             ;运行新任务,恢复现场,异常处理返回;中断返回指令的寄存器列表其中必须包括PC后的^符号,表示这是一条特殊形式的指令。这条指令在从存储器中装载PC的同时,CPSR也得到恢复。这里使用的堆栈指针SP是属于异常模式的寄存器,每个异常模式有自己的堆栈指针。

 

 

 

SoftwareInterrupt
        LDR     SP, StackSvc            ; 重新设置堆栈指针
        STMFD   SP!, {R0-R3, R12, LR}     ;保存寄存器
        MOV     R1, SP                  ; R1指向参数存储位置

        MRS     R3, SPSR
        TST     R3, #T_bit              ; 中断前是否是Thumb状态
        LDRNEH  R0, [LR,#-2]            ; 是: 取得Thumb状态SWI号
        BICNE   R0, R0, #0xff00
        LDREQ   R0, [LR,#-4]            ; 否: 取得arm状态SWI号
        BICEQ   R0, R0, #0xFF000000
                                        ; r0 = SWI号,R1指向参数存储位置
        CMP     R0, #1
        LDRLO   PC, =OSIntCtxSw
        LDREQ   PC, =__OSStartHighRdy   ; SWI 0x01为第一次任务切换

        BL      SWI_Exception
       
        LDMFD   SP!, {R0-R3, R12, PC}^
       
StackSvc           DCD     (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)

 

OSIntCtxSw
                                                    ;下面为保存任务环境
        LDR     R2, [SP, #20]                       ;获取PC(LR)
        LDR     R12, [SP, #16]                      ;获取R12
        MRS     R0, CPSR

        MSR     CPSR_c, #(NoInt | SYS32Mode)
        MOV     R1, LR
        STMFD   SP!, {R1-R2}                        ;保存LR,PC
        STMFD   SP!, {R4-R12}                       ;保存R4-R12

        MSR     CPSR_c, R0
        LDMFD   SP!, {R4-R7}                        ;获取R0-R3
        ADD     SP, SP, #8                          ;出栈R12,PC
       
        MSR     CPSR_c, #(NoInt | SYS32Mode)
        STMFD   SP!, {R4-R7}                        ;保存R0-R3
       
        LDR     R1, =OsEnterSum                     ;获取OsEnterSum
        LDR     R2, [R1]
        STMFD   SP!, {R2, R3}                       ;保存CPSR,OsEnterSum

                                                    ;保存当前任务堆栈指针到当前任务的TCB
        LDR     R1, =OSTCBCur
        LDR     R1, [R1]
        STR     SP, [R1]

        BL      OSTaskSwHook                        ;调用钩子函数
                                                    ;OSPrioCur <= OSPrioHighRdy
        LDR     R4, =OSPrioCur
        LDR     R5, =OSPrioHighRdy
        LDRB    R6, [R5]
        STRB    R6, [R4]             ;把OSPrioHighRdy最高优先级的就绪任务传给OSPrioCur
                                                    ;OSTCBCur <= OSTCBHighRdy
        LDR     R6, =OSTCBHighRdy
        LDR     R6, [R6]
        LDR     R4, =OSTCBCur
        STR     R6, [R4]         ;将最高优先级的任务控制块指针传给当前任务控制块指针


--------------------------------------------------------------------------------

--------------------------------------------------------------------------------


关于中断和时钟节拍,UCOS-II对于ARM7通用的中断服务程序的汇编与c函数接口如下:

MACRO和MEND伪指令用于宏定义,MACRO标识宏定义的开始,MEND标识宏定义的结束。定义之后在程序中就可以通过宏指令多次调用该段代码

MACRO
$IRQ_Label HANDLER $IRQ_Exception_Function

        EXPORT  $IRQ_Label                      ; 输出的标号
        IMPORT  $IRQ_Exception_Function         ; 引用的外部标号

$IRQ_Label
        SUB     LR, LR, #4                      ; 计算返回地址
        STMFD   SP!, {R0-R3, R12, LR}           ; 保存任务环境
        MRS     R3, SPSR                        ; 保存状态
        STMFD   SP, {R3, SP, LR}^               ; 保存用户状态的R3,SP,LR,注意不能回写
                                                ; 如果回写的是用户的SP,所以后面要调整SP
        LDR     R2,  =OSIntNesting              ; OSIntNesting++
        LDRB    R1, [R2]
        ADD     R1, R1, #1
        STRB    R1, [R2]

        SUB     SP, SP, #4*3
       
        MSR     CPSR_c, #(NoInt | SYS32Mode)    ; 切换到系统模式
        CMP     R1, #1
        LDREQ   SP, =StackUsr
       
        BL      $IRQ_Exception_Function         ; 调用c语言的中断处理程序

        MSR     CPSR_c, #(NoInt | SYS32Mode)    ; 切换到系统模式
        LDR     R2, =OsEnterSum                 ; OsEnterSum,使OSIntExit退出时中断关闭
        MOV     R1, #1
        STR     R1, [R2]

        BL      OSIntExit

        LDR     R2, =OsEnterSum                 ; 因为中断服务程序要退出,所以OsEnterSum=0
        MOV     R1, #0
        STR     R1, [R2]

        MSR     CPSR_c, #(NoInt | IRQ32Mode)    ; 切换回irq模式
        LDMFD   SP, {R3, SP, LR}^               ; 恢复用户状态的R3,SP,LR,注意不能回写
                                                ; 如果回写的是用户的SP,所以后面要调整SP
        LDR     R0, =OSTCBHighRdy
        LDR     R0, [R0]
        LDR     R1, =OSTCBCur
        LDR     R1, [R1]
        CMP     R0, R1

        ADD     SP, SP, #4*3                    ;
        MSR     SPSR_cxsf, R3
        LDMEQFD SP!, {R0-R3, R12, PC}^          ; 不进行任务切换
        LDR     PC, =OSIntCtxSw                 ; 进行任务切换
    MEND
广告

文章评论 4条评论)

登录后参与讨论

billzhu_345737527 2013-5-9 18:24

谢谢,写的太好了

billzhu_345737527 2012-10-26 12:33

谢谢

谢谢 2008-9-25 08:52

非常感谢,前进的捷径啊。

zxw_yxt_767338062 2007-12-29 10:41

谢谢

 

相关推荐阅读
embedtek 2007-10-31 20:43
ARM汇编的SWI指令软中断
从下面的一个ARM 汇编小程序要弄懂的以下三个问题:1).在ARM状态转到THUNB状态和BX的应用2).汇编的架构3).SWI指令的使用    AREA ADDREG,CODE,READONLY  ...
embedtek 2007-10-31 20:30
uc/OS II移植中软件中断的理解与应用
uc/OS II移植中软件中断的理解与应用1.   软件中断SWISWI(software interrupt)软件中断,由用户定义的中断指令.可以用于用户模式下的程序调用特权操作指令.在实时操作系统...
embedtek 2007-10-31 20:29
Luminary Micro半导体-LM3S101微控制器(超低价格的ARM芯片)
概述  Luminary Micro StellarisTM系列的微控制器是首款基于ARM® CortexTM-M3的控制器,它将高性能的32位计算引入到对价格敏感的嵌入式微控制器应用中。这些堪称先锋...
embedtek 2007-10-31 19:49
ARM9微控制器LPC3180的软硬件平台设计
来源:网络 作者:不详 发布时间:2007-03-02   摘要 介绍以Philips LPC3180微控制器为核心的嵌入式软硬件平台设计;对系统设计的硬件部分和软件部分进行详细的分析,并针对LPC3...
embedtek 2007-10-31 19:47
AVR单片机的RTOS-AVRX应用
来源:网络 作者:不详 发布时间:2007-03-02 摘  要:详细介绍AVR系列单片机的专用实时嵌入式操作系统AVRX的特点,并以ATmega16单片机为平台,结合AVR单片机应用实例分析AVRX...
我要评论
4
0
广告
关闭 热点推荐上一条 /3 下一条