打印
[文档下载]

uC/OS ii与μc/gui移植详细介绍

[复制链接]
1537|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
小猫爱吃鱼|  楼主 | 2015-11-4 20:33 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
uC/OS ii文件体系结构:
1:核心部分(OSCore.c)
   这部分代码是操作系统的核心,包括操作系统的初始化、操作系统运行、中断进出的前导、时钟节拍、任务调度、事件处理等多部分。能够维持系统基本工作的部分都在这里。
2:任务处理部分(OSTask.c)
   任务处理部分中的代码内容都是与任务的操作密切相关的包括任务的建立、删除、挂起、恢复等待。因为uC/OS ii是以任务为基本单位进行调度的,所以这部分内容相当重要。
3:时钟部分(OSTime.c)
   uC/OS ii的最小时钟单位是timetick(时钟节拍)。任务延时等操作就是在这里完成。
4:任务同步与通信部分
   这部分代码为事先处理部分(需要在OS_cfg.h文件中进行功能选择使能),包括信号量、邮箱、邮箱队列、事件标志等部分,主要用于任务间的相互联系和对临界资源的访问。
5:CPU的接口部分(Ports)--需要移植的代码部分
这部分是需要根据具体的CPU来做移植,且这部分由于很多涉及到具体硬件,所以很多都是用汇编来写。主要包括中断级任务切换的底层实现、任务级任务切换的底层实现、时钟节拍的产生和处理、中断的相关部分等。

uC/OS ii移植需要满足的条件:
1:处理器支持中断,并能够产生定时中断(通常在10~1000HZ)
   uC/OS ii是通过中断(软)来实现任务现场的切换,做到任务的切换;uC/OS ii中需要时基函数来进行任务的延时等(uC/OS iii支持时间片轮转,所以时基还用来做任务切换),M3中有一个硬件的systick定时器,专门用来产生定时中断。
2:在程序中可以打开或者关闭中断
  uC/OS ii中,打开/关闭中断主要通过OS_ENTER_CRITICAL()或OS_EXIT_CRITICAL()两个宏实现,在任务的切换时,是需要关中断的(进入临界区)。
3:处理器的C编译器能产生可重入代码
  uC/OS ii中,大部分函数都是用C编写的,所以编译时,需要产生可重入的代码(可以被一个以上的任务调用),可重入代码中不能使用全局变量,且可重入代码中的局部变量一般使用CPU的通用寄存器或堆栈(这个需要编译器支持),所以对编译器是有要求的。
4:处理器需具备一定量数据的硬件堆栈
  uC/OS ii中,有一些系统使用的数据结构(各种列表来管理系统的资源)需要一定的RAM空间,同时任务切换、局部变量等都需要使用堆栈,所以对RAM(堆栈也有要求),M3中,应用程序(PSP)与异常服务程序(MSP)使用的堆栈指针是不一样的,这样起保护作用。

沙发
小猫爱吃鱼|  楼主 | 2015-11-4 20:34 | 只看该作者
5:处理器有将堆栈指针和其他CPU寄存器 存储与读出到内存(堆栈)的指令
  uC/OS ii中,任务切换时一般要读取CPU的各个通用与特殊功能寄存器,M3用MRS、MSR等命令。

uC/OS ii的移植要点:
uC/OS ii的移植相当LINUX来说是非常简单的,uC/OS ii的移植涉及到的代码很少;我们只需要修改与处理器相关的代码即可;
  OS_CPU.H:设置与处理器与编译器有关的代码
  OS_CPU_C.C:在这其中用C语言编写6个与操作系统相关的函数
  OS_CPU.ASM:在这其中用汇编语言编写4个与处理器相关的函数
  如上图,需要移植的部分就是Ports路径下的几个代码文件,其他的代码都不要移植(os_cfg.h只是用来配置系统的各个功能,做系统的裁剪而且,ucos_ii.h中提供系统所有的接口函数,os_cpu_a.asm就是OS_CPU.ASM)
