μCOS II

嵌入式常用RTOS:μC/OS II、FreeRTOS、RT-thread

一、移植

使用的板子是TI的am1808板子,使用串口连接,用sfh_omap-1138.exe烧写程序到spi flash中,开发环境使用CCS5,使用starterware提供的bootloader程序引导系统,在bootloader中,已经配置好了系统参数,所以ucosII就不用再对系统进行初始化

1.1、新建空工程,芯片选择AM1808

1.2、修改AM1808.cmd文件

参照starterware中提供的例子来修改,内容如下

-stack 0x8000 /* SOFTWARE STACK SIZE*/
-heap 0x4000 /*  HEAP AREA SIZE */
-e Entry
MEMORY
{
	EMIFACS0: o = 0x40000000 l = 0x20000000  /*  512MB SDRAM Data  (CS0)*/
	EMIFACS2: o = 0x60000000 l = 0x02000000  /*  32MB Async  Data  (CS2)  */
	EMIFACS3: o = 0x62000000 l = 0x02000000  /*  32MB Async  Data  (CS3)  */
	EMIFACS4: o = 0x64000000 l = 0x02000000  /*  32MB Async  Data  (CS4)  */
	EMIFACS5: o = 0x66000000 l = 0x02000000  /*  32MB Async  Data  (CS5)  */
	SHRAM: o = 0x80000000 l = 0x00020000  /* 128kB Shared RAM */
	DDR_MEM :  org = 0xC1080000 len = 0x2F7FFFF /* RAM */
	ARMROM: o = 0xFFFD0000 l = 0x00010000  /* 64kB ARM local ROM */
	ARMRAM: o = 0xFFFF0000 l = 0x00002000  /* 8kB ARM local RAM */
}
SECTIONS
{
	.init : {
		system_config.lib<init.obj> ( .text )
		} load > 0xC1080000
	.text : load > DDR_MEM /* CODE */
	.data : load  > DDR_MEM
	.bss : load > DDR_MEM /* GLOBAL & STATIC VARS */
			RUN_START(bss_start),
				RUN_END(bss_end)
	.binit > DDR_MEM
	.init_array > DDR_MEM
	.args > DDR_MEM
	.neardata > DDR_MEM
	.fardata > DDR_MEM
	.rodata > DDR_MEM
	.const :  load  > DDR_MEM /* SOFTWARE SYSTEM STACK */
	.cinit :  load  > DDR_MEM /* SOFTWARE SYSTEM STACK */
	.stack : load > 0xC3FF7FFC /* SOFTWARE SYSTEM STACK */
}

1.3、建立工程目录

  在程序文件夹下建立SoftWare目录,目录下建立App,BSP,HWLib,OS,User文件夹和下级目录。
  其中,App是应用程序目录,BSP是板级支持包,主要是配置开发板启动初始化等工作。OS里面有几个文件夹,放操作系统源文件,系统配置文件等。还有一个HWLib,这个目录放置AM1808的硬件库,这是TI提供的StarterWare软件包的一部分,包括工程中用到的启动代码,驱动代码或库文件。User暂时没有用,准备用来放用户的应用文件。
  在App目录中建立App.c、App.h文件,从uC-SAM9263拷贝其它文件到该目录
  在BSP目录中建立两个空文件bsp.c、bsp.h
  HWLib目录:
  拷贝StarterWare中的system_config.lib、utils.lib两个库和drivers、platform、include目录,当然,也可以不用StarterWare包中的库文件,可以直接复制其中的源文件到工程目录下面,在这上面修改即可,以保证工程的独立性并且不影响StarterWare包源代码。其中drivers目录就是复制源文件。注意,并不是所有文件都要加入工程中,可以参考相应的StarterWare工程,从中提取有用的文件加入工程。
  上述文件如下图:
  
  从uC-SAM9263 工程中拷贝操作系统源代码到工作目录,结构如下所示:
  

