之前在ucos多任务切换中漏掉了一个变量,

OSCtxSwCtr标识系统任务切换次数

主要应该还是用在调试功能中

Ucos系统初始化函数为OSInit(),主要完成以下功能

全局变量初始化

就绪任务表初始化

空任务控制块初始化

事件控制块链表初始化

信号量集初始化

存储器管理初始化

Qs队列控制初始化

系统空闲任务初始化

系统统计任务初始化

部分功能需要依靠宏定义打开另外要注意一个变量OSTaskCtr标识系统全部任务数,在初始化完成之后就可以创建任务了,创建任务完成之后启动系统使用OSStart函数,代码如下

void  OSStart (void)

{

if (OSRunning == OS_FALSE) {

OS_SchedNew();

OSPrioCur     = OSPrioHighRdy;

OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];

OSTCBCur      = OSTCBHighRdy;

OSStartHighRdy();

}

}

开始阶段没有直接调用OS_SchedNew,因为此时系统是没有任务在执行的,也就没有中断锁啦,所以这一点需要注意,在系统启动之前,最好只留下系统中断,启动之后再打开中断,否则可能会造成问题

然后依旧是获取优先级,获取tcb,最后根据当前最高优先级任务调用OSStartHighRdy函数,该函数是一个需要汇编语言移植的函数,处于os_cpu_a.asm中,代码如下

OSStartHighRdy

LDR     R4, =NVIC_SYSPRI2      ; set the PendSV exception priority

LDR     R5, =NVIC_PENDSV_PRI

STR     R5, [R4]

MOV     R4, #0                 ; set the PSP to 0 for initial context switch call

MSR     PSP, R4

LDR     R4, =OSRunning         ; OSRunning = TRUE

MOV     R5, #1

STRB    R5, [R4]

;切换到最高优先级的任务

LDR     R4, =NVIC_INT_CTRL     ;rigger the PendSV exception (causes context switch)

LDR     R5, =NVIC_PENDSVSET

STR     R5, [R4]

CPSIE   I                      ;enable interrupts at processor level

OSStartHang

B       OSStartHang            ;should never get here死循环

先将 堆栈清零,然后将OSRunning设置为true,最后触发中断,因为之前的OS_SchedNew已经得到了最高优先级的任务,所以触发中断之后,在中断中就能直接切换到我们想要切换的最高优先级任务中,实现系统启动

另外需要注意一点,如果使用了统计任务,那么系统开始的时候必须要启动统计任务,否则打开这个宏却没有初始化统计任务会在成一定的异常

关于系统临界段

在系统运行过程中,有时候某段代码是不能被打断的,而中断的时机经常会比较随机,为了解决这个问题,提出了临界段的概念,ucos一般采用三种方式来处理临界段

  1. 开关中断的方式
  2. 通过保存程序状态字并关中断的方式
  3. 保存程序状态字到cpu_sr变量中

第一种方法简单粗暴,第二种方法复杂一点,但是可以让处理器中断允许标志在中断前和中断之后发生变化,但是状态时压到堆栈中的第三种方式使用局部变量来保存中断状态字,不用使用堆栈,更加灵活

移植的时候可以任选一种方式,一般选择第三种方式,示例如下

OS_CPU_SR_Save

MRS     R0, PRIMASK    ;读取PRIMASK到R0,R0为返回值

CPSID   I             ;PRIMASK=1,关中断(NMI和硬件FAULT可以响应)

BX      LR              ;返回

OS_CPU_SR_Restore

MSR     PRIMASK, R0    ;读取R0到PRIMASK中,R0为参数

BX      LR             ;返回

#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

Ucos时钟

Ucos为了处理等待,延时等与时间有关的时间,引入了一个周期性的信号,也就是ucos时钟,该时钟依托于硬件环境,需要我们根据硬件处理器的定时来去确定,与之相关的宏有一个,如下

OS_TICKS_PER_SEC,该宏定义了系统1s类中断的次数,例如我们将该宏定义为1000,那么我们就要保证系统每1ms中断一次,并且在中断处理程序中调用如下代码

OSIntEnter();     //进入中断

OSTimeTick();       //调用ucos的时钟服务程序