1:修改OS_CPU.H
OS_CPU.H中定义了数据类型、处理器的堆栈数据类型的字长、堆栈增长方向、任务切换宏、临界区访问处理。
  从方便移植的角度,uC/OS ii使用的数据类型,都是自己定义的那一套。
typedef unsigned char  BOOLEAN;
      typedef unsigned char  INT8U;                  
      typedef signed   char  INT8S;                    
      typedef unsigned short INT16U;                  
      typedef signed   short INT16S;                  
      typedef unsigned long  INT32U;                  
      typedef signed   long  INT32S;                  
      typedef float          FP32;                    
      typedef double         FP64;   
                  
      typedef unsigned int   OS_STK;                  
      typedef unsigned int   OS_CPU_SR;   



使用特权

评论回复
板凳
小猫爱吃鱼|  楼主 | 2015-11-4 20:36 | 只看该作者
M3使用的是向下生长的满栈,堆栈指针指向最后一个被压入堆栈的32位整数:
#define  OS_STK_GROWTH  1 

定义开关中断的宏:
#define  OS_CRITICAL_METHOD   3
      #if OS_CRITICAL_METHOD == 3
      #define  OS_ENTER_CRITICAL()  {cpu_sr = OS_CPU_SR_Save();}
      #define  OS_EXIT_CRITICAL()   {OS_CPU_SR_Restore(cpu_sr);}
      #endif

定义宏OS_TASK_SW执行任务切换:
#define  OS_TASK_SW()         OSCtxSw()

使用特权

评论回复
地板
小猫爱吃鱼|  楼主 | 2015-11-4 20:37 | 只看该作者
2:在OS_CPU_C.C中要求用户编写10个简单的C函数【唯一必要修改的函数是OSTaskStkInit(),其余的函数必须声明,但并不一定要包含任何代码】
OSTaskStkInit():任务创建函数OSCreate()与OSCreateExt()通过调用OSTaskStkInit,初始化任务的栈结构,因此,任务的堆栈看起来就像刚发生了中断一样(所以的寄存器按一定的次序都保存在堆栈中,为什么要这样做?因为任务间的切换就是任务堆栈中保存的CPU现场的切换,所以任务被创建时,要初始化它的堆栈,为后面的切换做准备)
OSTaskCreateHook(ptcb):【系统提供给用户的钩子函数】任务创建时OSCreate()与OSCreateExt()调用OS_TCBInit,OS_TCBInit会调用OSTaskCreateHook,同时传递指向刚刚建立的任务的任务控制块的指针,这样OSTaskCreateHook就可以访问任务控制块结构的所有成员,这个函数的功能有限,不是必要的函数,且需要通过OS_APP_HOOKS_EN使能,且该函数调用时中断是开着的。
OSTaskDelHook(ptcb):OSTaskDel()会调用OSTaskDelHook(ptcb),即在将任务从uC/OS II的内部有效链表中删除之前被调用,同时传递一个指向被删除任务的任务控制块的指针,OSTaskDelHook可以用来检验TCB扩展部分是否已建立(一个非空指针),并可进行一些清0操作,OSTaskDelHook调用时,中断是关闭的,函数太长会影响中断响应时间。
OSTaskSwHook():在任务切换时会调用OSTaskSwHook,不论是在OSCtxSw()还是OSIntCtxSw(),都会调用该函数,OSTaskSwHook可访问全局变量OSTCBCur(指向将被切换出去的任务的任务控制块)、OSTCBHiggRdy(指向新任务的任务控制块) ,OSTaskSwHook被调用时,中断是关闭的,函数太长会影响中断响应时间。
OSTaskIdleHook():很多微处理器都允许执行相应的指令,将CPU置于低功耗模式,而当接收到中断信号时,CPU就会自动退出低功耗模式。OS_TaskIdle()函数可以调用OSTaskIdleHook()来实现CPU的这种低功耗模式【很好】。
OSTaskStatHook():该函数每秒都会被统计任务OS_TaskStat()【统计任务每秒执行一次】调用一次,可以通过该函数扩展统计任务的功能,如可跟踪并显示每个任务的执行时间、每个任务所用的CPU份额以及每个任务执行的频率等等,该函数没有任何参数。
OSTimeTickHook():该函数在每个时钟节拍都会被OSTimeTick()调用;systick中断服务程序SysTickHandler调用OSTimeTickHook,在这里面用户可以处理应急的事务。
OSInitHookBegin():进入OSInit()函数后,OSInitHookBegin就会被调用,添加这个函数的原因在于想把与OS有关的初始化代码也放在OSInti()函数中,这个函数使得用户可以将自己特定的代码也放在OSInit()函数中。
OSInitHookEnd():OSInitHookEnd与OSInitHookBegin相似,只是他在OSInit()函数返回之前被调用,添加这个函数的原因与OSInitHookBegin是一样的。
OSTCBInitHook(ptcb):任务创建时OSCreate()与OSCreateExt()调用OS_TCBInit,OS_TCBInit会在调用OSTaskCreateHook前调用 OSTCBInitHook ,用户可以在OSTCBInitHook函数中做一些与初始化控制块OS_TCB有关的处理,在OSTaskCreateHook中做一些与初始化任务有关的处理;OSTCBInitHook收到的ptcb参数指向新添加任务的任务控制块的指针,而这个新添加任务的任务控制块绝大部分已经初始化完成,但是还没有链接到已经建立任务的链表中。