1.4、建立应用程序。

  在App.c 中,建立 main 函数,并建立一个任务,也可以从其它地方复制过来。
  在BSP.C 中,做开发板的初始化。 由于 bootloader 已经对硬件做好了配置, 这里要做的是:
    在OS启动前,关闭所有中断, OSStart() 运行以后,在用户任务中初始化硬件,配置中断,配置一个系统 tick 时钟。
    uCOS-II 操作系统需要一个时钟作为系统tick。配置 Timer2作为一个64bit的定时器,产生系统 tick。注意,Timer2使用的系统时钟是在 soc_AM1808.h文件中定义的。
    然后配置中断,把用户中断服务程序的函数指针注册到系统的中断向量表。 这里调用的函数都是在 system_config.lib 库中定义的。
    在中断服务程序中调用 OSTimeTick() 函数,这是一个系统服务函数。进行系统定时操作。

1.5、系统移植。

  系统移植就是要让 uCOS-II 操作系统运行与目标 CPU上,一般只需改 os_cpu.h ,os_cpu_c.c ,os_cpu_a.asm 三个文件,这3个文件和 CPU相关。在移植之前, 先配置 os_cfg.h文件,其实,在系统正常工作之前,可以注释所有不需要的功能,这里先注释掉#define OS_APP_HOOKS_EN 0
  一个一个来,首先是 OS_CPU_C.C 文件,该文件主要是定义一些 HOOK函数,这些函数在任务调度前或者创建前被调用, 让用户有一个做点什么事的机会。这些函数都有一个宏定义开关来打开或者注销。如果有编译不通过的注销就可以了。还有一个配置任务堆栈的函数:
    OSTaskStkInit() ,该函数在任务运行前初始化堆栈,并设置系统模式,可以看到,操作系统运行于 SVC模式。
  修改 os_cpu_a.asm 文件,该文件中使用到的函数有

  OS_CPU_SR OS_CPU_SR_Save(void);
  void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
  void OSCtxSw(void);
  void OSIntCtxSw(void);
  void OSStartHighRdy(void);
  void OS_CPU_ARM_ExceptIrqHndlr(void);

  该文件由 uC-SAM9263 工程复制过来,由于原先的开发环境是IAR,而现在改为 CCS5,IAR使用的是符合 ARM汇编标准的编译器, 而CCS5使用 GNU编译器,两个编译器的汇编伪代码定义不一样。首先要处理伪代码符合 GNU汇编标准。这一部分参考 GNU标准和系统提供的例程。
  OS_CPU_SR_Save 和 OS_CPU_SR_Restore 函数提供系统进入临界代码的保护, 当os_cpu.h文件中 OS_CRITICAL_METHOD 定义为3时,使用这两个文件保存 CPSR寄存器到一个临时变量。

#if OS_CRITICAL_METHOD == 3 /* See OS_CPU_A.ASM */
	OS_CPU_SR OS_CPU_SR_Save(void);
	void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
#endif

  这两个文件不需要修改
  OSStartHighRdy 函数功能是跳转到最高优先级任务,该函数在启动时只运行一次,首先调用一个 hook 函数,然后修改 OSRunning 变量为 1,标识系统进入运行状态,再切换到最高优先级任务, 最高优先级任务堆栈已经在 OS_CPU_C.C文件中的 OSTaskStkInit () 函数定义,任务堆栈指针是任务控制块 OS_TCB 的第一个变量: OS_STK *OSTCBStkPtr;
  该堆栈组织形式如下 :

  高地址       PC
         LR
         R12
         R11
         .
         .
         R0
  低地址      CPSR

  首先要明白一个概念,一般的 ARM编译器使用的堆栈是满递减堆栈,即堆栈的基指针在高地