OSIntExit();        //触发任务切换软中断

OSIntEnter和OSIntExit之前已经说过了,来看看OSTimeTick的构成,如下

while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) {

OS_ENTER_CRITICAL();

if (ptcb->OSTCBDly != 0u) {

ptcb->OSTCBDly--;

if (ptcb->OSTCBDly == 0u) {

if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {

ptcb->OSTCBStat  &= (INT8U)~(INT8U)OS_STAT_PEND_ANY;

ptcb->OSTCBStatPend = OS_STAT_PEND_TO;

} else {

ptcb->OSTCBStatPend = OS_STAT_PEND_OK;

}

if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {

OSRdyGrp               |= ptcb->OSTCBBitY;

OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

}

}

}

ptcb = ptcb->OSTCBNext;

OS_EXIT_CRITICAL();

}

可以看到,在这个函数中,系统遍历了整个tcb控制结构,对每一个元素中的OSTCBDly元素进行递减,当某个任务控制块的延时时间OSTCBDly为0的时候,检测任务的状态是否为挂起状态,如果不是挂起状态,就修改系统就绪表将当前任务设置为reday,等待系统进行任务调度,这个任务调度就是在上一个三句代码中的最后一句OSIntExit()完成的,这也是为什么设计一个中断中切换任务功能的原因,这样可以保证机时某个任务不释放处理器,在出现更高优先级的任务的时候也能立即切换到该任务中.

为了不让系统中优先级最高的任务独占处理器,ucos设计了一个延时函数,用于高优先级任务主动释放掉cpu所有权,在实际的嵌入式系统中,这种释放也是很常见的,比如等待器件反应,人眼视觉残留等都需要,与之相关的重要函数是

OSTimeDly 参数是延时节拍数

OSTimeDlyHMSM 长时间延时,参数分别为延时小时 分 秒 毫秒

OSTimeDlyResume 取消特定优先级的任务的延时

OSTimeGet 获取当前系统节拍

OSTimeSet 设置当前系统节拍

基本上我们只要关注OSTimeDly和OSTimeDlyResume便好,先看OSTimeDly

if (OSIntNesting > 0u) {

return;

}

if (OSLockNesting > 0u) {

return;

}