使用特权

评论回复
5
小猫爱吃鱼|  楼主 | 2015-11-4 20:38 | 只看该作者
OS_STK * OSTaskStkInit (void (*task)(void *p_arg), 
                        void *p_arg,
                        OS_STK *ptos,
                        INT16U opt)
{
    OS_STK *stk;
    (void)opt;                                
    stk       = ptos;   
    *(stk)    = (INT32U)0x01000000L;           //xPSR                                 
    *(--stk)  = (INT32U)task;                  //Entry Point                          
*(--stk) = (INT32U)0xFFFFFFFEL; // R14(LR) (init value will cause fault if ever used)   
    *(--stk)  = (INT32U)0x12121212L;          // R12                                 
    *(--stk)  = (INT32U)0x03030303L;          // R3  
    *(--stk)  = (INT32U)0x02020202L;           // R2   
    *(--stk)  = (INT32U)0x01010101L;           //  R1   
    *(--stk)  = (INT32U)p_arg;                 // R0 : argument  

   //Remaining registers saved on process stack
    *(--stk)  = (INT32U)0x11111111L;           // R11  
    *(--stk)  = (INT32U)0x10101010L;           // R10  
    *(--stk)  = (INT32U)0x09090909L;           // R9   
    *(--stk)  = (INT32U)0x08080808L;           // R8      
    *(--stk)  = (INT32U)0x07070707L;           // R7     
    *(--stk)  = (INT32U)0x06060606L;           // R6     
    *(--stk)  = (INT32U)0x05050505L;           // R5     
    *(--stk)  = (INT32U)0x04040404L;           // R4      
    return (stk);
}

使用特权

评论回复
6
小猫爱吃鱼|  楼主 | 2015-11-4 20:39 | 只看该作者
3:在OS_CPU_A.ASM中用汇编写4个与处理器相关的函数
   需要移植的函数有,如下所示,
OSStartHighRdy():在调用OSStart()函数时,使就绪态任务中优先级最高的任务开始运行
   OSCtxSw():
   OSIntCtxSw():
   OSTickISR():
   OS_CPU_SR_Save()
   OS_CPU_SR_Restore():

OSCtxSw():可以参考学习笔记中的记录,M3中任务切换还需实现软中断的处理函数
OSCtxSw   ;悬起PSV异常
    LDR     R0, =NVIC_INT_CTRL                                 
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR

OSIntCtxSw():在退出中断前调用OSIntCtxSw,在ISR中执行任务的切换功能,OSIntCtxSw与OSCtxSw基本相同,M3的是一模一样的。

OSPendSV 【软中断的处理函数】
MRS     R0, PSP                                            
    CBZ     R0, OSPendSV_nosave                                

    SUBS    R0, R0, #0x20                                      
    STM     R0, {R4-R11}

    LDR     R1, __OS_TCBCur                                    
    LDR     R1, [R1]
    STR     R0, [R1]                                                         
OSPendSV_nosave
    PUSH    {R14}                                               
    LDR     R0, __OS_TaskSwHook                                 ; OSTaskSwHook();
    BLX     R0
    POP     {R14}

    LDR     R0, __OS_PrioCur                                 
    LDR     R1, __OS_PrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    LDR     R0, __OS_TCBCur                                   
    LDR     R1, __OS_TCBHighRdy
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                                          
    LDM     R0, {R4-R11}                                       
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                                             
    ORR     LR, LR, #0x04                                       
    BX      LR   

使用特权

评论回复
7
小猫爱吃鱼|  楼主 | 2015-11-4 20:40 | 只看该作者
OSTickISR在这里即SysTickHandler函数
void SysTickHandler(void)
{
    OS_CPU_SR  cpu_sr;
    OS_ENTER_CRITICAL();  
    OSIntNesting++;
    OS_EXIT_CRITICAL();  
    OSTimeTick();     //主要判断延时的任务是否计时到
    OSIntExit();  //在os_core.c文件里定义,如果有更高优先级的任务就绪了,则执行一次任务切换
}

OSStartHighRdy
    LDR     R0, =NVIC_SYSPRI2                                   
    LDR     R1, =NVIC_PENDSV_PRI
    STRB    R1, [R0]               //这几句是设置PendSV优先级

    MOVS    R0, #0                                             
    MSR     PSP, R0          //设置PSP的初始值0(在第一次PendSV中断服务程序中不用保存CPU的各寄存器)

    LDR     R0, __OS_Running                                 
    MOVS    R1, #1
    STRB    R1, [R0]             // OSRunning = TRUE,表明操作系统已经运行

    LDR     R0, =NVIC_INT_CTRL                                 
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]            //触发PendSV中断,暂时挂起

    CPSIE   I                   //开总中断【一定要在这里才开总中断,如果在此之前任何一个地方开中断,系统一旦响应中断,很有可能造成系统因没有做出足够的准备而出错(比如如果在初始化systicks时就开启了中断,uC/OS此时还处于未知阶段,就会崩溃),所在在这里开启总中断才是安全的】                              

使用特权

评论回复
8
小猫爱吃鱼|  楼主 | 2015-11-4 20:40 | 只看该作者

OS_CPU_SR_Save()
    MRS     R0, PRIMASK   ;保存全局中断标志  
    CPSID   I           ;关中断
    BX      LR
OS_CPU_SR_Restore()
    MSR     PRIMASK, R0    ;恢复全局中断标志
    BX      LR

使用特权

评论回复
9
Luis德华| | 2015-11-6 21:01 | 只看该作者
小猫爱吃鱼 发表于 2015-11-4 20:34
5:处理器有将堆栈指针和其他CPU寄存器 存储与读出到内存(堆栈)的指令
  uC/OS ii中,任务切换时一般要读 ...

ucos iii比ucos ii多在什么地方呢?

使用特权

评论回复
10
Mancherstun| | 2015-11-8 20:41 | 只看该作者
现在觉得ucos ii真的是一个好的操作系统啊

使用特权

评论回复
11
huangcunxiake| | 2016-7-21 20:54 | 只看该作者
来学习一下这个高级的系统。

使用特权

评论回复
12
734774645| | 2016-7-22 07:09 | 只看该作者
移植的时候要注意什么,移植成功后怎么往里面安装软件,还是像电脑一样可以安装程序

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

33

主题

294

帖子

2

粉丝