址,向低地址生长,栈顶指针指向最后一个压栈地址。通过 STMFD和LDMFD指令压栈和出栈时,
如:LDMFD SP!, {R0-R12, LR, PC}^,寄存器列表可包含 16 个可见寄存器(R0~R15)的任意集合或全部, 列表中寄存器的次序是不重要的, 不影响存取的次序和指令执行后寄存器中的值,有一个约定,总是把编号低的寄存器对应于存储器的低地址。 也就是说, 编号最低的寄存器保存到堆栈的最低地址或从最低地址取出。
  在这个出栈指令中,最后的 ^号,表示出栈时同时要用 SPSR恢复 CPSR。
  OSStartHighRdy 跳转到用户任务,用户任务是一个死循环,永远不会再返回到这里了。这也就是当用户任务有返回时,系统会死机的原因。
  OSCtxSw 函数负责任务切换,首先保存当前任务堆栈,然后把任务堆栈指针赋值给任务控制块第一个变量 OSTCBStkPtr 。 调用一个 HOOK函数,最后取出最高优先级任务控制块的第一个变量,
即任务堆栈指针,把它赋值给 SP,用这个SP恢复最高优先级任务。
  OSIntCtxSw 函数是中断中的任务切换,由 OSIntExit 函数调用,而 _OSIntExit 函数在中断退出到任务时被调用。
  OSIntCtxSw 函数和 OSCtxSw 的基本功能是相同的,都是任务切换,但是前者不保存任务堆栈,因为该堆栈在中断处理时已经被保存过了,它只需要做后面的任务切换工作即可。
  OS_CPU_ARM_ExceptIrqHndlr 函数处理:
    该函数是系统 IRQ 中断的唯一入口函数,当 ARM系统发生 IRQ 中断时, PC指针就会跳转到该函数,在这里处理中断。
  我们知道, AM1808CPU 处理中断是通过内存中的中断向量表进行散转的,系统直接把中断服务程序函数指针送到 HIPVR2 寄存器,执行效率比较高。但是操作系统的中断服务程序需要统一处理,比如任务堆栈的入栈出栈,中断嵌套,中断退出的任务切换等。所以要在system_config 工程中修改 ARM中断向量表,在 startup.c 文件中修改如下:

static unsigned int const vecTbl[14]=
{
	0xE59FF018,
	0xE59FF018,
	0xE59FF018,
	0xE59FF018,
	0xE59FF014,
	0xE24FF008,
	0xE59FF010,
	0xE59FF010,
	( unsigned int )Entry,
	( unsigned int )UndefInstHandler,
	( unsigned int )SWIHandler,
	( unsigned int )AbortHandler,
	//(unsigned int)IRQHandler,
	( unsigned int )OS_CPU_ARM_ExceptIrqHndlr,
	( unsigned int )FIQHandler
};

  然后重新编译,把编译好的 lib 文件复制到工程中 HWLib 目录。
  中断服务程序 OS_CPU_ARM_ExceptIrqHndlr 首先切换到 SVC模式下,保存任务堆栈,然后根据 OSIntNesting 的值,判断是否中断嵌套,如果没有嵌套,表示该中断是打断了一个任务,就执行 OS_CPU_ARM_IRQHndlr_BreakTask 函数,反之执行OS_CPU_ARM_IRQHndlr_BreakIRQ 函数。
  OS_CPU_ARM_IRQHndlr_BreakTask 函数:
    首先保存当前堆栈指针到当前任务控制块的第一个变量,然后把异常堆栈指针给 SP.再通过 HIPVR2 调用中断服务程序,返回后恢复各个模式的堆栈,调用 OSIntExit 函数,检查是否切换任务,退出中断。
  OS_CPU_ARM_IRQHndlr_BreakIRQ 函数:
    处理中断嵌套情况,首先把当前 SP存储到异常堆栈中,然后通过HIPVR2调用中断服务程序。返回后直接对 OSIntNESTING 减一,并不调用 OSIntExit 函数。退出中断。IRQ中断服务程序要修改的是ARM和GNU格式的转换,在中断跳转时读取HIPVR2寄存器的值,然后据此跳转。其它地方不用修改。