if (ticks > 0u) {

OS_ENTER_CRITICAL();

y            =  OSTCBCur->OSTCBY;

OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;

if (OSRdyTbl[y] == 0u) {

OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;

}

OSTCBCur->OSTCBDly = ticks;

OS_EXIT_CRITICAL();

OS_Sched();

当设置的延时节拍大于0的时候,首先取消当前任务的在系统就绪表中的就绪标志,然后将系统控制块的OSTCBDly参数设置为设置的节拍数,最后调用系统任务调度函数,完成系统任务调度,并且OSTCBDly设置之后和之前的中断处理OSTimeTick函数就关联起来了,从而实现系统延时

取消任务的延时函数为OSTimeDlyResume,有用的代码为

ptcb = OSTCBPrioTbl[prio];

*******

ptcb->OSTCBDly = 0u;

if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {

ptcb->OSTCBStat     &= ~OS_STAT_PEND_ANY;

ptcb->OSTCBStatPend  =  OS_STAT_PEND_TO;

} else {

ptcb->OSTCBStatPend  =  OS_STAT_PEND_OK;

}

if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {

OSRdyGrp               |= ptcb->OSTCBBitY;

OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

OS_EXIT_CRITICAL();

OS_Sched();

} else {

OS_EXIT_CRITICAL();

}

首先获取想要取消延时的任务tcb(根据优先级获取),然后查看任务是否被挂起,如果没被挂起而且任务就绪,就设置任务就绪表相关位置为raday,并调用OS_Sched进行任务切换,但是并不是说取消了一定会运行,还是要看任务优先级的.

到这里我们可以说任务的调度时机在于系统定时中断的时候和系统调用延时函数的时候.(后面还有别的延时时机).

ucos系统初始化及启动过程的更多相关文章

  1. Android 面试必备 - 系统、App、Activity 启动过程“一锅端”

    Android 系统启动过程 从系统层看: linux 系统层 Android系统服务层 Zygote 从开机启动到Home Launcher: 启动bootloader (小程序:初始化硬件) 加载 ...

  2. 走进Linux之systemd启动过程

    Linux系统的启动方式有点复杂,而且总是有需要优化的地方.传统的Linux系统启动过程主要由著名的init进程(也被称为SysV init启动系统)处理,而基于init的启动系统被认为有效率不足的问 ...

  3. Android 启动过程总结

    SystemServer的启动 frameworks/base/services/java/com/android/server/SystemServer.java: run() 其中调用Activi ...

  4. spark 源码分析之四 -- TaskScheduler的创建和启动过程

    在 spark 源码分析之二 -- SparkContext 的初始化过程 中,第 14 步 和 16 步分别描述了 TaskScheduler的 初始化 和 启动过程. 话分两头,先说 TaskSc ...

  5. Hadoop源码:namenode格式化和启动过程实现

    body { margin: 0 auto; font: 13px / 1 Helvetica, Arial, sans-serif; color: rgba(68, 68, 68, 1); padd ...

  6. 关于esp32的系统初始化启动过程及设计学习方法

    对于esp32,其开发程序中有且只能有一个app_main函数,该函数是用户程序的入口,这在没有调用FreeRTOS的系统中相当于函数main,但其实在app_main之前,系统还有一段初始化的过程, ...

  7. 详解linux系统的启动过程及系统初始化

    一.linux系统的启动流程 关于linux系统的启动流程我们可以按步进行划分为如下: POST加电自检 -->BIOS(Boot Sequence)-->加载对应引导上的MBR(boot ...

  8. 探索 Linux 系统的启动过程

    引言 之所以想到写这些东西,那是因为我确实想让大家也和我一样,把 Linux 桌面系统打造成真真正正日常使用的工具,而不是安装之后试用几把再删掉.我是真的在日常生活和工作中都使用 Linux,比如在 ...

  9. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个 Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home ...

随机推荐

  1. Android中自定义veiw使用Java中的回调方法

    //------------------MainActivity----中---------------------------------- import android.os.Bundle;imp ...

  2. 基础-Servlet

    Servlet是运行在web服务器上的一个java类. 它的作用是将http请求和http相应进行操作完成我们的业务逻辑. servlet创建: 1.创建一个类extends HttpServlet ...

  3. android笔记20170116

    封装http请求类,利用回调机制获取返回值 public interface HttpCallbackListener { void onFinish(String response); void o ...

  4. 转:Jmeter常见问题 (转载) http://www.51testing.com/?uid-128005-action-viewspace-itemid-84094

    说明:这些问答是从网上转载的,自己修改了其中的一些内容,如果大家兴趣,可以将大家在使用Jmeter的时候碰到的问题写下来,我们一起补充到这个问答里面,共同努力完善jmeter的资料. 1.  JMet ...

  5. 转:Selenium借助AutoIt识别上传(下载)详解

    AutoIt目前最新是v3版本,这是一个使用类似BASIC脚本语言的免费软件,它设计用于Windows GUI(图形用户界面)中进行自动化操作.它利用模拟键盘按键,鼠标移动和窗口/控件的组合来实现自动 ...

  6. Ubuntu下使用rpm 软件包

    Ubuntu的软件包格式是deb,如果要安装rpm的包,则要先用alien把rpm转换成deb. sudo apt-get install alien sudo alien xxxx.rpm #将rp ...

  7. 优化之zencart第一时间修改原始内容

    Zen Cart 基本修改指南 Zen Cart,全球顶级B2C商城网站!要想自行搭建一个基本的Zen Cart的网站,这篇文章是绝对不能错过的.目前我已经做了两个B2C网站,但是还是离不开这篇文章的 ...

  8. 我也谈javascript正则匹配

    一.javascript 正则全局匹配 g 慎用test()方法 来个例子: var a = /^[a-z]+/gi; a.test('bb123'); //true a.lastIndex ; // ...

  9. One Bomb

    One Bomb time limit per test 1 second memory limit per test 256 megabytes input standard input outpu ...

  10. Swaps in Permutation

    Swaps in Permutation You are given a permutation of the numbers 1, 2, ..., n and m pairs of position ...