1.6、编译,除错。

  其中的错误大多数都是找不到定义, Project->Properties中,Build->ARM Compiler->Include Options 在页面中添加 include路径:

"../SoftWare/HWLib/include/armv5/am1808"
"../SoftWare/HWLib/include/armv5"
"../SoftWare/HWLib/include"
"../SoftWare/HWLib/include/hw"
"../SoftWare/HWLib"
"../SoftWare/OS/Ports/ARM"
"../SoftWare/OS/Source"
"../SoftWare/OS/uC-CPU/ARM"
"../SoftWare/OS/uC-CPU"
"../SoftWare/App"
"../SoftWare/BSP"
"../SoftWare/User"

  在ARM Linker->File SearchPath 页面中,在包含库文件一栏中添加库文件

"utils.lib"
"system_config.lib"

  在搜索路径一栏中,添加路径

"../SoftWare/OS/uC-CPU/ARM"
"../SoftWare/HWLib/include"
"../SoftWare/HWLib"

  注意的是, Debug和 Release选项都必须配置一次。
移植成功!

二、ucosII基础概念

2.1、嵌入式实时操作系统的基本功能

1)内存管理:malloc、free
2)多任务管理
3)外围资源管理:对内存、CPU、输入型设备、输出型设备等资源进行合理的调度和管理

2.2、ucos的代码结构

2.3、ucosII的内核实现

1)任务的结构--任务控制块
  这个任务控制块是非常的大的,这里面使用很多的宏定义,估计是可以让使用者使用的时候按需配置。
2)如何得到最高的优先级的任务---就绪表机制
  这个表的原理非常的简单,就是通过查表的原理,不断的从X轴到Y轴的不断的累加来计算的。
3)如何通过最高优先级的任务进行任务切换--进入中断,切换任务堆栈实现

三、ucosII启动过程

int main(void)
{
    OSInit(); /* 系统初始化*/  
    /* 创建主任务*/
    OSTaskCreate(MainTask, (void *)0, &MainTask_Stk[MainTask_StkSize-1], MainTask_Prio);
    OSStart(); /* 开始任务调度*/
    return 0;
}

3.1、初始化ucosII--OSInit()

1)在调用UCOSII在任何的其他的服务之前,UCOSII要求首先调用初始化函数OSInit();
  这个函数的目的就是在整个系统启动之前,初始化所有的变量和数据结构。
2)其中,在OSInit()函数中建立空闲任务OS_TaskIdle();
  这个任务总是处于就绪态的,空闲任务的优先级是设置为最低的。
3)调用OSInit以后,任务控制块缓冲池中有OS_MAX_TASKS个任务控制块,事件控制缓冲区中有OS_MAX_EVENTS个事件控制块,消息队列缓冲池OS_Q中有OS_MAX_QS个消息队列控制块等等。

3.2、创建任务--OSTaskCreate()

  通过调用OSTaskCreate()创建至少一个任务。
  因为我们到时候,程序的指针SP,会跳出main.c的函数,那么如果不创建一个任务的话,那么程序的指针就会跑飞。

3.3、任务调度--OSStart()

  OSStart()函数就是为了进行任务调度的,因为我们很快就会跳出main.c的函数,不会再跳进来,所以我们需要进行内部的指针跳出。

void  OSStart (void)
{
    if (OSRunning == OS_FALSE) {
        OS_SchedNew();                               /* Find highest priority's task priority number   */
        OSPrioCur     = OSPrioHighRdy;
        OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run    */
        OSTCBCur      = OSTCBHighRdy;
        OSStartHighRdy();                            /* Execute target specific code to start task     */
    }
}

1)这是UCOSII启动的一个关键的函数,此函数记录了我们操作系统是怎么进入多任务的状态的。
2)解析:当OS的状态等于错误的状态的时候,把当前的最高优先级的任务赋值给OS的当前的优先级。
3)最高优先级是通过位图来进行查找的,按上一讲已经完全说清楚获取最高优先级的算法。
4)OSStartHighRdy()此函数是这个开始函数的关键,也就是这个地方进行任务的切换的,就是我们上一节课说明的进入中断,然后把CPU寄存器的值进行切换,从而跳转到最高优先级的任务当中。

四、任务管理

4.1、重讲任务

1)任务可以是一个无限的循环,也可以在一次执行完毕后被删除。
  这里需要注意的是,任务的代码并不是真正的删除了,而是UCOSII不再理会该任务代码,所以该任务代码不会再执行。
(2)建立任务,OSTaskCreate()
  如果想让UCOSII管理用户的任务,必须先建立任务,可以通过将任务的地址(函数名)和其他参数传递到这2个函数中来建立任务。
(3)任务可以在多任务调度之前开始建立,也可以在其他的任务中创建需要的任务。但是有一点需要注意的是,在启动UCOS之前必须至少得建立一个任务。

4.2、分析创建任务函数

1)参数分析:
  参数1:任务的函数名:其实就是为了在任务切换的时候跳转到任务中执行的入口地址。
  参数2:传递给建立任务的参数,这个参数基本不会用到。
  参数3:传递给建立任务的堆栈,每个任务都有独一无二的堆栈。
  参数4:传递给任务的优先级。
2)函数内容分析:
  当OS_TASK_CREATE_EN宏大于0的时候,我们才可以使用创建任务的函数。
  如果创建的时候检测到任务的优先级比最大的优先级(数值上,实际上是最小)还大的话,那么就直接退出,输出一个错误码。
  我们不允许创建任务是在中断中进行的,所以我们也会在中断时创建任务返回一个错误码。
  最后就是把刚刚的四个参数赋值到任务当中去,实现任务的创建。

4.3、再谈任务堆栈

  任务的堆栈可以使用静态的堆栈生成,也可以使用动态的堆栈生成。
1)静态堆栈:

static OS_STK MyTaskStack[stack_size];

2)动态堆栈:

OS_STK   *pstk;
 
pstk = (OS_STK *)malloc(stack_size);
if(pstk != (OS_STK *)0)
{
    //确保malloc能够得到足够的内存空间
}

3)UCOSII支持的堆栈可以是递减的,也可以是递增的。
  在调用函数OS_TaskCreate(),必须知道堆栈是递减的,还是递增的。因为必须把堆栈的栈顶地址传递给上面的两个函数。
  PS:这里面就有OS_CPU.h文件中的OS_STK_GROWTH为0,需要将堆栈的最低地址传递给任务创建的函数。
这个是堆栈从下往上增长的:

OS_STK TaskStack[TASK_STACK_SIZE];
OSTaskCreate(task, pdata, &TaskStack[0], prio);

这个是堆栈从上往下增长的:

OS_STK TaskStack[TASK_STACK_SIZE];
OSTaskCreate(task, pdata, &TaskStack[TASK_STACK_SIZE-1], prio);

4.4、删除任务,OSTaskDel()

1)有时候我们需要删除任务,就是说任务返回到休眠状态,并不是说任务代码被删除了,而是仅仅从就绪队列中删除了而已。
  参数1:prio :也就是该任务的优先级,
  当我们支持多任务相同优先级的时候,必须指明任务堆栈,或者任务名,才能删除。
2)实现这个函数的关键步骤
  把任务从就绪表中移除,也就是不让该任务处于就绪状态中。
  假如任务需要事件控制块,消息队列,邮箱等,那么我们就需要在删除任务之前将他所在的链表中移除。
  把任务的各种资源释放掉。

4.5、挂起任务OS_TaskSuspend()和恢复挂起任务OSTaskResume()

OS_TaskSuspend():
1)这个函数是必须进行说明的一个函数,因为他涉及到任务的状态机。
2)这个实现挂起的函数主要是删除就绪表中的位图的相应优先级的那个位进行置0的操作。
3)然后将任务的相应的状态进行赋值为挂起的状态。
4)最后在最后要进行任务的调度的操作,如果当前是这个任务在进行的话,要切换到别的任务中继续去运行。
OSTaskResume()恢复任务的源码也是差不多的,就是一个是对就绪表上面的内容进行置0的操作,一个是对就绪表的内容进行置1的操作。

五、时间管理

5.1、UCOSII的定时中断

1)绝大多数的内核要求提供定时中断,以实现延时与超时控制等功能。这个定时中断叫做时钟节拍。
2)时钟的中断子程序ISR和时钟节拍函数OSTimeTick()该函数用于通知UCOSII,发生了时钟的节拍中断。

5.2、UCOSII的5个关键的时钟函数

1)OSTimeDly();
后面的4个函数可以根据需要进行配置使用
2)OSTimeHMSM();
3)OSTimeDlyResume();
4)OSTimeGet();
5)OSTimeSet();

5.3、关键的时钟延时函数的实现OSTimeDly()

void  OSTimeDly (INT32U ticks)
{
    INT8U      y;
#if OS_CRITICAL_METHOD == 3u                     /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr = 0u;
#endif

    if (OSIntNesting > 0u) {                     /* See if trying to call from an ISR                  */
        return;
    }
    if (OSLockNesting > 0u) {                    /* See if called with scheduler locked                */
        return;
    }
    if (ticks > 0u) {                            /* 0 means no delay!                                  */
        OS_ENTER_CRITICAL();
        y            =  OSTCBCur->OSTCBY;        /* Delay current task                                 */
        OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
        if (OSRdyTbl[y] == 0u) {
            OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;
        }
        OSTCBCur->OSTCBDly = ticks;              /* Load ticks in TCB                                  */
        OS_EXIT_CRITICAL();
        OS_Sched();                              /* Find next task to run!                             */
    }
}

1)参数分析:
  ticks:代表的是多少个时钟周期节拍
2)实现过程:
  把相应任务的就绪表中的位图的位置清零。
  把需要执行的等待节拍进行处理。
  最后进行任务调度,寻找下一个这个时刻最高优先级的任务。
3)发出疑问?为什么我在OS_TCB的结构体当中的Ticks填写相应的数字的时候可以实现把任务从就绪态变为等待的状态?
  答案:OSTimeTick()这个函数

void  OSTimeTick (void)
{
    OS_TCB    *ptcb;
 
    if (OSRunning == OS_TRUE) {
#if OS_TICK_STEP_EN > 0u
        switch (OSTickStepState) {                         /* Determine whether we need to process a tick  */
            case OS_TICK_STEP_DIS:                         /* Yes, stepping is disabled                    */
                 step = OS_TRUE;
                 break;
 
            case OS_TICK_STEP_WAIT:                        /* No,  waiting for uC/OS-View to set ...       */
                 step = OS_FALSE;                          /*      .. OSTickStepState to OS_TICK_STEP_ONCE */
                 break;
 
            case OS_TICK_STEP_ONCE:                        /* Yes, process tick once and wait for next ... */
                 step            = OS_TRUE;                /*      ... step command from uC/OS-View        */
                 OSTickStepState = OS_TICK_STEP_WAIT;
                 break;
 
            default:                                       /* Invalid case, correct situation              */
                 step            = OS_TRUE;
                 OSTickStepState = OS_TICK_STEP_DIS;
                 break;
        }
        if (step == OS_FALSE) {                            /* Return if waiting for step command           */
            return;
        }
#endif
 
//此处为此任务运行的核心的代码:
 
        ptcb = OSTCBList;                                  /* Point at first TCB in TCB list               */
        while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) {     /* Go through all TCBs in TCB list              */
            OS_ENTER_CRITICAL();
            if (ptcb->OSTCBDly != 0u) {                    /* No, Delayed or waiting for event with TO     */
                ptcb->OSTCBDly--;                          /* Decrement nbr of ticks to end of delay       */
                if (ptcb->OSTCBDly == 0u) {                /* Check for timeout                            */
 
                    if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
                        ptcb->OSTCBStat  &= (INT8U)~(INT8U)OS_STAT_PEND_ANY;          /* Yes, Clear status flag   */
                        ptcb->OSTCBStatPend = OS_STAT_PEND_TO;                 /* Indicate PEND timeout    */
                    } else {
                        ptcb->OSTCBStatPend = OS_STAT_PEND_OK;
                    }
 
                    if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {  /* Is task suspended?       */
                        OSRdyGrp               |= ptcb->OSTCBBitY;             /* No,  Make ready          */
                        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                    }
                }
            }
            ptcb = ptcb->OSTCBNext;                        /* Point at next TCB in TCB list                */
            OS_EXIT_CRITICAL();
        }
    }

实现任务等待时间的关键的函数:
1)当任务是处于运行的状态的时候。判断OS现在处于的是什么样的状态。
2)所有的任务都是通过任务的链表链接到一起的,我们后面的一个while循环就是,从开始不断的遍历到最后的任务,也就是空闲任务。
3)对延时的时间不断的减1,当延时的dlyTick减少到0的时候,对OS_TCB任务控制块的一些状态进行修改,并且重新的把任务添加回就绪表中。
  PS:这个函数应该是放置到中断服务函数中的,整个时间片的不断的触发执行的。

5.4、恢复延时的任务:OSTimeDlyResume()

  里面关键的操作就是把那个全局的时间计数器变量设置为0

INT8U  OSTimeDlyResume (INT8U prio)
{
    OS_TCB    *ptcb;
 
    if (prio >= OS_LOWEST_PRIO) {
        return (OS_ERR_PRIO_INVALID);
    }
    OS_ENTER_CRITICAL();
    ptcb = OSTCBPrioTbl[prio];                                 /* Make sure that task exist            */
    if (ptcb == (OS_TCB *)0) {
        OS_EXIT_CRITICAL();
        return (OS_ERR_TASK_NOT_EXIST);                        /* The task does not exist              */
    }
    if (ptcb == OS_TCB_RESERVED) {
        OS_EXIT_CRITICAL();
        return (OS_ERR_TASK_NOT_EXIST);                        /* The task does not exist              */
    }
    if (ptcb->OSTCBDly == 0u) {                                /* See if task is delayed               */
        OS_EXIT_CRITICAL();
        return (OS_ERR_TIME_NOT_DLY);                          /* Indicate that task was not delayed   */
    }
 
    ptcb->OSTCBDly = 0u;                                       /* 清除任务计数器       */
    if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
        ptcb->OSTCBStat     &= ~OS_STAT_PEND_ANY;              /* Yes, Clear status flag               */
        ptcb->OSTCBStatPend  =  OS_STAT_PEND_TO;               /* Indicate PEND timeout                */
    } else {
        ptcb->OSTCBStatPend  =  OS_STAT_PEND_OK;
    }
    if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {  /* Is task suspended?                   */
        OSRdyGrp               |= ptcb->OSTCBBitY;             /* No,  Make ready                      */
        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
        OS_EXIT_CRITICAL();
        OS_Sched();                                            /* See if this is new highest priority  */
    } else {
        OS_EXIT_CRITICAL();                                    /* Task may be suspended                */
    }
    return (OS_ERR_NONE);
}

未完待续

参考链接:
uCosII在AM1808上的移植 https://wenku.baidu.com/view/8fcee91b59eef8c75fbfb3b0.html
uCOS-II到ARM的移植 https://www.docin.com/p-816415450.html?docfrom=rrela
uCOS-II移植到STM32 http://www.elecfans.com/emb/586880_a.html

posted @ 2019-10-08 16:43  princepeng  阅读(534)  评论(0编辑  收藏  举报